[
  {
    "path": ".babelrc.js",
    "content": "const env = process.env.NODE_ENV;\n\nif (env === 'commonjs' || env === 'es') {\n  module.exports = {\n    ignore: [\n      '*.jest.js',\n      '*.e2e.js',\n      '*.ssr.js',\n      '*.example.js',\n      'source/demo',\n      'source/jest-*.js',\n      'source/TestUtils.js',\n    ],\n    plugins: [\n      '@babel/plugin-transform-runtime',\n      '@babel/plugin-proposal-class-properties',\n      '@babel/plugin-transform-flow-comments',\n      ['flow-react-proptypes', {deadCode: true, useESModules: true}],\n      ['transform-react-remove-prop-types', {mode: 'wrap'}],\n    ],\n    presets: [\n      ['@babel/preset-env', {modules: false}],\n      '@babel/preset-react',\n      '@babel/preset-flow',\n    ],\n  };\n\n  if (env === 'commonjs') {\n    module.exports.plugins.push('@babel/plugin-transform-modules-commonjs');\n  }\n}\n\nif (env === 'rollup') {\n  module.exports = {\n    comments: false,\n    plugins: [\n      '@babel/plugin-external-helpers',\n      '@babel/plugin-proposal-class-properties',\n    ],\n    presets: [\n      ['@babel/preset-env', {modules: false}],\n      '@babel/preset-react',\n      '@babel/preset-flow',\n    ],\n  };\n}\n\nif (env === 'development') {\n  module.exports = {\n    plugins: ['@babel/plugin-proposal-class-properties'],\n    presets: ['@babel/preset-react', '@babel/preset-flow'],\n  };\n}\n\nif (env === 'production') {\n  module.exports = {\n    comments: false,\n    plugins: [\n      '@babel/plugin-transform-runtime',\n      '@babel/plugin-proposal-class-properties',\n    ],\n    presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-flow'],\n  };\n}\n\nif (env === 'test') {\n  module.exports = {\n    comments: false,\n\n    plugins: [\n      '@babel/plugin-transform-modules-commonjs',\n      '@babel/plugin-proposal-class-properties',\n    ],\n    presets: ['@babel/preset-react', '@babel/preset-flow'],\n  };\n}\n"
  },
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\n\ndefaults: &defaults\n  working_directory: ~/repo\n  docker:\n    - image: circleci/node:10.16.3-stretch-browsers\n\njobs:\n  dependencies:\n    <<: *defaults\n    steps:\n      - checkout\n      - restore_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: Install NPM Dependencies\n          command: yarn --frozen-lockfile\n      - save_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n          paths:\n            - ~/repo/node_modules\n\n  lint:\n    <<: *defaults\n    steps:\n      - checkout\n      - restore_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: ESLint Code Analysis\n          command: yarn lint\n\n  typecheck:\n    <<: *defaults\n    steps:\n      - checkout\n      - restore_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: Typechecking with flow\n          command: yarn typecheck\n\n  test:\n    <<: *defaults\n    steps:\n      - checkout\n      - attach_workspace:\n          at: ~/repo\n      - restore_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: Run tests\n          command: yarn test\n\n  coverage:\n    <<: *defaults\n    steps:\n      - checkout\n      - attach_workspace:\n          at: ~/repo\n      - restore_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: Run tests and upload coverage results\n          command: yarn test:coverage\n\n  prettier:\n    <<: *defaults\n    steps:\n      - checkout\n      - restore_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: Check prettier diff\n          command: yarn prettier:diff\n\n  build:\n    <<: *defaults\n    steps:\n      - checkout\n      - restore_cache:\n          key: npm-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: Build all distributions formats\n          command: yarn build\n      - persist_to_workspace:\n          root: .\n          paths:\n            - dist\n\nworkflows:\n  version: 2\n  build_and_test:\n    jobs:\n      - dependencies\n      - lint:\n          requires:\n            - dependencies\n      - typecheck:\n          requires:\n            - dependencies\n      - prettier:\n          requires:\n            - dependencies\n      - build:\n          requires:\n            - dependencies\n      - test:\n          requires:\n            - build\n          filters:\n            branches:\n              ignore: gh-pages\n      - coverage:\n          requires:\n            - build\n          filters:\n            branches:\n              ignore: gh-pages\n"
  },
  {
    "path": ".eslintignore",
    "content": "node_modules/*\n\n# Website dist\nbuild/*\n\n# NPM dist\ndist/*\n\n# Vendor files\nsource/vendor/*"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"parser\": \"babel-eslint\",\n  \"plugins\": [\"react\"],\n  \"extends\": [\"fbjs\", \"eslint:recommended\", \"plugin:prettier/recommended\"],\n  \"rules\": {\n    \"no-console\": 0\n  }\n}\n"
  },
  {
    "path": ".flowconfig",
    "content": "[ignore]\n.*/Collection/.*\n.*/ColumnSizer/.*\n.*/InfiniteLoader/.*\n.*/Masonry/.*\n.*/MultiGrid/.*\n.*/ScrollSync/.*\n.*/Table/.*\n\n[untyped]\n.*/node_modules/babel-plugin-transform-react-remove-prop-types/.*\n.*/node_modules/graphql/.*\n.*/node_modules/immutable/.*\n\n[include]\n\n[libs]\n\n[options]\nmunge_underscores=true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug Report\nabout: Bugs, missing documentation, or unexpected behavior 🤔.\n---\n\n## Bug Report\n\nPlease include either a failing unit test or a simple repro. You can start by forking this Code Sandbox: https://codesandbox.io/s/03qpzq1p9p?module=%2FExample.js\n\n### What is the current behavior?\n\nIf the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React and react-virtualized. Paste the link to your [Code Sandbox](https://codesandbox.io/s/03qpzq1p9p?module=%2FExample.js) below:\n\n### What is the expected behavior?\n\n### Which versions of React and react-virtualized, and which browser / OS are affected by this issue? Did this work in previous versions of react-virtualized?\n|                   |          |\n|-------------------|----------|\n| Browser           |          |\n| OS                |          |\n| React             |          |\n| React DOM         |          |\n| react-virtualized |          |\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: Feature request\nabout: Ideas and suggestions\n---\n\n## Feature Request\n\nProvide as much information as possible about your requested feature. Here are a few questions you may consider answering:\n\n* What's your use case? (Tell me about your application and what problem you're trying to solve.)\n* What interface do you have in mind? (What new properties or methods do you think might be helpful?)\n* Can you point to similar functionality with any existing libraries or components? (Working demos can be helpful.)\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: Questions\nabout: Questions about react-virtualized\n---\n\n## Asking a Question?\n\nPlease don't file GitHub issues to ask questions! Instead use:\n* Stack Overflow: http://stackoverflow.com/questions/tagged/react-virtualized\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "Thanks for contributing to react-virtualized!\n\n**Before submitting a pull request,** please complete the following checklist:\n\n- [ ] The existing test suites (`npm test`) all pass\n- [ ] For any new features or bug fixes, both positive and negative test cases have been added\n- [ ] For any new features, documentation has been added\n- [ ] For any documentation changes, the text has been proofread and is clear to both experienced users and beginners.\n- [ ] Format your code with [prettier](https://github.com/prettier/prettier) (`yarn run prettier`).\n- [ ] Run the [Flow](https://flowtype.org/) typechecks (`yarn run typecheck`).\n\nHere is a short checklist of additional things to keep in mind before submitting:\n\n- Please make sure your pull request description makes it very clear what you're trying to accomplish. If it's a bug fix, please also provide a failing test case (if possible). In either case, please add additional unit test coverage for your changes. :)\n- Be sure you have notifications setup so that you'll see my code review responses. (I may ask you to make some adjustments before merging.)\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: Close stale issues and PRs\non:\n  schedule:\n    - cron: '0 8 * * *'\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@v9.0.0\n        with:\n          days-before-stale: 28\n          days-before-close: 0\n          exempt-issue-labels: do-not-close\n          exempt-pr-labels: do-not-close\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nbuild\ncoverage\ndist\nnode_modules\nnpm-debug.log\nstyles.css\n.vscode\n.idea\n"
  },
  {
    "path": ".nvmrc",
    "content": "v10.16.3\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"bracketSpacing\": false,\n  \"jsxBracketSameLine\": true,\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\"\n}"
  },
  {
    "path": ".watchmanconfig",
    "content": ""
  },
  {
    "path": "CHANGELOG.md",
    "content": "## Changelog\n\n##### NEXT\n\n- Update peer dependencies to allow React 17 ([levenleven](https://github.com/levenleven) - [#1625](https://github.com/bvaughn/react-virtualized/pull/1625))\n- Use DOM API instead of creating Trusted Types policy to append a markup ([shhnjk](https://github.com/shhnjk) - [#1627](https://github.com/bvaughn/react-virtualized/pull/1627))\n- Fix bug in WindowScroller::updatePosition ([yamadapc](https://github.com/yamadapc) - [#1642](https://github.com/bvaughn/react-virtualized/pull/1642), [#1648](https://github.com/bvaughn/react-virtualized/pull/1648))\n- Fix babel tranform es error ([fupengl](https://github.com/fupengl) - [#1651](https://github.com/bvaughn/react-virtualized/pull/1651))\n- Fix issue with unused import being emitted ([mewhhaha](https://github.com/mewhhaha) - [#1635](https://github.com/bvaughn/react-virtualized/pull/1635))\n- Fix grid roles for accessbility ([asnewman](https://github.com/asnewman) - [#1624](https://github.com/bvaughn/react-virtualized/pull/1624))\n\n##### 9.22.5\n\n- React 19 support added; special thanks to ([artur-ptaszek-mck](https://github.com/artur-ptaszek-mck) and ([adubrouski](https://github.com/adubrouski)\n\n##### 9.22.4\n\n- README changes\n\n##### 9.22.3\n\n- Add Trusted Types support ([shhnjk](https://github.com/shhnjk) - [#1614](https://github.com/bvaughn/react-virtualized/pull/1614))\n\n##### 9.22.2\n\n- CollectionView scrollTop/scrollLeft ([dawnmist](https://github.com/dawnmist) - [#1260](https://github.com/bvaughn/react-virtualized/pull/1260))\n\n##### 9.22.1\n\n- Upgrade babel-plugin-flow-react-proptypes ([Hypnosphi](https://github.com/Hypnosphi) - [#1578](https://github.com/bvaughn/react-virtualized/pull/1578))\n\n##### 9.22.0\n\n- Make AutoSizer a Component ([vzaidman](https://github.com/vzaidman) - [#1490](https://github.com/bvaughn/react-virtualized/pull/1490))\n- Update masonry props and docs ([seanstrom](https://github.com/seanstrom) - [#1493](https://github.com/bvaughn/react-virtualized/pull/1493))\n- CellMeasurer: add registerChild render prop ([Hypnosphi](https://github.com/Hypnosphi) - [#1477](https://github.com/bvaughn/react-virtualized/pull/1477))\n- Fix Table class names in documentation ([jakemmarsh](https://github.com/jakemmarsh) - [#1471](https://github.com/bvaughn/react-virtualized/pull/1471))\n- Fix style.css import in demo ([Dominic0512](https://github.com/Dominic0512) - [#1466](https://github.com/bvaughn/react-virtualized/pull/1466))\n\n##### 9.21.2\n\n- 🎉 Update prettier ([wuweiweiwu](https://github.com/wuweiweiwu) - [#1455](https://github.com/bvaughn/react-virtualized/pull/1455))\n- 🎉 Remove slack from documentation ([wuweiweiwu](https://github.com/wuweiweiwu) - [#1417](https://github.com/bvaughn/react-virtualized/pull/1417))\n- 🐛 Fix masionry scroll handler target ([chinmay17](https://github.com/chinmay17) - [#1420](https://github.com/bvaughn/react-virtualized/pull/1420))\n- 🎉 AutoSizer support for multi window ([mbman](https://github.com/mbman) - [#1421](https://github.com/bvaughn/react-virtualized/pull/1421))\n- 🎉 Upgrade dom-helpers ([dominykas](https://github.com/dominykas) - [#1424](https://github.com/bvaughn/react-virtualized/pull/1424))\n- 🎉 Remove react-vtree from docs ([Lodin](https://github.com/Lodin) - [#1415](https://github.com/bvaughn/react-virtualized/pull/1415))\n- 🐛 Fix detection of chrome ([nifgraup](https://github.com/nifgraup) - [#1355](https://github.com/bvaughn/react-virtualized/pull/1355))\n- 🐛 Fix hostname issues for images ([nickstew](https://github.com/nickstew) - [#1401](https://github.com/bvaughn/react-virtualized/pull/1401))\n- 🐛 Fix aria attributes on Table ([eps1lon](https://github.com/eps1lon) - [#1380](https://github.com/bvaughn/react-virtualized/pull/1380))\n\n##### 9.21.1\n\n- 🐛 Fix doc typos. ([tienpham94](https://github.com/tienpham94) - [#1268](https://github.com/bvaughn/react-virtualized/pull/1268))\n- 🐛 Fix typos in changelog. ([misacorn](https://github.com/misacorn) - [#1267](https://github.com/bvaughn/react-virtualized/pull/1267))\n- 🐛 Fix formatting in AutoSizer.md ([jacklee814](https://github.com/jacklee814) - [#1246](https://github.com/bvaughn/react-virtualized/pull/1246))\n- 🐛 Fix usage of reduce in InfiniteLoader ([jedwards1211](https://github.com/jedwards1211) - [#1277](https://github.com/bvaughn/react-virtualized/pull/1277))\n- 🎉 Migrate from classnames to clsx ([TrySound](https://github.com/TrySound) - [#1306](https://github.com/bvaughn/react-virtualized/pull/1306))\n- 🐛 Ensure a string title for header ([oakfang](https://github.com/oakfang) - [#1321](https://github.com/bvaughn/react-virtualized/pull/1321))\n- 🎉 Use sparse array for cell position caches ([trxcllnt](https://github.com/trxcllnt) - [#1312](https://github.com/bvaughn/react-virtualized/pull/1312))\n- 🐛 Fix scrollToPosition ([jaycrypto](https://github.com/jaycrypto) - [#1288](https://github.com/bvaughn/react-virtualized/pull/1288))\n\n##### 9.21.0\n\n- 🎉 Added new use case example for CellMeasurer. ([wuweiweiwu](https://github.com/wuweiweiwu) - [#1168](https://github.com/bvaughn/react-virtualized/pull/1168))\n- 🎉 Added react-timeline-9000 as a related library. ([vasdee](https://github.com/vasdee) - [#1197](https://github.com/bvaughn/react-virtualized/pull/1197))\n- 🐛 Fix CellMeasurer docs with correct import statement. ([skipjack](https://github.com/skipjack) - [#1187](https://github.com/bvaughn/react-virtualized/pull/1187))\n- 🐛 Fix broken Slack badge. ([slieshke](https://github.com/slieshke) - [#1205](https://github.com/bvaughn/react-virtualized/pull/1205))\n- 🐛 Fix type in CellMeasurer example. ([rloqvist](https://github.com/rloqvist) - [#1190](https://github.com/bvaughn/react-virtualized/pull/1190))\n- 🐛 Fix Table aria attributes. ([jsomsanith](https://github.com/jsomsanith) - [#1208](https://github.com/bvaughn/react-virtualized/pull/1208))\n- 🐛 Removed unused variable in Masonry example. ([ignocide](https://github.com/ignocide) - [#1218](https://github.com/bvaughn/react-virtualized/pull/1218))\n- 🎉 Add onColumnClick to Table. ([grahamlyus](https://github.com/grahamlyus) - [#1207](https://github.com/bvaughn/react-virtualized/pull/1207))\n- 🎉 Allow users to override default table row styles. ([emroussel](https://github.com/emroussel) - [#1175](https://github.com/bvaughn/react-virtualized/pull/1175))\n\n##### 9.20.1\n\n- 🐛 Removing `sideEffects: false` from package.json. ([wuweiweiwu](https://github.com/wuweiweiwu) - [#1163](https://github.com/bvaughn/react-virtualized/pull/1163))\n- 🐛 Prevent early `debounceScrollEndedCallback`. ([Gvozd](https://github.com/Gvozd) - [#1141](https://github.com/bvaughn/react-virtualized/pull/1141))\n- 🐛 Fix `scrollToIndex` behavior in `InfiniteLoader`. ([mengdage](https://github.com/mengdage), [dcolens](https://github.com/dcolens) - [#1154](https://github.com/bvaughn/react-virtualized/pull/1154))\n\n##### 9.20.0\n\n- 🎉 Code of Conduct and updated Issue/PR templates. ([aem](https://github.com/aem) - [#1052](https://github.com/bvaughn/react-virtualized/pull/1052))\n- 🐛 Make scrollTo{Column,Row} take precedence over scroll{Left,Top}. ([wuweiweiwu](https://github.com/wuweiweiwu) - [#1130](https://github.com/bvaughn/react-virtualized/pull/1130))\n- 🐛 No `setState(null)`. ([wuweiweiwu](https://github.com/wuweiweiwu) - [#1129](https://github.com/bvaughn/react-virtualized/pull/1129))\n- 🎉 New `isScrollingOptOut` prop for `Grid` that prevents re-rendering on scroll-end. ([wuweiweiwu](https://github.com/wuweiweiwu) - [#1131](https://github.com/bvaughn/react-virtualized/pull/1131))\n- 🐛 Updated npm badge link. ([SpainTrain](https://github.com/SpainTrain) - [#1146](https://github.com/bvaughn/react-virtualized/pull/1146))\n\n##### 9.19.1\n\n- Updated [react-lifecycles-compat](https://github.com/reactjs/react-lifecycles-compat) to 3.0.4. ([pigcan](https://github.com/pigcan) - [#1114](https://github.com/bvaughn/react-virtualized/pull/1114))\n\n##### 9.19.0\n\n- Replaced `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` with async-safe lifecycles in advance of React 16.x deprecation warnings. Added [react-lifecycles-compat](https://github.com/reactjs/react-lifecycles-compat) as a dependency, to ensure backwards compatibility.\n- Public flow interface for `CellMeasurer`. ([diogofcunha](https://github.com/diogofcunha) - [#1058](https://github.com/bvaughn/react-virtualized/pull/1058))\n- Improved build by setting `sideEffects` to `false`. ([0xR](https://github.com/0xR) - [#1064](https://github.com/bvaughn/react-virtualized/pull/1064))\n- 🐛 Fix flow type checks. ([RyanLiu0235](https://github.com/RyanLiu0235) - [#1066](https://github.com/bvaughn/react-virtualized/pull/1066))\n- Rollup for UMD build. ([TrySound](https://github.com/TrySound) - [#994](https://github.com/bvaughn/react-virtualized/pull/994))\n- Row direction support for `Masonry` ([bardiarastin](https://github.com/bardiarastin) - [#1071](https://github.com/bvaughn/react-virtualized/pull/1071))\n- Add lint-staged and precommit hooks ([TrySound](https://github.com/TrySound) - [#1082](https://github.com/bvaughn/react-virtualized/pull/1082))\n- Add `scrollToRow` and `scrollToColumn` support for ssr. ([microcood](https://github.com/microcood) - [#1072](https://github.com/bvaughn/react-virtualized/pull/1072))\n- Add `getTotalRowsHeight` and `getTotalColumnsWidth` to `Grid`. ([nihgwu](https://github.com/nihgwu) - [#1022](https://github.com/bvaughn/react-virtualized/pull/1022))\n- Allow top-right and bottom-left scrollbars in `MultiGrid` be hidden. ([RaviDasari](https://github.com/RaviDasari) - [#1040](https://github.com/bvaughn/react-virtualized/pull/1040))\n- Documentation changes\n  - Added `forceUpdateGrid` documentation for `MultiGrid`. ([kartikluke](https://github.com/kartikluke) - [#1079](https://github.com/bvaughn/react-virtualized/pull/1079))\n  - 🐛 Fixed typo in `Grid` docs. ([r-kot](https://github.com/r-kot) - [#1092](https://github.com/bvaughn/react-virtualized/pull/1092))\n  - 🐛 Fixed typo in `Collection` docs. ([skipjack](https://github.com/skipjack) - [#1050](https://github.com/bvaughn/react-virtualized/pull/1050))\n  - Added dynamically measured images example for `Masonry`. ([kirill-konshin](https://github.com/kirill-konshin) - [#1081](https://github.com/bvaughn/react-virtualized/pull/1081))\n\n##### 9.18.5\n\n- 🐛 Revert changes > 9.18.0\n\n<!--\n##### 9.18.1\n* ✨ Prevent generating cjs `prop-types` requires in esm build ([TrySound](https://github.com/TrySound) - [#950](https://github.com/bvaughn/react-virtualized/pull/950))\n-->\n\n##### 9.18.0\n\n- ✨ Add `onScrollbarPresenceChange` prop to `MultiGrid`.\n\n##### 9.17.3\n\n- 🐛 Fix `Grid` server-side rendering which was broken after natural scrolling tweak in Chrome. ([TrySound](https://github.com/TrySound) - [#970](https://github.com/bvaughn/react-virtualized/pull/970))\n\n##### 9.17.2\n\n- ✨ Eliminate unnecessary renders for `CellMeasurer` and `Grid`. ([bvaughn](https://github.com/bvaughn) - [#969](https://github.com/bvaughn/react-virtualized/pull/969))\n\n##### 9.17.1\n\n- 🐛 `CellMeasurer` works properly in iframes and popup windows. ([dfdeagle47](https://github.com/dfdeagle47) - [#968](https://github.com/bvaughn/react-virtualized/pull/968))\n\n##### 9.17.0\n\n- More natural scrolling speeds for large lists in Chrome. ([TrySound](https://github.com/TrySound) - [#936](https://github.com/bvaughn/react-virtualized/pull/936))\n- Support for multi-column sort added to `Table` component. Read more about this [here](https://github.com/bvaughn/react-virtualized/blob/master/docs/multiColumnSortTable.md). Special thanks to [CzBuCHi](https://github.com/CzBuCHi) for the initial proposal and iteration in PRs [#946](https://github.com/bvaughn/react-virtualized/pull/946) and [#957](https://github.com/bvaughn/react-virtualized/pull/957). ([bvaughn](https://github.com/bvaughn) - [#966](https://github.com/bvaughn/react-virtualized/pull/966))\n- ✨ Improved `Table` performance for cases with large numbers of columns. ([gannunziata](https://github.com/gannunziata) - [#942](https://github.com/bvaughn/react-virtualized/pull/942))\n- 🐛 Fixed potential initial render bug when using `CellMeasurer` with a `List`. ([OriR](https://github.com/OriR) - [#959](https://github.com/bvaughn/react-virtualized/pull/959))\n- 🐛 `Masonry` component now renders at least one column to avoid an invalid, Infinity height layout issue. ([djeeg](https://github.com/djeeg) - [#961](https://github.com/bvaughn/react-virtualized/pull/961))\n- 🎉 Optional `className` and `style` props added to `AutoSizer` component.\n\n##### 9.16.1\n\n- 🐛 Run server-side rendering tests under pure node environment and fix SSR in `WindowScroller` ([TrySound](https://github.com/TrySound) - [#953](https://github.com/bvaughn/react-virtualized/pull/953))\n- 🎉 Warn on passing wrong value to registerChild in `WindowScroller` ([TrySound](https://github.com/TrySound) - [#949](https://github.com/bvaughn/react-virtualized/pull/949))\n- 🐛 Fixed overrided merge `WindowScroller` scrollElement prop type ([TrySound](https://github.com/TrySound) - [#948](https://github.com/bvaughn/react-virtualized/pull/948))\n- 🎉 `AutoSizer` (via the `detectElementResize` helper) now supports properly rendering into iframes and child windows ([ahutchings](https://github.com/ahutchings) - [#900](https://github.com/bvaughn/react-virtualized/pull/900))\n\n##### 9.16.0\n\n- 🐛 Fixed window check for SSR in `detectElementResize` ([eqyiel](https://github.com/eqyiel) - [#945](https://github.com/bvaughn/react-virtualized/pull/945))\n- 🎉 Allowed custom `WindowScroller` child with `registerChild` in children function ([TrySound](https://github.com/TrySound) - [#940](https://github.com/bvaughn/react-virtualized/pull/940) and [#947](https://github.com/bvaughn/react-virtualized/pull/947))\n- 🐛 Fixed `WindowScroller` scrollElement prop type ([TrySound](https://github.com/TrySound) - [#939](https://github.com/bvaughn/react-virtualized/pull/939))\n\n##### 9.15.0\n\n- 🎉 Detected `WindowScroller` container (not only window) resize similar to `AutoSizer` ([TrySound](https://github.com/TrySound) - [#918](https://github.com/bvaughn/react-virtualized/pull/918))\n- 🐛 Prevented position breaking on `WindowScroller` container resize ([TrySound](https://github.com/TrySound) - [#920](https://github.com/bvaughn/react-virtualized/pull/920))\n- 🎉 Published `AutoSizer` Flow types ([TrySound](https://github.com/TrySound) - [#934](https://github.com/bvaughn/react-virtualized/pull/934))\n- 🎉 Published `WindowScroller` Flow types ([TrySound](https://github.com/TrySound) - [#915](https://github.com/bvaughn/react-virtualized/pull/915))\n\n##### 9.14.1\n\n- 🐛 Fixed server-side rendering bug in `WindowScroller` with undefined `window` variable.\n\n##### 9.14.0\n\n- 🎉 Added `serverHeight` and `serverWidth` props to `WindowScroller` for better server-side rendering support.\n\n##### 9.13.0\n\n- 🎉 Added `headerStyle` support for `Table` columns ([@mucsi96](https://github.com/mucsi96) - [#877](https://github.com/bvaughn/react-virtualized/pull/877))\n- 🐛 Fixed `Masonry` bug that caused cells to be unnecessarily destroyed and then recreated when new cells were measured - d561d9c\n\n##### 9.12.0\n\n- 🎉 Added `defaultWidth` and `defaultHeight` props to `AutoSizer` to better support server-side rendering.\n\n##### 9.11.1\n\n- 🐛 `Masonry` component now properly pre-renders as specified by `overscanByPixels`\n\n##### 9.11.0\n\n- 🐛 `List` and `Grid` scroll snapping / resetting bugfix #825 by @TrySound\n- 🐛 `MultiGrid` crash due to `scrollTo*` prop being `NaN` #829 by @mcordova47\n- 🐛 `MultiGrid` invalid `tabIndex` prop type #818 by @kalley\n- 🎉 Column default sort direction #833 by @mbseid\n\n##### 9.10.1\n\n- 🐛 Server-side rendering `window` reference bugfix\n- 🐛 `Grid.defaultProps` bugfix\n\n##### 9.10.0\n\n- ✨ `Grid` uses `requestAnimationFrame` instead of `setTimeout` for improved scroll-ended debounce timing ([@guilhermefloriani](https://github.com/guilhermefloriani) - [#742](https://github.com/bvaughn/react-virtualized/pull/742))\n- 🎉 `onRowRightClick` prop added to `Table` ([@damian-codilime](https://github.com/damian-codilime) - [#741](https://github.com/bvaughn/react-virtualized/pull/741))\n- 🎉 `Table` component now allow children that extend `Column` ([@CptLemming](https://github.com/CptLemming) - [#748](https://github.com/bvaughn/react-virtualized/pull/748))\n- 🐛 Firefox edge-case bugfix ([@ReinAkane](https://github.com/ReinAkane) - [#798](https://github.com/bvaughn/react-virtualized/pull/798))\n- 🎉 `containerProps` prop added to `Grid` ([@implausible](https://github.com/implausible) - [#778](https://github.com/bvaughn/react-virtualized/pull/778))\n- ✨ `Grid` accessibility improved via better aria attributes ([@smockle](https://github.com/smockle) - [#744](https://github.com/bvaughn/react-virtualized/pull/744))\n- ✨ `CellMeasurererCache.clearAll` also sets row and column counts ([@tcosentino](https://github.com/tcosentino) - [#796](https://github.com/bvaughn/react-virtualized/pull/796))\n\n##### 9.9.0\n\n- 🎉 `InfiniteLoader` API method `resetLoadMoreRowsCache` accepts optional parameter to auto-reload most recent range of rows. ([@BamaBoy](https://github.com/BamaBoy) - [#704](https://github.com/bvaughn/react-virtualized/pull/704))\n- 🎉 `MultiGrid` now supports scrolling when hovering over fixed rows or columns by way of new `enableFixedColumnScroll` and `enableFixedRowScroll` props. ([@danalloway](https://github.com/danalloway) - [#708](https://github.com/bvaughn/react-virtualized/pull/708))\n- 🎉 `WindowScroller` supports new configurable `scrollingResetTimeInterval` prop (similar to `Grid`). ([@djeeg](https://github.com/djeeg) - [#728](https://github.com/bvaughn/react-virtualized/pull/728))\n- 🐛 Edge-case bugfix for style caching of `Grids` locked with `ScrollSync`. ([@nathanpower](https://github.com/nathanpower) - [#727](https://github.com/bvaughn/react-virtualized/pull/727))\n- ✨ New `onScrollbarPresenceChange` prop added to `Grid`.\n\n##### 9.8.0\n\n- 🎉 `WindowScroller` supports `scrollToIndex` prop. ([@leoasis](https://github.com/leoasis) - [#643](https://github.com/bvaughn/react-virtualized/pull/643))\n- 🎉 Allow `ArrowKeyStepper` to be used as a controlled component. ([@mking-clari](https://github.com/mking-clari) - [#688](https://github.com/bvaughn/react-virtualized/pull/688))\n- 🎉New `handleScroll` method on `Grid` to better support custom scrollbars. ([@5angel](https://github.com/5angel) - [#693](https://github.com/bvaughn/react-virtualized/pull/693))\n- 🐛 Added edge-case guard to `WindowScroller` to prevent calling `setState` when unmounted. ([@liorbrauer](https://github.com/liorbrauer) - [#689](https://github.com/bvaughn/react-virtualized/pull/689))\n- 🐛 Fixed edge-case in `Grid` where setting an initial scroll-to offset with a `height` or `width` of 0 caused the scroll-to prop to be ignored when size later changed. ([#691](https://github.com/bvaughn/react-virtualized/pull/691))\n\n##### 9.7.6\n\n- ✨ Better aria roles set for `Table` column cells and headers. ([@jchen527](https://github.com/jchen527) - [#681](https://github.com/bvaughn/react-virtualized/pull/681))\n- 🐛 `CellMeasurer` restores `width` and `height` `style` values after measuring to avoid edge-case layout bugs. ([@marcelmokos](https://github.com/marcelmokos) - [#675](https://github.com/bvaughn/react-virtualized/pull/675))\n\n##### 9.7.5\n\n- ✨ Improved performance for `CellMeasurerCache` by removing some unnecessary computations for fixed-width/fixed-height use cases. ([@RaviDasari](https://github.com/RaviDasari) - [#676](https://github.com/bvaughn/react-virtualized/pull/676))\n- 🐛 `MultiGrid` ensures correct row/column indices are passed to `CellMeasurerCache` for top-right and bottom `Grid`s. ([#670](https://github.com/bvaughn/react-virtualized/pull/670))\n\n##### 9.7.4\n\n- 🎉 Add `nonce` attribute to `AutoSizer` for better [Content Security Policy compliance](https://www.w3.org/TR/2016/REC-CSP2-20161215/#script-src-the-nonce-attribute). ([@akihikodaki](https://github.com/akihikodaki) - [#663](https://github.com/bvaughn/react-virtualized/pull/663))\n- ✨ `Column` renderers now accept a `columnIndex` parameter as well. This allows multiple `Table` columns to more easily use `CellMeasurer` to compute the min row height. ([@BamaBoy](https://github.com/BamaBoy) - [#662](https://github.com/bvaughn/react-virtualized/pull/662))\n\n##### 9.7.3\n\n- Clear cell and style cache when controlled-scroll mode `Grid` stops scrolling. ([@leoasis](https://github.com/leoasis) - [#649](https://github.com/bvaughn/react-virtualized/pull/649))\n\n##### 9.7.2\n\n- ✨ Removed lingering `React.PropTypes` reference in `InfiniteLoader`.\n\n##### 9.7.1\n\n- ✨ Added `prop-types` dependency to avoid deprecation warnings for React 15.5+.\n\n##### 9.7.0\n\n- Added public animation-friendly API methods to `Grid`/`List`/`Table` for an alternative to props-based animating. ([@imadha](https://github.com/imadha) - [#641](https://github.com/bvaughn/react-virtualized/pull/641))\n\n##### 9.6.1\n\n- 🐛 Fixed module syntax error in vendered file.\n\n##### 9.6.0\n\n- 🎉 `WindowScroller` and `Grid` now support horizontal window-scrolling via new `autoWidth` property. ([@maxnowack](https://github.com/maxnowack) - [#644](https://github.com/bvaughn/react-virtualized/pull/644))\n- 🐛 Fixed a Content Security Policy (CSP) issue in an upstream dependency that impacted users of the `Masonry` component. For more information see issue [#640](https://github.com/bvaughn/react-virtualized/issues/640).\n- ✨ `List` and `Table` always overscan 1 row in the direction _not_ being scrolled to better support keyboard navigation (via TAB and SHIFT+TAB). For more information see [issue #625](https://github.com/bvaughn/react-virtualized/issues/625).\n- ✨ `Grid` no longer alters scroll direction for one axis (eg vertical) if a scroll event occurs for another axis (eg horizontal).\n\n##### 9.5.0\n\n- 🎉 `Grid` supports state-override of `isScrolling` value via new `isScrolling` prop. This enables cache-while-scrolling of cells when used with `WindowScroller`. ([@olavk](https://github.com/olavk) - [#639](https://github.com/bvaughn/react-virtualized/pull/639))\n\n##### 9.4.2\n\n- 🐛 Small accessibility fix to `MultiGrid` so that focus outline shows through by default for main (bottom/right) `Grid`. Top and left `Grid`s are also not tab-focusable by default now since they are scroll-observers anyway.\n- ✨ Added `columnWidth` parameter to `ColumnSizer` and deprecated `getColumnWidth` callback. The callback was not necessary since `columnWidth` doesn't change without a re-render and fixed number values perform better in `Grid` due to some internal optimizations anyway.\n\n##### 9.4.1\n\n- 🐛 Edge-case `InfiniteLoader` bug fix; prevent jumping to the first row when scrolling fast. ([@reVrost](https://github.com/reVrost) - [#632](https://github.com/bvaughn/react-virtualized/pull/632))\n- 🐛 Reverted unexpected regression from [#616](https://github.com/bvaughn/react-virtualized/pull/616) until a safer fix can be found.\n\n##### 9.4.0\n\n- 🎉 New `Masonry` component optimized for Pinterest-style layouts. Check out the [docs](https://github.com/bvaughn/react-virtualized/blob/master/docs/Masonry.md) and [demo page](https://bvaughn.github.io/react-virtualized/#/components/Masonry) to learn more. ([#618](https://github.com/bvaughn/react-virtualized/pull/618))\n- 🎉 `MultiGrid` supports `scrollLeft` and `scrollTop` props for controlled scrolling. ([@julianwong94](https://github.com/julianwong94) - [#624](https://github.com/bvaughn/react-virtualized/pull/624))\n- 🎉 New `direction` parameter passed to `overscanIndicesGetter` with values \"horizontal\" or \"vertical\". ([@offsky](https://github.com/offsky) - [#629](https://github.com/bvaughn/react-virtualized/pull/629))\n- ✨ Replaced inline `require` statement with header `import` in `Grid` for better integration with the Rollup module bundler. ([@odogono](https://github.com/odogono) - [#617](https://github.com/bvaughn/react-virtualized/pull/617))\n- 🐛 Improved guard for edge-case scrolling issue with rubberband scrolling in iOS. ([@dtoddtarsi](https://github.com/offsky) - [#616](https://github.com/bvaughn/react-virtualized/pull/616))\n- ✨ Replaced `getBoundingClientRect()` with slightly faster `offsetWidth` and `offsetHeight` inside of `AutoSizer`.\n- ✨ `AutoSizer` no longer re-renders nor calls `onResize` callback unless `width` and/or `height` have changed (depending on which properties are being watched).\n\n##### 9.3.0\n\n- 🎉 Added `resetLoadMoreRowsCache` method to `InfiniteLoader` to reset any cached data about loaded rows. This method should be called if any/all loaded data needs to be refetched (eg a filtered list where the search criteria changes). ([#612](https://github.com/bvaughn/react-virtualized/issues/612))\n\n##### 9.2.3\n\n- 🐛 `CellMeasurer` should work better out of the box with `MultiGrid`.\n- 🐛 `CellMeasurerCache` should return correct values from `rowHeight` and `columnWidth` functions when `keyMapper` is used. ([#613](https://github.com/bvaughn/react-virtualized/pull/613))\n\n##### 9.2.2\n\n- 🐛 Fixed small scrollbar offset bug in `MultiGrid`. ([#609](https://github.com/bvaughn/react-virtualized/issues/609))\n\n##### 9.2.1\n\n- 🐛 Fixed potential scrollbar offset bug in `MultiGrid` by giving top and left `Grid`s a little extra space to scroll into. ([#535](https://github.com/bvaughn/react-virtualized/pull/535))\n\n##### 9.2.0\n\n- 🎉 New `Table` prop, `headerRowRenderer`. ([@kaoDev](https://github.com/kaoDev) - [#600](https://github.com/bvaughn/react-virtualized/pull/600))\n- 🎉 All `Table` event handlers now receive a named `event` params ([@paulbrom](https://github.com/paulbrom) - [#605](https://github.com/bvaughn/react-virtualized/pull/605))\n- 🎉 Aria roles for `Table` improved to specify `role=\"row\"` for table rows and `role=\"rowgroup\"` for inner `Grid`. ([@jchen527](https://github.com/jchen527) - [#607](https://github.com/bvaughn/react-virtualized/pull/607))\n- 🐛 Calling `scrollToRow` for `List` or `Table` no longer potentially messes up horizontal scroll position. ([#603](https://github.com/bvaughn/react-virtualized/issues/603))\n\n##### 9.1.0\n\n- 🎉 Public method `setScrollIndexes` added to `ArrowKeyStepper` to enable easier overrides of current/default focused cell. - ([@alexandro81](https://github.com/alexandro81) - [#592](https://github.com/bvaughn/react-virtualized/pull/592))\n- ✨ Replaced `value instanceof Function` checks with `typeof value === 'function'` for improved robustness with iframes/frames/popups. (Learn more [here](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_context_(e.g._frames_or_windows)>).) ([@rickychien](https://github.com/rickychien) - [#596](https://github.com/bvaughn/react-virtualized/pull/596))\n- 🐛 `Grid` props `scrollToColumn` and `scrollToRow` as well as `Collection` prop `scrollToCell` now default to `-1` to avoid false positives from `null>=0` check. - ([#595](https://github.com/bvaughn/react-virtualized/issues/595))\n\n##### 9.0.5\n\n- 🎉 Explicitly set `width`/`height` style to \"auto\" before re-measuring `CellMeasurer` content so that new measurements can be taken. ([#593](https://github.com/bvaughn/react-virtualized/issues/593))\n- 🐛 CellMeasurerCache now correctly recomputes cached row height and column width values when cells are cleared. ([#594](https://github.com/bvaughn/react-virtualized/issues/594))\n\n##### 9.0.4\n\n- 🐛 Moved flow-bin from 'dependencies' to 'devDependencies'. This was accidentally placed as a dep before.\n\n##### 9.0.3\n\n- 🐛 `Grid` takes scrollbar size into account when aligning cells for `scrollToColumn` or `scrollToRow` usage. ([#543](https://github.com/bvaughn/react-virtualized/issues/543))\n\n##### 9.0.2\n\n- 🎉 Added additional DEV-only warnings for improperly configured `CellMeasurerCache` based on user-feedback for the new API.\n- 🐛 Fixed edge-case where restoring `columnCount` from 0 wouldnt properly restore previous `scrollToRow` offset (and vice versa for `rowCount` and `scrollToColumn`)\n- Updated `Grid` and `Collection` to move some state-setting logic related to offsets from `componentWillUpdate` to `componentWillReceiveProps`. This change should have no externally visible impact. ([#585](https://github.com/bvaughn/react-virtualized/issues/585))\n\n##### 9.0.1\n\n- 🐛 Edge-case bug with scroll-to-index and cell size function property ([#565](https://github.com/bvaughn/react-virtualized/issues/565))\n- 🐛 Edge-case bug with `WindowScroller` and mocked `window` object\n\n# 9.0.0\n\nVersion 9 changes and upgrade steps are described in detail on the [version 9 pull request](https://github.com/bvaughn/react-virtualized/pull/577).\n\n##### 8.11.4\n\n- 🐛 Better guard against minification/uglification in `ColumnSizer` when verifying child is either a `Grid` or a `MultiGrid`. (#558)\n\n##### 8.11.3\n\n- Adding missing `scrollToRow` method to `List` and `Table` (as pass-thrus for `Grid.scrollToCell`).\n- 🐛 Bugfixes with `MultiGrid` resize handling and caching. ([@codingbull](https://github.com/codingbull) - [#552](https://github.com/bvaughn/react-virtualized/pull/552))\n- 🐛 List checks it row-style object has been frozen before modifying width; (fix for [upcoming React 16 change](https://github.com/facebook/react/commit/977357765b44af8ff0cfea327866861073095c12#commitcomment-20648713)).\n- 🐛 `MultiGrid` better handles case where `rowCount === fixedRowCount`.\n\n##### 8.11.2\n\n- 🐛 Added `MultiGrid` method `measureAllCells`; deprecated misnamed `measureAllRows` method.\n\n##### 8.11.1\n\n- 🐛 Fixed regression in `WindowScroller` when browser is resized. ([@andrewbranch](https://github.com/andrewbranch) - [#548](https://github.com/bvaughn/react-virtualized/pull/548))\n\n##### 8.11.0\n\n- 🐛 Minor Preact compat fix to element resize detector; see [developit/preact-compat/issues/228](https://github.com/developit/preact-compat/issues/228)\n- 🎉 New `scrollToCell` public method added to `Grid`.\n\n##### 8.10.0\n\n- 🎉 `WindowScroller` supports custom target element via a new `scrollElement` prop; defaults to `window` for backwards compatibility. ([@andrewbranch](https://github.com/andrewbranch) - [#481](https://github.com/bvaughn/react-virtualized/pull/481))\n- 🐛 `MultiGrid` supports `onScroll` property. ([@Pana](https://github.com/Pana) - [#536](https://github.com/bvaughn/react-virtualized/pull/536))\n- 🎉 New id-based `CellMeasurer` cell size cache, `idCellMeasurerCellSizeCache`. ([@bvaughn](https://github.com/bvaughn) - [#538](https://github.com/bvaughn/react-virtualized/pull/538))\n\n##### 8.9.0\n\n- New `MultiGrid` reduces the boilerplate required to configure a `Grid` with fixed columns and/or rows.\n- `defaultTableRowRenderer` passes new `rowData` param to event handlers (in addition to `index`).\n- 🐛 Styles are no longer cached while scrolling for compressed lists ([@nickclaw](https://github.com/nickclaw) - [#527](https://github.com/bvaughn/react-virtualized/pull/527))\n- 🐛 Cell cache is reset once `InfiniteLoader` load completes ([@nickclaw](https://github.com/nickclaw) - [#528](https://github.com/bvaughn/react-virtualized/pull/528))\n- Add loose-envify support for Browserify users ([@chrisvasz](https://github.com/chrisvasz) - [#519](https://github.com/bvaughn/react-virtualized/pull/519), [#523](https://github.com/bvaughn/react-virtualized/pull/523))\n- `dom-helpers` dependency relaxed to support 2.x and 3.x versions ([@danez](https://github.com/danez) - [#522](https://github.com/bvaughn/react-virtualized/pull/522))\n- 🐛 `Collection` no longer drops its `overflow` style in certain conditions; see facebook/react/issues/8689 for more info.\n\n##### 8.8.1\n\nFixed a bug with `Grid` style-cache that caused stale cell-sizes to be used when `Grid` resized.\n\n##### 8.8.0\n\n`Grid` now temporarily caches inline style objects to avoid causing shallow compare to fail unnecessarily (see [PR 506](https://github.com/bvaughn/react-virtualized/pull/506)).\n\n`AutoSizer` internal `detectElementResize` library now no longer creates duplicate `<style>` tags when unmounted and remounted (see [PR 507](https://github.com/bvaughn/react-virtualized/pull/507)).\n\n##### 8.7.1\n\nReverted part of the change introduced in version 8.6.0 that changed the behavior regarding controlled/uncontrolled `scrollTop` and `scrollLeft` props and `Grid` in a way that was not backwards compatible. (See issue #490 for more.)\n\n##### 8.7.0\n\nAdded `updatePosition` to `WindowScroller` to handle case when header items change or resize. `WindowScroller` also better handles window resize events.\n\n##### 8.6.1\n\nUpdated `CellSizeCache` interface for the better perfomance by removing `has` methods, reducing a double hashtable lookup to a single lookup. Special thanks to @arusakov for this contribution!\n\n##### 8.6.0\n\n`CellMeasurer` passes `index` param (duplicate of `rowIndex`) in order to more easily integrate with `List` by default.\n\n`Grid` now better handles a mix of controlled and uncontrolled scroll offsets. Previously changes to one offset would wipe out the other causing cells to disappear (see PR #482). This is an edge-case bug and only impacted an uncommon usecase for `Grid`. As such this change is expected to only impact only a small percetange of users.\n\n##### 8.5.3\n\nChanged overscan rows/cols behavior as described [here](https://github.com/bvaughn/react-virtualized/pull/478).\nThis change targets performance improvements only and should have no other noticeable impact.\n\n##### 8.5.2\n\nAdded guard against potential `null` return value from `getComputedStyle` for hidden elements (see PR #465).\n\n##### 8.5.1\n\n`Table` header height is no longer subtracted from overall (rows) height if header is disabled.\nThanks to @Jakehp for this contribution!\n\n##### 8.5.0\n\nAdded `disabled` property to `ArrowKeyStepper`; when `true` this component ignores keyboard events.\n\n##### 8.4.1\n\n`Collection` and `Grid` now set a default `direction: ltr` style property.\nNeither component gracefully handles RTL layout at the moment and so it is disabled by default.\nCells within either component can be layed out RTL without any problem though.\nTo do so, just add an additional `direction: rtl` style property via the cell renderer.\n\n##### 8.4.0\n\n`ArrowKeyStepper` incrementing behavior can now be further customized via new `mode` prop.\nDefault behavior will remain the same (eg `mode='edges'`).\nUse `mode='cells'` to prefer smaller increments.\n\n##### 8.3.1\n\nFixed `scrollToAlignment` bug in `Collection` that caused cells not to render under certain conditions.\nSpecial thanks to @coluccini for spotting and fixing this bug!\n\n##### 8.3.0\n\n`cellRenderer` and `rowRenderer` callbacks now accept an additional property, `isVisible`.\nThis property can be used to differentiate between visible and overscanned cells.\nThanks to @mbrevda for this feature!\n\n##### 8.2.0\n\nAdded optional `id` prop to `Collection`, `Grid`, `List`, and `Table`.\nThanks to @mnquintana for the contribution!\n\n##### 8.1.1\n\nWrapped component `propTypes` with `process.env.NODE_ENV !== 'production'` so they can be stripped from production builds.\nThanks to @mbrevda for the suggestion and contribution.\n\n##### 8.1.0\n\nAdded `containerStyle` property to `Grid`.\nThis property allows custom styling to be applied to the inner cell-containing div.\nThis can be used to enable padding within the grid.\nFor more context see: github.com/metabase/metabase/pull/3547\n\nRefactored the `detect-element-resize` util to export a factory function.\nThis allows `AutoSizer` to import it initially but defer execution until mounted.\n(Executing immediately causes problems for server-side rendering context.\nUsing a deferred `require` causes problems for es6 bundlers, eg Rollup.)\n\nFixed an edge-case that occurred for slower browsers when mounting and then quickly unmounting an `AutoSizer`.\nIn some cases, an animation event was left attached.\nThis event is now specifically checked for and removed.\nSpecial thanks to @yb (PR #436) for this bugfix contribution.\n\n##### 8.0.13\n\nReplaces references to `getComputedStyle` with `window.getComputedStyle` to better support Enzyme.\nThanks to @DevinClark for the contribution!\n\n##### 8.0.12\n\nAdded \"module\" entry to `package.json` in order to support ES modules with the latest Webpack.\nThis should enable tree-shaking support ouf of the box for Webpack.\nRollup will continue to use the \"jsnext:main\" entry to my knowledge.\n\nFor more info see https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md\nRelated issues: webpack/webpack#1979, bvaughn/react-virtualized/issues/427\n\n##### 8.0.11\n\nFixed an invalid export for `IS_SCROLLING_TIMEOUT` inside the `WindowScroller` module.\n\n##### 8.0.10\n\nChanged the embedded `detect-element-resize` library `includes` call to use `indexOf` instead for better IE compatibility.\n\n##### 8.0.9\n\n`scrollToColumn` and `scrollToRow` offsets will always be 0 when `Grid` size is <= 0.\nTechnically this is an invalid size but a 0 offset is a more meaningful return value.\nPreviously the min/max offset check could result in a positive offset in this case (which is invalid).\n\n##### 8.0.8\n\nFixes bug in resize detector that broke scrollbar functionality in Safari.\n\n##### 8.0.7\n\nFixed a small `Table` alignment issue due to conflicting `padding-right` and `width: 100%` value.\nAlso fixed an edge-case horiztonal scrollbar that was appearing for some tables.\n\n##### 8.0.6\n\nUpdated the embedded `detect-element-resize` library to reduce the number of reflows it triggered.\nThis library was forcing reflow (to measure itself) each time a scroll event occurred within its children.\nThis was unnecessary since it really only cared about resizes to its expand-trigger and collapse-trigger.\nI have updated it to ignore scroll events triggered by other DOM elements.\n\nAs a result of this change, `AutoSizer` no longer needs to trap bubbling \"scroll\" events.\nThis opens the door to potentially using passive scroll event handlers in the future.\nIt also fixes a long-standing bug that prevented scrollbars from working correctly within auto-sized-content.\n\n##### 8.0.5\n\n`Grid` notifies `onScroll` callback if `scrollLeft` or `scrollTop` have changed in response to prop changes (including `scrollToColumn` or `scrollToRow`).\n\n##### 8.0.4\n\nFixed a small regression in the `Table` component that caused a horizontal scrollbar to appear.\n\n##### 8.0.3\n\nRemoved unnecessary `react-router` dependency.\n(This was accidentally added as to `dependencies` when it should have been in `devDependencies`).\n\n##### 8.0.2\n\nImproved fix for regression with scaled `Grid`s that caused position offsets to be incorrect while scrolling.\n\n##### 8.0.1\n\nInitial attempt at fixing regression with scaled `Grid`s that caused position offsets to be incorrect while scrolling.\n\n# 8.0.0\n\nVersion 8 changes are described in detail on the [Version 8 Roadmap](https://github.com/bvaughn/react-virtualized/issues/386).\nUpgrade instructions and [jscodeshift](https://github.com/facebook/jscodeshift) mods can be found [here](docs/upgrades/Version8.md).\n\n##### 7.24.3\n\nES module build (_jsnext:main_ target) updated to depend on Babel's `transform-runtime` rather than referencing global `babelHelpers`.\nThis should fix support within projects like `react-boilerplate`.\n\n##### 7.24.2\n\n`Grid` and `Collection` now set `pointer-events: ''` (instead of _auto_) after scrolling has stopped to avoid overriding parents who may have specified `pointer-events: 'none'`.\n\n##### 7.24.1\n\nRefactored `AutoSizer` slightly to add support for `react-lite`.\n\nNote that if you intend to use the UMD build of `react-lite` the following lines are required before `react-virtualized` is loaded:\n\n```js\nReact.addons = {\n  shallowCompare(context, nextProps, nextState) {\n    return React.PureComponent.prototype.shouldComponentUpdate(\n      nextProps,\n      nextState,\n    );\n  },\n};\nReactDOM = React;\n```\n\n##### 7.24.0\n\nAdded `autoHeight` prop to `Collection` so that it can more easily be used with `WindowScroller`.\n\n##### 7.23.0\n\n`Grid` scrolling timeout for pointer events can be customized now via `scrollingResetTimeInterval` property.\nThis defaults to 150ms (as before).\n\nAlso addressed a couple of small bugs as well:\n\n- Multiple `WindowScroller` instances can be used on a single page now without interfering with each other's `pointer-events` settings.\n- Calling `recomputeGridSize` on `Grid` (or any of its wrapping HOCs) will clear any pending cell cache to avoid edge-case issue where a scrolling `Grid` has invalid cached cells due to a change in the underlying collection data.\n\n##### 7.22.3\n\nWhile a scroll is in progress, `Grid` focuses overscan on the direction being scrolled- doubling up the number of overscanned cells.\nThis reduces the amount of empty space that temporarily appears when a user is quickly scrolling.\nIt does not increase the overall number of cells being rendered (and so does not negatively impact performance).\n\n##### 7.22.2\n\nIn the event that size or cell count decreases for a `Grid`, remaining cells are no longer remeasured in order to verify the current scroll offset.\nInstead the most recent measurements are used.\nThis change should positively impact performance but should have no other affect.\n\n##### 7.22.1\n\n`InfiniteLoader` now better handles `FlexTable` and `VirtualScroll` children by calling `forceUpdateGrid` when defined.\nThis prevents rows from being stuck in a visual \"loading\" state until a user scrolls.\n\n##### 7.22.0\n\nUpdated the exported `defaultCellMeasurerCellSizeCache` to support configured uniform column widths and/or row heights.\nThis allows greater customization and flexibility than the version released in 7.21.0.\nFor backwards compat `uniformSizeCellMeasurerCellSizeCache` is still exported but also points to `defaultCellMeasurerCellSizeCache`.\n\n##### 7.21.1\n\nLowered the `ScalingCellSizeAndPositionManager` maximum scroll threshold from 10M to 1.5M pixels to accommodate Edge's lower-than-expected `scrollTop` maximum.\n\n##### 7.21.0\n\nNew `cellSizeCache` added for `CellMeasurer` (`uniformSizeCellMeasurerCellSizeCache`) for cells that have a _dyanmic but uniform_ width or height.\nThis cache will measure only a single cell and then return its width and height for all other cells.\nThis allows for greater performance optimization for use cases like virtualized drop-down lists, etc.\n\n##### 7.20.0\n\n`Collection` now temporarily caches cells while scrolling is in progress.\n\n##### 7.19.4\n\nInitialize `WindowScroller` height to `window.innerHeight` if component is initially rendered in the browser.\nThis fixes temporary zero-height that would cause previous scroll position to be lost when a user navigates back in their history.\n\n##### 7.19.3\n\nImproved memoization in `InfiniteLoader` to reduce repeated calls to `loadMoreRows`.\n\n##### 7.19.2\n\nEdge-case bug fix for `WindowScroller` when user returning to a page (via browser back button) that has already been scrolled. Previously, `WindowScroller` failed to correctly calculate its position from the top under these conditions. Now it calculates the proper position.\n\n##### 7.19.1\n\n`WindowScroller` auto-restores body pointer-events when unmounted to fix edge-case bug when component was unmounted during (or _right after_) scrolling.\n\n##### 7.19.0\n\n`CellMeasurer` now properly uses `shallowCompare`.\nIt also supports a custom caching strategy for measured cell sizes (see `cellSizeCache` prop).\n\n`Collection` supports 2 new properties: `horizontalOverscanSize` and `verticalOverscanSize`.\nThese properties enable the `Collection` to \"overscan\" its content similar to how `Grid` does.\nThis can reduce flicker around the edges when a user scrolls quickly.\n\n##### 7.18.1\n\nFixed edge-case scroll-to-cell bug in `Grid` when row or column count increases from 0 after a scroll-offset was previous assigned.\nFor more info see issue #218.\n\n##### 7.18.0\n\nAdded named exports for `defaultFlexTableCellDataGetter`, `defaultFlexTableCellRenderer`, and `defaultFlexTableHeaderRenderer` due to user request.\n\n##### 7.17.1\n\nFixed a `Collection` bug that could cause the `noContentRenderer` to be shown when a collection happened to contain no visible children (due to a sparse layout).\n\n##### 7.17.0\n\n`CellMeasurer` exposes 2 new public methods: `resetMeasurementForColumn` and `resetMeasurementForRow`.\nThese methods allow a finer grain of control over the cell measurement cache.\nLearn more [here](docs/CellMeasurer.md#children-function).\n\n##### 7.16.0\n\nAdded new property `autoContainerWidth` to `Grid`.\nIt can be used to set the width of the inner scrollable container to 'auto'.\nThis is useful for single-column Grids to ensure that the column doesn't extend below a vertical scrollbar.\nBy default this is disabled but `VirtualScroll` and `FlexTable` both enable it on their inner `Grid`s.\n\n##### 7.15.1\n\nRenamed `Grid` refs within `FlexTable` and `VirtualScroll` from `_grid` to `Grid`.\n\nThis is done to better support interoperability between `FlexTable` and `react-sortable-hoc` which requires a handle on the inner `Grid`.\nTechnically the change is not required but it is more inline with JavaScript naming conventions (since I plan to preserve this property from an Api perspective).\n\n##### 7.15.0\n\nAdded support for greater `FlexTable` customization via a new `rowRenderer` property.\nAlso exported default implementation as `defaultFlexTableRowRenderer`.\nLearn more [here](docs/FlexTable.md#rowrenderer).\n\n##### 7.14.0\n\n`WindowScroller` component passes new named argument, `isScrolling`, to its child render function.\n\n##### 7.13.0\n\nAdded `onRowDoubleClick` support to `FlexTable`.\n\n##### 7.12.3\n\n`CellMeasurer` implementation changed to use `ReactDOM.unstable_renderSubtreeIntoContainer` instead of `ReactDOMServer.renderToString` in order to support `context`.\n`Grid` has been changed slightly as well to calculate its visible children just before `render` (instead of in it).\nThis change is not expected to have any public-facing consequences beyond supporting the `context` property for `CellMeasure`d cells.\nThanks to @jquense for this contribution!\n\n##### 7.12.2\n\nUser-specified `Grid` and `Collection` styles can now override default style options (eg overflow, height/width).\n\n##### 7.12.1\n\nFixed unexpected usage of `recomputeRowHeights` / `recomputeGridSize` where method is called with an index higher than the last measured row/cell index.\nCell measurer now properly updates the value only if the requested index is lower than the most-recently-measured cell.\n\n##### 7.12.0\n\n`FlexTable` `rowStyle` property can now be a on Object _or_ a function similar to the `rowClassName` property.\n\n##### 7.11.8\n\nFixed edge-case bug previously possible when combining the `scrollToAlignment` property with `scrollToRow` or `scrollToColumn` at the end of a collection.\nUnder certain circumstances this caused the grid to scroll too far; that has now been resolved.\nCenter-alignment logic has also been improved to better align scrolled cells.\n\n##### 7.11.7\n\nRemoved `xmlns` property from `<svg>` tag in `SortEditor` to avoid React 15.2 property warning.\n\n##### 7.11.6\n\nFixed `CellMeasurer` throws \"Only a ReactOwner can have refs\" error.\n\n##### 7.11.5\n\nSmall change to inline styles for `Grid` to work around obscure bug where an initial scroll offset prop is specified before external CSS stylesheets have loaded.\n\n##### 7.11.4\n\nAdded more pass-thru props from `VirtualScroll` to `Grid` to ensure that when `VirtualScroll` re-renders (due to changed props) so does its inner `Grid`.\nBoth components are still \"pure\" (from a shallow comparison perspective).\n\n##### 7.11.3\n\nUpdated `Grid` and `VirtualScroll` so that the width of rows in a `VirtualScroll` does not stretch beneath a scrollbar (if one is visible).\n\n##### 7.11.2\n\nAdded more pass-thru props from `FlexTable` to `Grid` to ensure that when `FlexTable` re-renders (due to changed props) so does its inner `Grid`.\nBoth components are still \"pure\" (from a shallow comparison perspective).\nThis just avoids the unintuitive use-case where some table properties (eg headers) may change while others (eg rows) do not.\n\n##### 7.11.1\n\nUpdated UMD build to remove `react-addons-shallow-compare` from the build.\nUMD users should use `react-with-addons.min.js` (should have already been using it in fact) instead of `react.min.js`.\nThanks to @ducky427 for reporting this oversight and updating the Webpack config!\n\n##### 7.11.0\n\nThe `recomputeRowHeights` method of `FlexTable` and `VirtualScroll` accepts an optional index (defaults to 0) after which to recompute sizes.\nThe `recomputeGridSize` method of `Grid` accepts named `columnIndex` and `rowIndex` parameters tha function similarly.\n\nThis allows for a finer grained optimization when invalidating a collection.\nIf, for example, a specific row in a table has resized- it is now possible to recompute the positions of only the rows occurring after this row.\nBecause of the way react-virtualized just-in-time measures rows, this will also avoid re-measuring any but the visible rows at the time of the change.\n\nIf several items in the collection have changed and you are unsure of which, it is safest to recompute all columns/rows.\nThis remains the default behavior unless override indices are specified as parameters.\n\n##### 7.10.0\n\nNew `gridClassName` and `gridStyle` pass-through properties added to `FlexTable`.\n\n##### 7.9.1\n\nFixed edge-case bug in `FlexTable` that caused the inner `Grid` not to update when there was a vertical scrollbar.\nThis in turn caused headers to be misaligned.\n\n##### 7.9.0\n\nAdded `forceUpdateGrid` method to `FlexTable` and `VirtualScroll` to enable the inner `Grid` to be udpated without resorting to recomputing cached row heights.\n\n##### 7.8.3\n\n`Grid` no longer checks `scrollTop` when `autoHeight=true` in order to avoid unnecessary reflows/repaints.\nThis change only impacts `WindowScroller` use cases.\n\n##### 7.8.2\n\nFixed edge-case problem with `FlexTable` where changes to the number of children (`FlexColumn`s) didn't update the inner `Grid`.\n\n##### 7.8.1\n\nReverted default `tabIndex = null` value for `Grid` (introduced in 7.8.0) due to a negative accessibility impact.\nA focused `Grid` paints significantly more while scrolling which impacts FPS.\nUnfortunately it is a necessity to support keyboard scrolling properly and so it's the default once more.\nThis can be explicitly disabled by setting `tabIndex = null` if you want.\n\n##### 7.8.0\n\nScrolling performance improvements for `FlexTable` and to a lesser extent `Grid`.\n\nThe primary change to `Grid` is that `tabIndex` will be set to `null` by default instead of `0`.\nThis improves repainting performance when a `Grid` is being scrolled.\n\nThis release removes the `FlexTable__truncatedColumnText` wrapper column and collapses its styles into `FlexTable__rowColumn`.\nIf you were depending on the former class you will want to update your dependencies.\nI was on the fence about this in terms of compatibility, but I feel this is more of an internal implementation detail than it is public-facing API.\n\nThis release also changes the primary `FlexTable` cell from a flex container to a block.\nThis means that if you were right-aligning text within a column you will need to change from `align-items: flex-end` to `text-align: right`.\n\n##### 7.7.1\n\nExport the `defaultCellRangeRenderer` used by `Grid` in order to enable easier composition.\n\n##### 7.7.0\n\nAdded configurable `tabIndex` property to `Grid`, `FlexTable`, and `VirtualScroll`.\nDefault value remains 0 but can now be overridden.\n\n##### 7.6.0\n\nNew property added to `Grid`, `FlexTable`, and `VirtualScroll` to enable custom CSS class name and style to be added to the outer cell decorator.\nThis can be used to greater customize styles as well as to better implement custom (non-flexbox) styles for IE9.\nThanks to nicholasrq@ for this contribution!\n\n##### 7.5.0\n\nNew `WindowScroller` HOC added to enable a `FlexTable` or `VirtualScroll` component to be scrolled based on the window's scroll positions.\nThis can be used to create layouts similar to Facebook or Twitter news feeds.\nBig thanks to minheq@ for this contribution!\n\n##### 7.4.0\n\nAdded mouse-over and mouse-out row-level events to `FlexTable`. Thanks to @queeto for the PR!\n\n##### 7.3.3\n\nFixed unintention regression in IE10 support introduced with `ScalingCellSizeAndPositionManager` extending `CellSizeAndPositionManager`.\nInheritance has been replaced with composition for this case in order to simplify IE10 compatibility.\nNotice that Babel `babel-polyfill` is still required in order to support other ES5 features.\n\n##### 7.3.2\n\nEdge-case bug fix for `CellMeasurer` in the event that its `getRowHeight` or `getColumnWidth` method gets called before the initial render completes.\n\n##### 7.3.1\n\nIncreased the safe-scale size from 1,000,000 to 10,000,000 to make for better UX.\n\n##### 7.3.0\n\n`Grid` (and its HOCs `FlexTable` and `VirtualScroll`) now support larger heights and widths than browsers support natively.\nFor example, the current version of Chrome will not allow users to scroll pass ~33.5M pixel offset.\nTo work around this limitation, `Grid` increases the density of cells, shifting them as a ratio of what the full scrollable size would be to a browser-safe size.\nThis should be more or less transparent to users, although in extreme cases it can lead to _really sensitive_ scroll responsiveness.\n\n##### 7.2.0\n\nAdded new method- `measureAllCells`- to `Grid`, `FlexTable`, and `VirtualScroll` to force-measure all cells.\nThis supports special use-cases where deferred measuring is not desired.\n\nAdded `estimatedRowSize` property to `FlexTable` and `VirtualScroll` to be passed through to the inner `Grid`.\n\nAlso added guard to ensure the `onScroll` callback for `Collection`, `Grid`, `FlexTable`, and `VirtualScroll` is never called with a negative number.\n\n##### 7.1.3\n\nThe inner javascript-detect-element-resize library used by `AutoSizer` now passes the proper `useCapture` value when removing listeners as well. This should prevent lingering event listeners in certain cases. Thanks to @cyberxndr for this fix.\n\n##### 7.1.2\n\nAdded \"_center_\" option for `scrollToAlignment` property of `Collection`, `Grid`, `FlexTable`, and `VirtualScroll`.\nThanks to @edulan for the contribution!\n\nAlso added a check to avoid rendering content frmo `noContentRenderer` if `width` or `height` are 0.\n\n##### 7.1.1\n\nResolved edge-case bug that caused the bottom/right cells in a `Grid` or `Collection` to be partially overlapped by a scrollbar.\nThanks to @anjianshi for reporting this and collaborating on the fix!\n\n##### 7.1.0\n\nAdded `scrollToAlignment` property to `Collection`, `Grid`, `FlexTable`, and `VirtualScroll` to offer finer-grained control of how scrolled-to cells are aligned.\nDefault behavior (\"_auto_\") remains unchanged- the least amount of scrolling will occur to ensure that the specified cell is visible.\n\n##### 7.0.5\n\nFixed edge-case bug where `InfiniteLoader` did not respect `minBatchSize` setting when a user was scrolling up.\n\n##### 7.0.4\n\nAdded `scrollLeft` and `scrollTop` parameters to `cellRangeRenderer` callback for `Grid`.\n\n##### 7.0.3\n\nAdded `box-sizing: border-box` rules to `.FlexTable__headerRow` and `.FlexTable__Grid` classes to fix edge-case scrollbar bug experienced by some users.\n\n##### 7.0.2\n\nAdded `recomputeCellSizesAndPositions` method to `Collection` (to pass through to inner `CollectionView`).\n\n##### 7.0.1\n\nReplaced single occurence of `Number.isNaN` with `isNaN` to avoid IE compatibility issues.\n\n# 7.0.0\n\nVersion 7 changes are described in detail on the [Version 7 Roadmap wiki page](https://github.com/bvaughn/react-virtualized/wiki/Version-7-Roadmap).\nUpgrade instructions and [jscodeshift](https://github.com/facebook/jscodeshift) mods can also be found there.\n\nTo run a code mod, check out react-virtualized (or download the codemod) and then...\n\n```\njscodeshift -t /path/to/react-virtualized/codemods/6-to-7/rename-properties.js source\n```\n\n##### 6.3.2\n\nFixed edge-case bug in `Collection` where initial `scrollLeft` and `scrollTop` would not correctly adjust inner offsets.\nThanks @edulan for the contribution!\n\n##### 6.3.1\n\nAdded better checks against invalid style properties in `AutoSizer` to protected against the case when it is removed from the DOM immediately after being added.\n\n##### 6.3.0\n\nAdded new `minimumBatchSize` property to `InfiniteLoader` to simplify HTTP request batching.\nFixed edge-case NPE with `AutoSizer` when it is unmounted immediately after being mounted.\n\n##### 6.2.2\n\nFixed off-by-one for `InfiniteLoader` that caused it to request one too many rows when scrolled to the end of the list.\n\n##### 6.2.1\n\n`FlexTable` supports `true`, `false`, `undefined`, and `null` children now to more easily enable support for dynamic columns (see issue #174).\nImproved edge-case handling for changes to cell counts when scroll-to-index properties have been set.\n\n### 6.2.0\n\nAdded new `Collection` component for rendering non-checkboard data.\nThis component's cells can be positioned in any arrangement, even overlapping.\nNote that because it has fewer constraints, `Collection` cannot compute positioning and layout data as fast as `Grid`.\n\n##### 6.1.2\n\nMoved `react-addons-shallow-compare` from `dependencies` to `peerDependencies`.\n\n##### 6.1.1\n\nUpdated React dependency ranges now that 15.0 has been released.\n\n### 6.1.0\n\n`Grid` supports a new `renderCellRanges` property for customizing the rendering of a window of cells.\nThis function should implement the following signature:\n\n```js\nfunction renderCellRanges ({\n  columnMetadata:Array<Object>,\n  columnStartIndex: number,\n  columnStopIndex: number,\n  renderCell: Function,\n  rowMetadata:Array<Object>,\n  rowStartIndex: number,\n  rowStopIndex: number\n}): Array<PropTypes.node>\n```\n\n##### 6.0.8\n\nFixed dependency ranges for `react-addons-shallow-compare` and `react-dom`.\n\n##### 6.0.7\n\nAdded key handling to sortable `FlexTable` headers so that ENTER and SPACE keys can be used to toggle sort direction.\n\n##### 6.0.6\n\nAdded conditional checks to when `aria-label`, `role`, and `tabIndex` get attached to `FlexTable` headers and rows.\nThese a11y properties are only added when on-click or sort handlers are present.\n\n##### 6.0.5\n\nAdded `aria-label` and `role` attributes to `FlexTable`, `Grid`, and `VirtualScroll` components to fix a11y issues reported by [reactjs/react-a11y](https://github.com/reactjs/react-a11y).\nThanks to @globexdesigns for the contributions!\n\n##### 6.0.4\n\nSeparated horiontal and vertical `Grid` metadata calculation to avoid unnecessarily recomputing row metadata for `FlexTable`s and `VirtualScroll`s when a browser's window is resized, for example.\nAlso replaced `columnWidth` and `rowHeight` getter uses in `Grid.render` in favor of cached cell metadata instead.\n\n##### 6.0.3\n\nSmall update to `FlexTable` to move the `rowGetter` call outside of the column loop to reduce the number of times that method gets called.\n\n##### 6.0.2\n\nAdded [transform-react-inline-elements](http://babeljs.io/docs/plugins/transform-react-inline-elements/) to UMD build for minor runtime performance improvements.\nThis change does not effect CommonJS or ES6 module builds because I did not want to remove prop-type checks.\nYou should apply this transformation step as part of your own production build pipeline.\n\n##### 6.0.1\n\nRemoved lingering references to `react-pure-render` with with [`shallowCompare`](https://facebook.github.io/react/docs/shallow-compare.html).\nThis was meant to be part of the initial 6.0 release but was left out accidentally.\n\n# 6.0.0\n\nVersion 6 includes the following changes.\n(For more background information refer to the [Version 6 Roadmap wiki page](https://github.com/bvaughn/react-virtualized/wiki/Version-6-Roadmap).)\nAt a high-level the purpose of this release is to improve customization and flexibility with regard to arrow-key event handling.\n\n### Backwards-incompatible changes\n\n- Refactored `Grid` to remove arrow-key scroll-snapping. Instead this feature is implemented in a HOC, `ArrowKeyStepper`. The upgrade path from React 5.x to 6.x if you want to maintain arrow-key navigation behavior is as follows:\n\n```jsx\n// Before...\n<Grid {...gridProps}/>\n\n// After...\n<ArrowKeyStepper\n  columnsCount={columnsCount}\n  rowsCount={rowsCount}\n>\n  {({ onSectionRendered, scrollToColumn, scrollToRow }) => (\n    <Grid\n      columnsCount={columnsCount}\n      onSectionRendered={onSectionRendered}\n      rowsCount={rowsCount}\n      scrollToColumn={scrollToColumn}\n      scrollToRow={scrollToRow}\n      {...otherGridProps}\n    />\n  )}\n</ArrowKeyStepper>\n```\n\n- The following public methods have also be removed from components:\n  - `FlexTable`: `scrollToRow` (use `scrollToIndex` prop instead), `setScrollTop` (use `scrollTop` prop instead)\n  - `Grid`: `scrollToCell` (use `scrollToColumn` and `scrollToRow` props instead), `setScrollPosition` (use `scrollLeft` and `scrollTop` props instead)\n  - `VirtualScroll`: `scrollToRow` (use `scrollToIndex` prop instead), `setScrollTop` (use `scrollTop` prop instead)\n\n### Backwards-compatible changes\n\n- Replaced (the now unsupported) `react-pure-render` with [`shallowCompare`](https://facebook.github.io/react/docs/shallow-compare.html).\n\n##### 5.5.6\n\nMax scroll position logic in `Grid` now takes scrollbar size into consideration.\nAlso includes a small `render` optimization for null cells.\nThis release made possible by @jquense!\n\n##### 5.5.5\n\nUpdated `package.json` to support React `^0.14.0` as well as `^15.0.0-rc.1`.\nThanks to @opichals for the PR.\n\n##### 5.5.4\n\nChanged key-down event handler in `VirtualScroll`, `FlexTable`, and `Grid` to no longer call `event.preventDefault()` for arrow-key events.\nThis was causing poor user interactions for `<input>` elements within `VirtualScroll` and `FlexTable` components.\nNote that this issue still occurs for `<input>` elements in a `Grid` component.\n\nThis release also removes the `outline: 0` default style for `Grid`.\nAfter consideration I think that's a harmful default behavior.\n\n##### 5.5.3\n\nAdded `will-change` property to `Grid` to work around a Chrome bug(?) that caused the entire grid to be repainted whenever a new row or column was added. This was negatively impacting scrolling performance for Chrome under certain conditions. This change is not expected to impact Firefox, Safari, or IE.\n\nAlso trapped scroll events inside of `AutoSizer` so that `sdecima/javascript-detect-element-resize` did not treat them as potential resizes and unnecessarily force a sync DOM layout.\n\n##### 5.5.2\n\nRemoved two unnecessary method calls in `Grid` and replaced them with cached properties. Should offer a minor performance boost.\nAdded better bounds-checking to util function `getVisibleCellIndices()`\n\n##### 5.5.1\n\nRemoved unnecessary `setImmediate` in `Grid` initialization code.\nThis prevents a possible edge-case runtime error when a `Grid` is mounted and then removed before `setImmediate` is invoked.\n\n### 5.5.0\n\n`ScrollSync` passes additional parameters to child function in order to enable more complex scroll-driven UI changes.\n\n### 5.4.0\n\nAdded optional `headerRenderer` property to `FlexColumn` to enable custom `FlexTable` header cells.\n\n##### 5.3.2\n\nDecoupled x/y axes in `Grid` when determining whether or not to enable overflow.\nThis results in more robustly handling issues like the one reported in PR #133.\nIt also comes with the small cost of partially obscuring a small part of cells (the area used by a scrollbar).\n\n##### 5.3.1\n\nFixed edge-case where always-on scrollbars were not hidden once shown (see issue #116).\n\n### 5.3.0\n\nSeparated CommonJS and UMD builds and pointed package.json's `main` target at the CommonJS build.\nAlso moved the ES6 modules build from `/es` to `/dist/es` to reduce the amount of clutter in the packaged dir.\n\n##### 5.2.4\n\nChanged `Grid` child `key` attributes again to reduce the number of elements created as a result of scrolling.\nThis dramatically improves perforamance without introducing any known regressions.\nThanks to @cesarandreu for consulting on this release.\n\n##### 5.2.3\n\nReverted `transform: translate` positioning to old `top` / `left` positioning to address performance concerns reported via PR #124 and issue #94.\n\n##### 5.2.2\n\nUpdated ES6 module build to be Rollup-friendly by way of `es2015-rollup` Babel preset.\nAlso cleaned up NPM package to suppress unnecessary files (via new `.npmignore`).\n\n##### 5.2.1\n\nFixes long-standing slow wheel scrolling issue that affected certain browsers such as Firefox (see issue #2). Big thanks to James Long (@jlongster), Markus Stange ‏(@mstange), and Dan Abramov (@gaearon) ‏for their help with this fix.\n\n### 5.2.0\n\nAdded optional `onResize` callback property to `AutoSizer`. This method is invoked any time the `AutoSizer` detects a resize. It is passed `width` and `height` named parameters.\n\nAdded optional `minWidth` and `maxWidth` properties to `FlexColumn` to enable greater flexibility with regard to table-column layout.\n\n##### 5.1.1\n\nMarked `FlexColumn` `width` property as required since ommitting this property can lead to uneven column layouts.\n\n### 5.1.0\n\nAdded `ColumnSizer` high-order component for auto-calculating column widths for `Grid` cells.\n\n#### 5.0.1\n\nAdded `webkit-transform` style to `Grid` cells for iOS 8 support.\n\n# 5.0.0\n\nVersion 5 includes the following changes.\n(For more background information refer to the [Version 5 Roadmap wiki page](https://github.com/bvaughn/react-virtualized/wiki/Version-5-Roadmap).)\nAt a high-level the purpose of this release is to make HOCs more easily composible in order to support a wider variety of them in the future.\nA secondary goal was to cut redundant code from `VirtualScroll` and rely more heavily on the base `Grid` component.\n\n###### Backwards-incompatible changes\n\n- Refactored `FlexTable` and `VirtualScroll` to be HOCs that use `Grid` internally. This change makes `width` a required attribute for all virtualized components. A simple upgrade strategy is to use the `AutoSizer` HOC (learn more [here](docs/AutoSizer.md)).\n- Changed globally exported library name (for use with vanilla `<script>` tags) to `window.ReactVirtualized` instead of `window[\"react-virtualized\"]` (see [issue #86](https://github.com/bvaughn/react-virtualized/issues/86)).\n- Removed `horizontalPadding` and `verticalPadding` properties from `FlexTable`. These properties were redundant. Such padding should be the responsibility of the parent container and taken into consideration by the injected `width` and `height`.\n- Refactored `InfiniteLoader` and `AutoSizer` to require function children so as to be more easily composable with each other and new HOCs like `ScrollSync` (learn more [here](docs/usingAutoSizer.md#using-autosizer-with-infiniteloader)).\n- `AutoSizer` no longer supports a `className` property or uses the global 'AutoSizer' class.\n\n###### Backwards-compatible changes\n\n- Added ES6 module and `jsnext:main` target to enable tree-shaking support.\n- Updated `onScroll` property to specific total scrollable area so that offsets can be converted into percentages if desired (learn more [here](docs/README.md)).\n- Replaced `top` / `left` cell positioning with `transform: translate()` for small performance gains. (This may become configurable in the future if any negative impact on performance is noticed.)\n- Created `ScrollSync` HOC for synchronizing scrolling between two or more virtualized components (learn more [here](docs/ScrollSync.md)).\n\n### 4.10.0\n\n`FlexTable` and `VirtualScroll` get a new property, `overscanRowsCount`. `Grid` gets `overscanRowsCount` and `overscanColumnsCount`.\nThese properties can be used to reduce visual flicker around the sides of virtualized components when quickly scrolling.\n`overscanRowsCount` defaults to 10 and `overscanColumnsCount` defaults to 0; adjust as necessary based on the size of your lists and cells.\n\n`FlexTable` sets a default value of 0 for `headerHeight` to more gracefully support `disableHeader` use case.\n\n### 4.9.0\n\n`AutoSizer` component now takes padding into consideration before setting the `width` and `height` of its children.\n\n##### 4.8.1\n\nUpdated `InfiniteLoader` to better reflect required properties. (`isRowLoaded`, `rowsCount`, and `threshold` were not marked as required before.)\n\n### 4.8.0\n\nUpdated `InfiniteLoader` to support being composable within an `AutoSizer` HOC. If either a `width` or `height` attribute are specified on `InfiniteLoader` they will be bundled through to the loader's child component.\n\n##### 4.7.1\n\nFixed `AutoSizer` bug that caused it to prevent parent flex containers from shrinking in some contexts.\n\n### 4.7.0\n\nAdded `scrollToIndex` property to `FlexTable` to be passed through to inner `Grid`.\n\n##### 4.6.6\n\nBetter guard against `NaN` values for `clientWidth` and `offsetWidth` for test environments using `jsdom`.\n\n##### 4.6.5\n\nAdded `react-dom` to the Webpack :externals node to avoid including it in the build.\nThis fixes the bad `4.6.3` and `4.6.4` builds. Sorry!\n\n##### 4.6.4\n\nMoved `react-dom` from `dependencies` to `peerDependencies` to fix bad `4.6.3` build.\n\n##### 4.6.3\n\nFixed edge-case sizing bug with `FlexTable` headers and always-on scrollbars (see issue #80 for more info).\n\n##### 4.6.2\n\nReplaced single occurence of `Number.isNaN` with `isNaN` to avoid IE compatibility issues. Maybe in the future I will add a polyfill dependency but I did not intend to introduce this without a major version bump so I'm removing it.\n\n##### 4.6.1\n\nRemoves `event.stopPropagation` since it was unnecessary to prevent keyboard event bubbling, only to prevent the default browser behavior.\n\n### 4.6.0\n\nRelocated a couple of static style properties from inline style object to exported CSS file for easier customization.\nAdded `Grid__cell` and `VirtualScroll__row` classes.\n\n### 4.5.0\n\nAdded `onScroll` callback to `Grid`, `FlexTable`, and `VirtualScroll`.\nAdded `scrollToCell` method to `Grid` and `scrollToRow` to `FlexTable`, and `VirtualScroll`.\n\n##### 4.4.3\n\nAdded `-ms-flex` and `-webkit-flex` browser prefixes to `FlexTable` cells.\n\n##### 4.4.2\n\nFixed invalid function reference in `Grid` triggered by specifying an initial `scrollToRow` property.\n\n##### 4.4.1\n\nFixed distribution to include new `Grid` component as an export.\n\n### 4.4.0\n\nAdded new `Grid` component for virtualizing rows _and_ columns .\nUpdated `AutoSizer` component to support managing _only_ `width` or `height` (in addition to both).\n\n##### 4.3.1\n\nFixed small CSS property misnaming issue.\n\n### 4.3.0\n\n`FlexTable` now supports dynamic row-heights (in the same way as `VirtualScroll`).\n\n##### 4.2.1\n\nSet `VirtualScroll` default style to `width: 100%` to be more inline with default `FlexTable` behavior.\n\n### 4.2.0\n\nReplaced `React.cloneElement` with wrapper element in order to:\n\n- Better support for pure function components; (they were not compatible with inline style positioning).\n- Relax the requirement of `rowRenderer` having to specify a `key`.\n- Support `React.PropTypes.node` children (including plain strings, numbers, etc.) instead of just elements.\n\n### 4.1.0\n\nAdded `-webkit-overflow-scrolling: touch` for smoother inertial scrolling on mobile devices.\n\n##### 4.0.2\n\nAdditional `columnData` parameter passed to `onHeaderClick` callback.\n\n##### 4.0.1\n\nRemoved an unused dependency on 'inline-style-prefixer' from the `package.json`.\n\n# 4.0.0\n\nCSS styles have been split into their own, separately loaded stylesheet. This simplifies universal/isomorphic use cases without breaking vendor prefixing. This change means that you'll need to import the following additional file. This only needs to be done once (usually during bootstrapping).\n\n```js\nimport 'react-virtualized/styles.css';\n```\n\nIn this release the `width` property of the `FlexTable` component was removed. Tables will now grow to fill 100% of the width of their parent container.\n\nThe `AutoSizer`'s `ChildComponent` attribute has been removed in favor of using a regular react child. For example:\n\n```jsx\n<AutoSizer ChildComponent={VirtualScroll} {...props} />\n```\n\nShould instead be this:\n\n```jsx\n<AutoSizer>\n  <VirtualScroll {...props} />\n</AutoSizer>\n```\n\n##### 3.1.1\n\nNew `onHeaderClick` property added to `FlexTable`. Thanks to @olslash for the contribution!\n\n### 3.1.0\n\nAdded high-order `InfiniteLoader` component to manage just-in-time fetching of data as a user scrolls up or down in a list.\nFor more information about this component refer to the [API docs](https://github.com/bvaughn/react-virtualized/blob/master/docs/InfiniteLoader.md).\n\n##### 3.0.1\n\nFixed small NPE when up/down arrow key was used while an empty VirtualScroll was in-focus.\n\n# 3.0.0\n\nCSS styles have been split into two groups: functional styles (eg. `position`, `overflow`) and presentational styles (eg. `text-transform`, `color`) and both have been converted to inline styles rather than being loaded as CSS. This was done primarily to simplify usage for universal/isomorphic rendering.\n\nFor more information on customizing styles refer to the [documentation](https://github.com/bvaughn/react-virtualized/#customizing-styles)...\n\n### 2.8.0\n\nChanged `Autosizer` component to support a single child instead of the `ChildComponent` property.\n(For backwards compatibility purposes the `ChildComponent` property will continue to be supported.)\n\n##### 2.7.5\n\nDefer loading of element resize code until `componentDidMount` to avoid undefined `document` and `body` references.\nThis was breaking server-side rendering.\n\n##### 2.7.4\n\nUglify dist build to remove dead code.\n\n##### 2.7.2 & 2.7.3\n\nImproved checks for undefined `document` and `window` in hopes of better supporting server-side rendering.\n\n##### 2.7.1\n\nReplaced invalid `rowHeight instanceof Number` check with `typeof rowHeight === 'number'` in `VirtualScroll`.\n\n### 2.7.0\n\nMoved `onRowsRendered` to `componentDidUpdate` (instead of `render`) to keep `render` free of side-effects.\nAdded tests to ensure that the callback is only invoked once per start/stop index pair (and not again unless the indices change).\n\n##### 2.6.2\n\nAdded check for undefined `document` before accessing `attachEvent` to avoid causing problems with server-side rendering.\n\n##### 2.6.1\n\nCell `title` now only set if rendered cell contents are a string. This fixes issue #35.\n\n### 2.6.0\n\n`VirtualScroll` and `FlexTable` now support dynamic row heights by accepting a function as the `rowHeight` property.\n\n### 2.5.0\n\nAdded `AutoSizer` component for wrapping `FlexTable` or `VirtualScroll` and growing to fill the parent container. This should hopefully simplify usage of these components.\n\n### 2.4.0\n\n`FlexTable` and `VirtualScroll` offer new callback property `onRowsRendered` to be invoked with a params object `{ startIndex, stopIndex }` after rows have been rendered.\n\n### 2.3.0\n\n`FlexTable`'s `rowClassName` property can now be either a string or a function in order to support dynamic row classes (eg. alternating colors).\n\n### 2.2.0\n\nAdded `onRowClick` property to `FlexTable`.\n\n##### 2.1.1\n\nFixed a few minor FlexTable font styles to use relative sizes instead of custom ones\n\n### 2.1.0\n\nAdded optional `noRowsRenderer` property to `VirtualScroll` and `FlexTable`.\nThis property can be used to render loading indicators or placeholder content for empty lists.\n\n# 2.0.0\n\nSet `shouldPureComponentUpdate` on component prototypes instead of instances.\nDropped half-ass support for React 0.13. This module has always depended on React 0.14 but it was checking in previous versions and trying to be backwards compatible with 0.13. Since that check is no longer in place, this is a major version bump (even though there is no real new functionality being added).\n\n##### 1.0.4\n\nFixed package.json dependencies by moving `classnames`, `raf`, and `react-pure-render` out of `peerDependencies` and into `dependencies`.\n\n##### 1.0.3\n\nSame as version 1.0.2; published just to update NPM keyword and description.\n\n##### 1.0.2\n\nRemoved default row-border styling from FlexTable and added new :rowClassName property.\n\n##### 1.0.1\n\nUpdated to use ReactDOM.findDOMNode instead of getDOMNode (but added backwards-compatible check for < React v0.14).\n\n# 1.0.0\n\nPackage JSON updated so that \"main\" entry points to `dist/react-virtualized.js` to provide easier integration for users that don't want Babel/Webpack to have to process their `node_modules` folder.\n\n##### 0.0.4\n\nAdded keypress scrolling support.\n\n##### 0.0.3\n\nAdded \"main\" entry to package.json.\n\n##### 0.0.2\n\nAdded CSS auto-prefixing to support Safari and other, older browsers.\n\n##### 0.0.1\n\nInitial release.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n## When Something Happens\n\nIf you see a Code of Conduct violation, follow these steps:\n\n1. Let the person know that what they did is not appropriate and ask them to stop and/or edit their message(s) or commits.\n2. That person should immediately stop the behavior and correct the issue.\n3. If this doesn’t happen, or if you're uncomfortable speaking up, [contact the maintainers](#contacting-maintainers).\n4. As soon as available, a maintainer will look into the issue, and take [further action (see below)](#further-enforcement), starting with a warning, then temporary block, then long-term repo or organization ban.\n\nWhen reporting, please include any relevant details, links, screenshots, context, or other information that may be used to better understand and resolve the situation.\n\n**The maintainer team will prioritize the well-being and comfort of the recipients of the violation over the comfort of the violator.** See [some examples below](#enforcement-examples).\n\n## Our Pledge\n\nWe encourage everyone to participate and are committed to building a community for all. Although we may not be able to satisfy everyone, we all agree that everyone is equal. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.\n\nAlthough this list cannot be exhaustive, we explicitly honor diversity and hope to foster an open and welcoming environment for all, regardless of age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected characteristics above, including participants with disabilities.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n- Using welcoming and inclusive language.\n- Being respectful of differing viewpoints and experiences.\n- Gracefully accepting constructive feedback.\n- Focusing on what is best for the community.\n- Showing empathy and kindness towards other community members.\n- Encouraging and raising up your peers in the project so you can all bask in hacks and glory.\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or advances, including when simulated online. The only exception to sexual topics is channels/spaces specifically for topics of sexual identity.\n- Casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology.\n- Making light of/making mocking comments about trigger warnings and content warnings.\n- Trolling, insulting/derogatory comments, and personal or political attacks.\n- Public or private harassment, deliberate intimidation, or threats.\n- Publishing others' private information, such as a physical or electronic address, without explicit permission. This includes any sort of \"outing\" of any aspect of someone's identity without their consent.\n- Publishing private screenshots or quotes of interactions in the context of this project without all quoted users' _explicit_ consent.\n- Publishing of private communication that doesn't have to do with reporting harrassment.\n- Any of the above even when [presented as \"ironic\" or \"joking\"](https://en.wikipedia.org/wiki/Hipster_racism).\n- Any attempt to present \"reverse-ism\" versions of the above as violations. Examples of reverse-isms are \"reverse racism\", \"reverse sexism\", \"heterophobia\", and \"cisphobia\".\n- Unsolicited explanations under the assumption that someone doesn't already know it. Ask before you teach! Don't assume what people's knowledge gaps are.\n- [Feigning or exaggerating surprise](https://www.recurse.com/manual#no-feigned-surprise) when someone admits to not knowing something.\n- \"[Well-actuallies](https://www.recurse.com/manual#no-well-actuallys)\"\n- Other conduct which could reasonably be considered inappropriate in a professional or community setting.\n\n## Scope\n\nThis Code of Conduct applies both within spaces involving this project and in other spaces involving community members. This includes the repository, its Pull Requests and Issue tracker, its Twitter community, private email communications in the context of the project, and any events where members of the project are participating, as well as adjacent communities and venues affecting the project's members.\n\nDepending on the violation, the maintainers may decide that violations of this code of conduct that have happened outside of the scope of the community may deem an individual unwelcome, and take appropriate action to maintain the comfort and safety of its members.\n\n### Other Community Standards\n\nAs a project on GitHub, this project is additionally covered by the [GitHub Community Guidelines](https://help.github.com/articles/github-community-guidelines/).\n\nAdditionally, as a project hosted on npm, is is covered by [npm, Inc's Code of Conduct](https://www.npmjs.com/policies/conduct).\n\nEnforcement of those guidelines after violations overlapping with the above are the responsibility of the entities, and enforcement may happen in any or all of the services/communities.\n\n## Maintainer Enforcement Process\n\nOnce the maintainers get involved, they will follow a documented series of steps and do their best to preserve the well-being of project members. This section covers actual concrete steps.\n\n### Contacting Maintainers\n\nIf you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via the following methods:\n\n- Through email:\n  - [brian.david.vaughn@gmail.com](mailto:brian.david.vaughn@gmail.com) (Brian Vaughn)\n  - [me@weiweiwu.me](mailto:me@weiweiwu.me) (Wei-Wei Wu)\n\nAll reports will be handled with discretion. In your report please include:\n\n- Your contact information.\n- Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link.\n- Any additional information that may be helpful.\n\n### Further Enforcement\n\nIf you've already followed the [initial enforcement steps](#enforcement), these are the steps maintainers will take for further enforcement, as needed:\n\n1. Repeat the request to stop.\n2. If the person doubles down, they will have offending messages removed or edited by a maintainers given an official warning. The PR or Issue may be locked.\n3. If the behavior continues or is repeated later, the person will be blocked from participating for 24 hours.\n4. If the behavior continues or is repeated after the temporary block, a long-term (6-12mo) ban will be used.\n\nOn top of this, maintainers may remove any offending messages, images, contributions, etc, as they deem necessary.\n\nMaintainers reserve full rights to skip any of these steps, at their discretion, if the violation is considered to be a serious and/or immediate threat to the health and well-being of members of the community. These include any threats, serious physical or verbal attacks, and other such behavior that would be completely unacceptable in any social setting that puts our members at risk.\n\nMembers expelled from events or venues with any sort of paid attendance will not be refunded.\n\n### Who Watches the Watchers?\n\nMaintainers and other leaders who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. These may include anything from removal from the maintainer team to a permanent ban from the community.\n\nAdditionally, as a project hosted on both GitHub and npm, [their own Codes of Conducts may be applied against maintainers of this project](#other-community-standards), externally of this project's procedures.\n\n### Enforcement Examples\n\n#### The Best Case\n\nThe vast majority of situations work out like this. This interaction is common, and generally positive.\n\n> Alex: \"Yeah I used X and it was really crazy!\"\n\n> Patt (not a maintainer): \"Hey, could you not use that word? What about 'ridiculous' instead?\"\n\n> Alex: \"oh sorry, sure.\" -> edits old comment to say \"it was really confusing!\"\n\n#### The Maintainer Case\n\nSometimes, though, you need to get maintainers involved. Maintainers will do their best to resolve conflicts, but people who were harmed by something **will take priority**.\n\n> Patt: \"Honestly, sometimes I just really hate using \\$library and anyone who uses it probably sucks at their job.\"\n\n> Alex: \"Whoa there, could you dial it back a bit? There's a CoC thing about attacking folks' tech use like that.\"\n\n> Patt: \"I'm not attacking anyone, what's your problem?\"\n\n> Alex: \"@maintainers hey uh. Can someone look at this issue? Patt is getting a bit aggro. I tried to nudge them about it, but nope.\"\n\n> KeeperOfCommitBits: (on issue) \"Hey Patt, maintainer here. Could you tone it down? This sort of attack is really not okay in this space.\"\n\n> Patt: \"Leave me alone I haven't said anything bad wtf is wrong with you.\"\n\n> KeeperOfCommitBits: (deletes user's comment), \"@patt I mean it. Please refer to the CoC over at (URL to this CoC) if you have questions, but you can consider this an actual warning. I'd appreciate it if you reworded your messages in this thread, since they made folks there uncomfortable. Let's try and be kind, yeah?\"\n\n> Patt: \"@keeperofbits Okay sorry. I'm just frustrated and I'm kinda burnt out and I guess I got carried away. I'll DM Alex a note apologizing and edit my messages. Sorry for the trouble.\"\n\n> KeeperOfCommitBits: \"@patt Thanks for that. I hear you on the stress. Burnout sucks :/. Have a good one!\"\n\n#### The Nope Case\n\n> PepeTheFrog🐸: \"Hi, I am a literal actual nazi and I think white supremacists are quite fashionable.\"\n\n> Patt: \"NOOOOPE. OH NOPE NOPE.\"\n\n> Alex: \"JFC NO. NOPE. @keeperofbits NOPE NOPE LOOK HERE\"\n\n> KeeperOfCommitBits: \"👀 Nope. NOPE NOPE NOPE. 🔥\"\n\n> PepeTheFrog🐸 has been banned from all organization or user repositories belonging to KeeperOfCommitBits.\n\n## Attribution\n\nThis Code of Conduct was adapted from the [Facebook Open Source Code of Conduct](https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct), as well as the [WeAllJS Code of Conduct Generator](https://npm.im/weallbehave), which is based on the [WeAllJS Code of\nConduct](https://wealljs.org/code-of-conduct), which is itself based on\n[Contributor Covenant](http://contributor-covenant.org), version 1.4, available\nat\n[http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4),\nand the LGBTQ in Technology Slack [Code of\nConduct](http://lgbtq.technology/coc.html).\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "[Bug Reports](#bugs) | [Features Requests](#features) | [Submitting Pull Requests](#pull-requests) | [Running Local Demo](#running-local-demo) | [Running Tests](#running-tests)\n\n# Contributing to this project\n\nPlease take a moment to review this document in order to make the contribution process easy and effective for everyone involved.\n\nFollowing these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project.\nIn return, they should reciprocate that respect in addressing your issue or assessing patches and features.\n\n## Using the issue tracker\n\nThe issue tracker is the preferred channel for bug reports but please respect the following restrictions:\n\n- Please **do not** use the issue tracker for personal support requests (use [Slack](https://react-virtualized.now.sh)).\n- Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others.\n\n<a name=\"bugs\"></a>\n\n## Bug reports\n\nA bug is a _demonstrable problem_ that is caused by the code in the repository.\nGood bug reports are extremely helpful - thank you!\n\nGuidelines for bug reports:\n\n1. **Use the GitHub issue search** &mdash; check if the issue has already been reported.\n2. **Check if the issue has been fixed** &mdash; try to reproduce it using the latest `master` or development branch in the repository.\n3. **Isolate the problem** &mdash; create a [reduced test case](http://css-tricks.com/reduced-test-cases/) and a live example (using a site like [Plunker](http://plnkr.co/)).\n\nA good bug report shouldn't leave others needing to chase you up for more information.\nPlease try to be as detailed as possible in your report.\nWhich versions of react-virtualized and react are you using?\nWhat steps will reproduce the issue? What browser(s) and OS experience the problem?\nWhat would you expect to be the outcome?\nAll these details will help people to fix any potential bugs.\n\nExample:\n\n> Short and descriptive example bug report title\n>\n> A summary of the issue and the browser/OS environment in which it occurs.\n> If suitable, include the steps required to reproduce the bug.\n>\n> 1. This is the first step\n> 2. This is the second step\n> 3. Further steps, etc.\n>\n> `<url>` - a link to the reduced test case\n>\n> Any other information you want to share that is relevant to the issue being reported.\n> This might include the lines of code that you have identified as causing the bug,\n> and potential solutions (and your opinions on their merits).\n\n<a name=\"features\"></a>\n\n## Feature requests\n\nFeature requests are welcome.\nBut take a moment to find out whether your idea fits with the scope and aims of the project.\nIt's up to _you_ to make a strong case to convince the project's developers of the merits of this feature.\nPlease provide as much detail and context as possible.\n\n<a name=\"pull-requests\"></a>\n\n## Pull requests\n\nGood pull requests - patches, improvements, new features - are a fantastic help.\nThey should remain focused in scope and avoid containing unrelated commits.\n\n**Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code, porting to a different language),\notherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project.\n\nPlease adhere to the coding conventions used throughout a project (indentation, accurate comments, etc.) and any other requirements (such as test coverage).\n\nFollow this process if you'd like your work considered for inclusion in the project:\n\n1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes:\n\n   ```bash\n   # Clone your fork of the repo into the current directory\n   git clone https://github.com/<your-username>/react-virtualized\n   # Navigate to the newly cloned directory\n   cd react-virtualized\n   # Assign the original repo to a remote called \"upstream\"\n   git remote add upstream https://github.com/bvaughn/react-virtualized\n   ```\n\n2. If you cloned a while ago, get the latest changes from upstream:\n\n   ```bash\n   git checkout master\n   git pull upstream master\n   ```\n\n3. Install/update dependencies:\n\n   ```bash\n   yarn install\n   ```\n\n4. Create a new topic branch (off the main project development branch) to\n   contain your feature, change, or fix:\n\n   ```bash\n   git checkout -b <topic-branch-name>\n   ```\n\n5. Commit your changes in logical chunks.\n   Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)\n   or your code is unlikely be merged into the main project.\n   Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase)\n   feature to tidy up your commits before making them public.\n\n6. Locally merge (or rebase) the upstream development branch into your topic branch:\n\n   ```bash\n   git pull [--rebase] upstream master\n   ```\n\n7. Push your topic branch up to your fork:\n\n   ```bash\n   git push origin <topic-branch-name>\n   ```\n\n8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)\n   with a clear title and description.\n\n**IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by this project (MIT).\n\n<a name=\"running-local-demo\"></a>\n\n## Running Local Demo\n\nYou can run the local demo with NPM like so:\n\n```bash\ncd <root>\nyarn start\n```\n\nThe local app will then be available at http://localhost:3001\n\n<a name=\"running-tests\"></a>\n\n## Running Tests\n\nAll unit tests must pass before a pull request will be approved.\nYou can run unit tests with NPM like so:\n\n```bash\ncd <root>\nyarn test\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Brian Vaughn\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "[<img src=\"https://cloud.githubusercontent.com/assets/29597/11737732/0ca1e55e-9f91-11e5-97f3-098f2f8ed866.png\" alt=\"React virtualized\" data-canonical-src=\"https://cloud.githubusercontent.com/assets/29597/11737732/0ca1e55e-9f91-11e5-97f3-098f2f8ed866.png\" width=\"330\" height=\"100\" />](http://bvaughn.github.io/react-virtualized/)\n\nReact components for efficiently rendering large lists and tabular data.\nCheck out [the demo](https://bvaughn.github.io/react-virtualized/) for some examples.\n\n### If you like this project, 🎉 [become a sponsor](https://github.com/sponsors/bvaughn/) or ☕ [buy me a coffee](http://givebrian.coffee/)\n\n### Sponsors\n\nThe following wonderful companies have sponsored react-virtualized:\n\n<a href=\"https://www.treasuredata.com/\"><img width=\"64\" height=\"64\" title=\"Treasure Data\" src=\"https://cloud.githubusercontent.com/assets/29597/17391516/962647f8-59cb-11e6-83be-aa1bac299dd0.png\"></a>\n<a href=\"https://developer.hpe.com/\"><img width=\"64\" height=\"64\" title=\"HPE Dev\" src=\"https://user-images.githubusercontent.com/5983843/37311298-1c3a711a-261d-11e8-9129-ef1589d7063f.png\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/9/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/10/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/10/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/11/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/11/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/12/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/12/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/13/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/13/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/14/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/14/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/15/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/15/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/16/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/16/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/17/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/17/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/18/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/18/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/19/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/19/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/20/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/20/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/21/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/21/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/22/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/22/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/23/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/23/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/24/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/24/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/25/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/25/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/26/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/26/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/27/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/27/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/28/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/28/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/sponsor/29/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/sponsor/29/avatar.svg\"></a>\n\n[Learn more about becoming a sponsor!](https://opencollective.com/react-virtualized#sponsor)\n\n<a href=\"https://opencollective.com/react-virtualized/backer/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/9/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/10/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/10/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/11/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/11/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/12/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/12/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/13/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/13/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/14/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/14/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/15/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/15/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/16/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/16/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/17/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/17/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/18/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/18/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/19/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/19/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/20/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/20/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/21/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/21/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/22/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/22/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/23/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/23/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/24/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/24/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/25/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/25/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/26/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/26/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/27/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/27/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/28/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/28/avatar.svg\"></a>\n<a href=\"https://opencollective.com/react-virtualized/backer/29/website\" target=\"_blank\"><img src=\"https://opencollective.com/react-virtualized/backer/29/avatar.svg\"></a>\n\n## A word about `react-window`\n\nIf you're considering adding `react-virtualized` to a project, take a look at [`react-window`](https://github.com/bvaughn/react-window) as a possible lighter-weight alternative. [Learn more about how the two libraries compare here.](https://github.com/bvaughn/react-window#how-is-react-window-different-from-react-virtualized)\n\n## Getting started\n\nInstall `react-virtualized` using npm.\n\n```shell\nnpm install react-virtualized --save\n```\n\nES6, CommonJS, and UMD builds are available with each distribution.\nFor example:\n\n```js\n// Most of react-virtualized's styles are functional (eg position, size).\n// Functional styles are applied directly to DOM elements.\n// The Table component ships with a few presentational styles as well.\n// They are optional, but if you want them you will need to also import the CSS file.\n// This only needs to be done once; probably during your application's bootstrapping process.\nimport 'react-virtualized/styles.css';\n\n// You can import any component you want as a named export from 'react-virtualized', eg\nimport {Column, Table} from 'react-virtualized';\n\n// But if you only use a few react-virtualized components,\n// And you're concerned about increasing your application's bundle size,\n// You can directly import only the components you need, like so:\nimport AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';\nimport List from 'react-virtualized/dist/commonjs/List';\n```\n\nNote webpack 4 makes this optimization itself, see the [documentation](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free).\n\nIf the above syntax looks too cumbersome, or you import react-virtualized components from a lot of places, you can also configure a Webpack alias. For example:\n\n```js\n// Partial webpack.config.js\n{\n  alias: {\n    'react-virtualized/List': 'react-virtualized/dist/es/List',\n  },\n  ...rest\n}\n```\n\nThen you can just import like so:\n\n```js\nimport List from 'react-virtualized/List';\n\n// Now you can use <List {...props} />\n```\n\nYou can also use a global-friendly UMD build:\n\n```html\n<link rel=\"stylesheet\" href=\"path-to-react-virtualized/styles.css\" />\n<script src=\"path-to-react-virtualized/dist/umd/react-virtualized.js\"></script>\n```\n\nNow you're ready to start using the components.\nYou can learn more about which components react-virtualized has to offer [below](#documentation).\n\n## Dependencies\n\nReact Virtualized has very few dependencies and most are managed by NPM automatically.\nHowever the following peer dependencies must be specified by your project in order to avoid version conflicts:\n[`react`](https://www.npmjs.com/package/react),\n[`react-dom`](https://www.npmjs.com/package/react-dom).\nNPM will not automatically install these for you but it will show you a warning message with instructions on how to install them.\n\n## Pure Components\n\nBy default all react-virtualized components use [`shallowCompare`](https://facebook.github.io/react/docs/shallow-compare.html) to avoid re-rendering unless props or state has changed.\nThis occasionally confuses users when a collection's data changes (eg `['a','b','c']` => `['d','e','f']`) but props do not (eg `array.length`).\n\nThe solution to this is to let react-virtualized know that something external has changed.\nThis can be done a couple of different ways.\n\n###### Pass-thru props\n\nThe `shallowCompare` method will detect changes to any props, even if they aren't declared as `propTypes`.\nThis means you can also pass through additional properties that affect cell rendering to ensure changes are detected.\nFor example, if you're using `List` to render a list of items that may be re-sorted after initial render- react-virtualized would not normally detect the sort operation because none of the properties it deals with change.\nHowever you can pass through the additional sort property to trigger a re-render.\nFor example:\n\n```js\n<List {...listProps} sortBy={sortBy} />\n```\n\n###### Public methods\n\n`Grid` and `Collection` components can be forcefully re-rendered using [`forceUpdate`](https://facebook.github.io/react/docs/component-api.html#forceupdate).\nFor `Table` and `List`, you'll need to call [`forceUpdateGrid`](https://github.com/bvaughn/react-virtualized/blob/master/docs/Table.md#forceupdategrid) to ensure that the inner `Grid` is also updated. For `MultiGrid`, you'll need to call [`forceUpdateGrids`](https://github.com/bvaughn/react-virtualized/blob/master/docs/MultiGrid.md#forceupdategrids) to ensure that the inner `Grid`s are updated.\n\n## Documentation\n\nAPI documentation available [here](docs/README.md).\n\nThere are also a couple of how-to guides:\n\n- [Customizing classes and styles](docs/customizingStyles.md)\n- [Displaying items in reverse order](docs/reverseList.md)\n- [Using AutoSizer](docs/usingAutoSizer.md)\n- [Creating an infinite-loading list](docs/creatingAnInfiniteLoadingList.md)\n- [Natural sort Table](docs/tableWithNaturalSort.md)\n- [Sorting a Table by multiple columns](docs/multiColumnSortTable.md)\n\n## Examples\n\nExamples for each component can be seen in [the documentation](docs/README.md).\n\nHere are some online demos of each component:\n\n- [ArrowKeyStepper](https://bvaughn.github.io/react-virtualized/#/components/ArrowKeyStepper)\n- [AutoSizer](https://bvaughn.github.io/react-virtualized/#/components/AutoSizer)\n- [CellMeasurer](https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer)\n- [Collection](https://bvaughn.github.io/react-virtualized/#/components/Collection)\n- [ColumnSizer](https://bvaughn.github.io/react-virtualized/#/components/ColumnSizer)\n- [Grid](https://bvaughn.github.io/react-virtualized/#/components/Grid)\n- [InfiniteLoader](https://bvaughn.github.io/react-virtualized/#/components/InfiniteLoader)\n- [List](https://bvaughn.github.io/react-virtualized/#/components/List)\n- [Masonry](https://bvaughn.github.io/react-virtualized/#/components/Masonry)\n- [MultiGrid](https://bvaughn.github.io/react-virtualized/#/components/MultiGrid)\n- [ScrollSync](https://bvaughn.github.io/react-virtualized/#/components/ScrollSync)\n- [Table](https://bvaughn.github.io/react-virtualized/#/components/Table)\n- [WindowScroller](https://bvaughn.github.io/react-virtualized/#/components/WindowScroller)\n\nAnd here are some \"recipe\" type demos:\n\n- [Table with resizable (drag and drop) columns](https://codesandbox.io/s/j30k46l7xw)\n- [Collapsable tree view](https://rawgit.com/bvaughn/react-virtualized/master/playground/tree.html)\n- [Full-page grid (spreadsheet)](https://rawgit.com/bvaughn/react-virtualized/master/playground/grid.html)\n- [Dynamic cell measuring](https://rawgit.com/bvaughn/react-virtualized/master/playground/chat.html)\n- [Cell hover effects](https://rawgit.com/bvaughn/react-virtualized/master/playground/hover.html)\n\n## Supported Browsers\n\nreact-virtualized aims to support all evergreen browsers and recent mobile browsers for iOS and Android. IE 9+ is also supported (although IE 9 will require some user-defined, custom CSS since flexbox layout is not supported).\n\nIf you find a browser-specific problem, please report it along with a repro case. The easiest way to do this is probably by forking [this Plunker](https://plnkr.co/edit/6syKo8cx3RfoO96hXFT1).\n\n## Friends\n\nHere are some great components built on top of react-virtualized:\n\n- [react-infinite-calendar](https://github.com/clauderic/react-infinite-calendar): Infinite scrolling date-picker with localization, themes, keyboard support, and more\n- [react-sortable-hoc](https://github.com/clauderic/react-sortable-hoc): Higher-order components to turn any list into an animated, touch-friendly, sortable list\n- [react-sortable-tree](https://github.com/fritz-c/react-sortable-tree): Drag-and-drop sortable representation of hierarchical data\n- [react-virtualized-checkbox](https://github.com/emilebres/react-virtualized-checkbox): Checkbox group component with virtualization for large number of options\n- [react-virtualized-select](https://github.com/bvaughn/react-virtualized-select): Drop-down menu for React with windowing to support large numbers of options.\n- [react-virtualized-tree](https://github.com/diogofcunha/react-virtualized-tree/): A reactive tree component that aims to render large sets of tree structured data in an elegant and performant way\n- [react-timeline-9000](https://github.com/BHP-DevHub/react-timeline-9000/): A calendar timeline component that is capable of displaying and interacting with a large number of items\n\n## Contributions\n\nUse [GitHub issues](https://github.com/bvaughn/react-virtualized/issues) for requests.\n\nI actively welcome pull requests; learn how to [contribute](https://github.com/bvaughn/react-virtualized/blob/master/CONTRIBUTING.md).\n\n## Changelog\n\nChanges are tracked in the [changelog](https://github.com/bvaughn/react-virtualized/blob/master/CHANGELOG.md).\n\n## License\n\n_react-virtualized_ is available under the MIT License.\n"
  },
  {
    "path": "codemods/6-to-7/rename-properties.js",
    "content": "'use strict'\n\n// Renames react-virtualized version 6.x properties to be version-7 compatible\nmodule.exports = function transformer (file, api) {\n  const jscodeshift = api.jscodeshift\n\n  let source = file.source\n\n  // Rename variable references\n  for (var property in propertyRenameMap) {\n    source = jscodeshift(source)\n      .findVariableDeclarators(property)\n      .renameTo(propertyRenameMap[property])\n      .toSource()\n  }\n\n  // Rename JSX attributes\n  source = jscodeshift(source)\n    .find(jscodeshift.JSXAttribute)\n    .filter(shouldAttributeBeRenamed)\n    .replaceWith(renameReactVirtualizedAttribute)\n    .toSource()\n\n  return source\n}\n\nconst reactVirtualizedElementNames = [\n  'ArrowKeyStepper',\n  'AutoSizer',\n  'Collection',\n  'ColumnSizer',\n  'FlexTable',\n  'Grid',\n  'ScrollSync',\n  'VirtualScroll'\n]\n\n// @param path jscodeshift.JSXAttribute\nconst attributeBelongsToReactVirtualizedElement = path => reactVirtualizedElementNames.includes(path.parent.value.name.name)\n\n// See https://github.com/bvaughn/react-virtualized/wiki/Version-7-Roadmap#clean-up-property-names\nconst propertyRenameMap = {\n  cellClassName: 'className',\n  columnsCount: 'columnCount',\n  overscanColumnsCount: 'overscanColumnCount',\n  overscanRowsCount: 'overscanRowCount',\n  renderCell: 'cellRenderer',\n  renderCellRanges: 'cellRangeRenderer',\n  rowsCount: 'rowCount'\n}\n\n// @param path jscodeshift.JSXAttribute\nconst shouldAttributeBeRenamed = path => attributeBelongsToReactVirtualizedElement(path) && isAttributeInPropertyRenameMap(path)\n\n// @param path jscodeshift.JSXAttribute\nconst isAttributeInPropertyRenameMap = path => propertyRenameMap.hasOwnProperty(path.value.name.name)\n\n// @param path jscodeshift.JSXAttribute\nconst renameReactVirtualizedAttribute = path => {\n  path.value.name.name = propertyRenameMap[path.value.name.name] || path.value.name.name\n\n  return path.node\n}\n"
  },
  {
    "path": "codemods/7-to-8/rename-components.js",
    "content": "const renameMap = {\n  FlexColumn: 'Column',\n  FlexTable: 'Table',\n  VirtualScroll: 'List'\n}\n\nmodule.exports = function transformer (file, api) {\n  const j = api.jscodeshift\n\n  return j(file.source)\n    .find(j.Identifier)\n    .filter(\n      identifier => !!renameMap[identifier.value.name]\n    )\n    .replaceWith(\n      identifier => j.identifier(renameMap[identifier.node.name])\n    )\n    .toSource()\n}\n"
  },
  {
    "path": "docs/ArrowKeyStepper.md",
    "content": "## ArrowKeyStepper\n\nHigh-order component that decorates another virtualized component and responds to arrow-key events by scrolling one row or column at a time.\nThis provides a snap-to behavior rather than the default browser scrolling behavior.\n\nNote that unlike the other HOCs in react-virtualized, the `ArrowKeyStepper` adds a `<div>` element around its children in order to attach a key-down event handler.\nThe appearance of this wrapper element can be customized using the `className` property.\n\n### Prop Types\n\n| Property         | Type               | Required? | Description                                                                                                                                                                                                        |\n| :--------------- | :----------------- | :-------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| children         | Function           |     ✓     | Function responsible for rendering children. This function should implement the following signature: `({ onSectionRendered: Function, scrollToColumn: number, scrollToRow: number }) => PropTypes.element`         |\n| className        | String             |           | CSS class name to attach to the wrapper `<div>`.                                                                                                                                                                   |\n| columnCount      | Number             |     ✓     | Number of columns in grid; for `Table` and `List` this property should always be `1`.                                                                                                                              |\n| disabled         | Boolean            |           | Disables all scrolling using arrow-keys; defaults to `false`                                                                                                                                                       |\n| isControlled     | Boolean            |           | This component is \"controlled\"; it will not update `scrollToColumn` or `scrollToRow`. This property should be used with `onScrollToChange`.                                                                        |\n| mode             | \"edges\" or \"cells\" |           | Controls behavior of stepper when arrow key direction changes. \"cells\" means that the index will only increment or decrement by 1; \"edges\" (default) means that the opposite side of the grid will be incremented. |\n| onScrollToChange | Function           |           | Called when arrow key navigation should update the current scroll-to values.                                                                                                                                       |\n| rowCount         | Number             |     ✓     | Number of rows in grid.                                                                                                                                                                                            |\n| scrollToColumn   | Number             |           | Optional default/initial `scrollToColumn` value                                                                                                                                                                    |\n| scrollToRow      | Number             |           | Optional default/initial `scrollToRow` value                                                                                                                                                                       |\n\n### Public Methods\n\n##### setScrollIndexes ({ scrollToColumn: number, scrollToRow: number })\n\nOverride the local state of the component with new values for `scrollToRow` and `scrollToColumn`.\n\n### Children function\n\nThe child function is passed the following named parameters:\n\n| Parameter         | Type     | Description                                                                                                                  |\n| :---------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------- |\n| onSectionRendered | Function | Pass-through callback to be attached to child component; informs the key-stepper which range of cells are currently visible. |\n| scrollToColumn    | Number   | Specifies which column in the child component should be visible                                                              |\n| scrollToRow       | Number   | Specifies which row in the child component should be visible                                                                 |\n\n### Examples\n\nYou can decorate any virtualized component (eg. `Table`, `Grid`, or `List`) with arrow-key snapping like so:\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {ArrowKeyStepper, Grid} from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\nReactDOM.render(\n  <ArrowKeyStepper columnCount={columnCount} rowCount={rowCount}>\n    {({onSectionRendered, scrollToColumn, scrollToRow}) => (\n      <Grid\n        columnCount={columnCount}\n        onSectionRendered={onSectionRendered}\n        rowCount={rowCount}\n        scrollToColumn={scrollToColumn}\n        scrollToRow={scrollToRow}\n        {...otherGridProps}\n      />\n    )}\n  </ArrowKeyStepper>,\n  document.getElementById('example'),\n);\n```\n"
  },
  {
    "path": "docs/AutoSizer.md",
    "content": "## AutoSizer\n\nHigh-order component that automatically adjusts the width and height of a single child.\n\n### Prop Types\n\n| Property      | Type     | Required? | Description                                                                                                                                                     |\n| :------------ | :------- | :-------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| children      | Function |     ✓     | Function responsible for rendering children. This function should implement the following signature: `({ height: number, width: number }) => PropTypes.element` |\n| className     | String   |           | Optional custom CSS class name to attach to root `AutoSizer` element. This is an advanced property and is not typically necessary.                              |\n| defaultHeight | Number   |           | Height passed to child for initial render; useful for server-side rendering. This value will be overridden with an accurate height after mounting.              |\n| defaultWidth  | Number   |           | Width passed to child for initial render; useful for server-side rendering. This value will be overridden with an accurate width after mounting.                |\n| disableHeight | Boolean  |           | Fixed `height`; if specified, the child's `height` property will not be managed                                                                                 |\n| disableWidth  | Boolean  |           | Fixed `width`; if specified, the child's `width` property will not be managed                                                                                   |\n| nonce         | String   |           | Nonce of the inlined stylesheets for [Content Security Policy](https://www.w3.org/TR/2016/REC-CSP2-20161215/#script-src-the-nonce-attribute)                    |\n| onResize      | Function |           | Callback to be invoked on-resize; it is passed the following named parameters: `({ height: number, width: number })`.                                           |\n| style         | Object   |           | Optional custom inline style to attach to root `AutoSizer` element. This is an advanced property and is not typically necessary.                                |\n\n### Examples\n\nMany react-virtualized components require explicit dimensions but sometimes you just want a component to just grow to fill all of the available space.\nThe `AutoSizer` component can be useful in this case.\n\nOne word of caution about using `AutoSizer` with flexbox containers.\nFlex containers don't prevent their children from growing and `AutoSizer` greedily grows to fill as much space as possible.\nCombining the two can cause a loop.\nThe simple way to fix this is to nest `AutoSizer` inside of a `block` element (like a `<div>`) rather than putting it as a direct child of the flex container.\nRead more about common `AutoSizer` questions [here](usingAutoSizer.md).\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {AutoSizer, List} from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\n// List data as an array of strings\nconst list = [\n  'Brian Vaughn',\n  // And so on...\n];\n\nfunction rowRenderer({key, index, style}) {\n  return (\n    <div key={key} style={style}>\n      {list[index]}\n    </div>\n  );\n}\n\n// Render your list\nReactDOM.render(\n  <AutoSizer>\n    {({height, width}) => (\n      <List\n        height={height}\n        rowCount={list.length}\n        rowHeight={20}\n        rowRenderer={rowRenderer}\n        width={width}\n      />\n    )}\n  </AutoSizer>,\n  document.getElementById('example'),\n);\n```\n"
  },
  {
    "path": "docs/CellMeasurer.md",
    "content": "## CellMeasurer\n\nHigh-order component that automatically measures a cell's contents by temporarily rendering it in a way that is not visible to the user.\nSpecify a fixed width to measure dynamic height (or vice versa).\nThis is an advanced component and has some limitations and performance considerations.\n[See below for more information](#limitations-and-performance-considerations).\n\n`CellMeasurer` can be used with `Grid`, `List`, and `Table` components. It is not intended to be used with the `Collection` component.\n\n### Prop Types\n\n| Property    | Type                | Required? | Description                                                                                                                                                                                                  |\n| :---------- | :------------------ | :-------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| cache       | `CellMeasurerCache` |     ✓     | Cache to be shared between `CellMeasurer` and its parent `Grid`. Learn more [here](#cellmeasurercache).                                                                                                      |\n| children    | Element or Function |     ✓     | Either a React element as a child (eg `<div />`) or a function (eg. `({ measure, registerChild }) => <div ref={registerChild} />`). See [below](#using-cellmeasurer-with-images) for more detailed examples. |\n| columnIndex | number              |     ✓     | Index of column being measured (within the parent `Grid`) or 0 (if used within a `List` or `Table`).                                                                                                         |\n| parent      | `Grid`              |     ✓     | Reference to the parent `Grid`; this value is passed by `Grid` to the `cellRenderer` and should be passed along as-is.                                                                                       |\n| rowIndex    | number              |     ✓     | Index of row being measured (within the parent `Grid`).                                                                                                                                                      |\n\n### Render Props\n\n| Property      | Type     | Description                                                                                                                    |\n| :------------ | :------- | :----------------------------------------------------------------------------------------------------------------------------- |\n| measure       | Function | Perform the cell measurements.                                                                                                 |\n| registerChild | Function | Specify DOM element to be measured, can be used as a `ref` (by default `WindowScroller` uses `ReactDOM.findDOMNode` function). |\n\n### CellMeasurerCache\n\nThe `CellMeasurerCache` stores `CellMeasurer` measurements and shares them with a parent `Grid`.\nIt should be configured based on the type of measurements you need. It accepts the following parameters:\n\n### Prop Types\n\n| Property      | Type      | Required? | Description                                                                                                                                                                                                                |\n| :------------ | :-------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| defaultHeight | number    |           | Unmeasured cells will initially report this height                                                                                                                                                                         |\n| defaultWidth  | number    |           | Unmeasured cells will initially report this width                                                                                                                                                                          |\n| fixedHeight   | boolean   |           | Rendered cells will have a fixed height, dynamic width                                                                                                                                                                     |\n| fixedWidth    | boolean   |           | Rendered cells will have a fixed width, dynamic height                                                                                                                                                                     |\n| minHeight     | number    |           | Derived row height (of multiple cells) should not be less than this value                                                                                                                                                  |\n| minWidth      | number    |           | Derived column width (of multiple cells) should not be less than this value                                                                                                                                                |\n| keyMapper     | KeyMapper |           | Enables more intelligent mapping of a given column and row index to an item ID. This prevents a cell cache from being invalidated when its parent collection is modified. `(rowIndex: number, columnIndex: number) => any` |\n\nNote that while all of the individual parameters above are optional, you must supply at least some of them.\n`CellMeasurerCache` is not meant to measure cells that are both dynamic width _and_ height.\nIt would be inefficient to do so since the size of a row (or column) is equal to the largest cell within that row.\nSee [below](#limitations-and-performance-considerations) for more information.\n\n### `CellMeasurerCache` Public Methods\n\n##### clear (rowIndex: number, columnIndex: number)\n\nReset cached measurements for a specific cell.\n\nThis should be called when a cell needs to be re-measured to handle dynamic content (eg. replacing a loading indicator with loaded content or reacting to state-changes for stateful cells).\n\n##### clearAll ()\n\nReset cached measurements for all cells.\n\nThis method should be called when a `Grid`, `List` or `Table` needs to reflow content due to a resizing event for a responsive layout (eg. a window width resize may have an impact on the height of cells).\n\n### Examples\n\n###### Grid\n\nThis example shows a `Grid` with fixed row heights and dynamic column widths.\nFor more examples check out the component [demo page](https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer).\n\n```jsx\nimport React from 'react';\nimport { CellMeasurer, CellMeasurerCache, Grid } from 'react-virtualized';\n\n// In this example, average cell width is assumed to be about 100px.\n// This value will be used for the initial `Grid` layout.\n// Cell measurements smaller than 75px should also be rounded up.\n// Height is not dynamic.\nconst cache = new CellMeasurerCache({\n  defaultWidth: 100,\n  minWidth: 75,\n  fixedHeight: true\n});\n\nfunction cellRenderer ({ columnIndex, key, parent, rowIndex, style }) {\n  const content // Derive this from your data somehow\n\n  return (\n    <CellMeasurer\n      cache={cache}\n      columnIndex={columnIndex}\n      key={key}\n      parent={parent}\n      rowIndex={rowIndex}\n    >\n      <div\n        style={{\n          ...style,\n          height: 35,\n          whiteSpace: 'nowrap'\n        }}\n      >\n        {content}\n      </div>\n    </CellMeasurer>\n  );\n}\n\nfunction renderGrid (props) {\n  return (\n    <Grid\n      {...props}\n      columnWidth={cache.columnWidth}\n      deferredMeasurementCache={cache}\n      cellRenderer={cellRenderer}\n    />\n  );\n}\n```\n\n##### Using `registerChild`\n\nBy default, `CellMeasurer` uses `findDOMNode` to access the DOM element to measure.\nThis API is [deprecated in React `StrictMode`](https://reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage), so you may want to avoid its usage.\nAs an alternative, you can use `registerChild` render prop to specify the element, e.g. by passing as a `ref`.\n\n```jsx\nimport React from 'react';\nimport { CellMeasurer, CellMeasurerCache, Grid } from 'react-virtualized';\n\n// In this example, average cell width is assumed to be about 100px.\n// This value will be used for the initial `Grid` layout.\n// Cell measurements smaller than 75px should also be rounded up.\n// Height is not dynamic.\nconst cache = new CellMeasurerCache({\n  defaultWidth: 100,\n  minWidth: 75,\n  fixedHeight: true\n});\n\nfunction cellRenderer ({ columnIndex, key, parent, rowIndex, style }) {\n  const content // Derive this from your data somehow\n\n  return (\n    <CellMeasurer\n      cache={cache}\n      columnIndex={columnIndex}\n      key={key}\n      parent={parent}\n      rowIndex={rowIndex}\n    >\n      {({registerChild}) => (\n        <div\n          style={{\n            ...style,\n            height: 35,\n            whiteSpace: 'nowrap'\n          }}\n        >\n          {content}\n        </div>\n      )}\n    </CellMeasurer>\n  );\n}\n\nfunction renderGrid (props) {\n  return (\n    <Grid\n      {...props}\n      columnWidth={cache.columnWidth}\n      deferredMeasurementCache={cache}\n      cellRenderer={cellRenderer}\n    />\n  );\n}\n```\n\n###### Using `CellMeasurer` with images\n\nThis example shows how you might use the `CellMeasurer` component along with the `List` component in order to display dynamic-height rows.\nThe difference between this example and the above example is that the height of the row is not determined until image data has loaded.\nTo support this, a function-child is passed to `CellMeasurer` which then receives `measure` and `registerChild` parameters.\n`measure` should be called when cell content is ready to be measured (in this case, when the image has loaded).\n\n```jsx\nimport React from 'react';\nimport { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';\n\n// In this example, average cell height is assumed to be about 50px.\n// This value will be used for the initial `Grid` layout.\n// Width is not dynamic.\nconst cache = new CellMeasurerCache({\n  defaultHeight: 50,\n  fixedWidth: true\n});\n\nfunction rowRenderer ({ index, isScrolling, key, parent, style }) {\n  const source // This comes from your list data\n\n  return (\n    <CellMeasurer\n      cache={cache}\n      columnIndex={0}\n      key={key}\n      parent={parent}\n      rowIndex={index}\n    >\n      {({ measure, registerChild }) => (\n        // 'style' attribute required to position cell (within parent List)\n        <div ref={registerChild} style={style}>\n          <img\n            onLoad={measure}\n            src={source}\n          />\n        </div>\n      )}\n    </CellMeasurer>\n  );\n}\n\nfunction renderList (props) {\n  return (\n    <List\n      {...props}\n      deferredMeasurementCache={cache}\n      rowHeight={cache.rowHeight}\n      rowRenderer={rowRenderer}\n    />\n  );\n}\n```\n\n###### Using `CellMeasurer` with equal dynamic cell heights or widths\n\n```jsx\n// Normally, every cell gets measured individually and is very slow.\n// However, with the keyMapper prop we can specify a constant return value and\n// tell CellMeasurer that all measurements after the first one will hit the\n// cache and we get a speedy solution.\nconst cache = new CellMeasurerCache({\n  defaultHeight: 30,\n  fixedWidth: true,\n  keyMapper: () => 1,\n});\n```\n\n### Limitations and Performance Considerations\n\n###### Performance\n\nMeasuring a column's width requires measuring all rows in order to determine the widest occurrence of that column.\nThe same is true in reverse for measuring a row's height.\nFor this reason it may not be a good idea to use this HOC for `Grid`s containing a large number of both columns _and_ cells.\n\nSince this component measures one cell at a time to determine its width/height, it will likely be slow if a user skips many rows (or columns) at once by scrolling with a scrollbar or via a scroll-to-cell prop.\nThere is (unfortunately) no workaround for this performance limitation at the moment.\n"
  },
  {
    "path": "docs/Collection.md",
    "content": "## Collection\n\nRenders scattered or non-linear data.\nUnlike `Grid`, which renders checkerboard data, `Collection` can render arbitrarily positioned- even overlapping- data.\n\n**Note** that this component's measuring and layout phase is more expensive than `Grid` since it can not assume a correlation between a cell's index and position. For this reason it will take significantly longer to initialize than the more linear/checkerboard components.\n\n### Prop Types\n\n| Property                  | Type     | Required? | Description                                                                                                                                                                                                                                                                                                                                  |\n| :------------------------ | :------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| autoHeight                | Boolean  |           | Outer `height` of `Collection` is set to \"auto\". This property should only be used in conjunction with the `WindowScroller` HOC.                                                                                                                                                                                                             |\n| className                 | String   |           | Optional custom CSS class name to attach to root `Collection` element.                                                                                                                                                                                                                                                                       |\n| cellCount                 | Number   |     ✓     | Number of cells in collection.                                                                                                                                                                                                                                                                                                               |\n| cellGroupRenderer         | Function |           | Responsible for rendering a group of cells given their indices.: `({ cellSizeAndPositionGetter:Function, indices: Array<number>, cellRenderer: Function }): Array<PropTypes.node>`                                                                                                                                                           |\n| cellRenderer              | Function |     ✓     | Responsible for rendering a cell given an row and column index: `({ index: number, isScrolling: boolean, key: string, style: object }): PropTypes.element`                                                                                                                                                                                   |\n| cellSizeAndPositionGetter | Function |     ✓     | Callback responsible for returning size and offset/position information for a given cell (index): `({ index: number }): { height: number, width: number, x: number, y: number }`                                                                                                                                                             |\n| height                    | Number   |     ✓     | Height of Collection; this property determines the number of visible (vs virtualized) rows.                                                                                                                                                                                                                                                  |\n| horizontalOverscanSize    | Number   |           | Enables the `Collection` to horizontally \"overscan\" its content similar to how `Grid` does. This can reduce flicker around the edges when a user scrolls quickly. This property defaults to `0`;                                                                                                                                             |\n| id                        | String   |           | Optional custom id to attach to root `Collection` element.                                                                                                                                                                                                                                                                                   |\n| noContentRenderer         | Function |           | Optional renderer to be rendered inside the grid when `cellCount` is 0: `(): PropTypes.node`                                                                                                                                                                                                                                                 |\n| onSectionRendered         | Function |           | Callback invoked with information about the section of the Collection that was just rendered: `({ indices: Array<number> }): void`                                                                                                                                                                                                           |\n| onScroll                  | Function |           | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number }): void`                                                                                                            |\n| scrollLeft                | Number   |           | Horizontal offset                                                                                                                                                                                                                                                                                                                            |\n| scrollToAlignment         | String   |           | Controls the alignment of scrolled-to-cells. The default (\"_auto_\") scrolls the least amount possible to ensure that the specified cell is fully visible. Use \"_start_\" to always align cells to the top/left of the `Collection` and \"_end_\" to align them bottom/right. Use \"_center_\" to align specified cell in the middle of container. |\n| scrollToCell              | Number   |           | Cell index to ensure visible (by scrolling if necessary)                                                                                                                                                                                                                                                                                     |\n| scrollTop                 | Number   |           | Vertical offset                                                                                                                                                                                                                                                                                                                              |\n| sectionSize               | Number   |           | Optionally override the size of the sections a Collection's cells are split into. This is an advanced option and should only be used for performance tuning purposes.                                                                                                                                                                        |\n| style                     | Object   |           | Optional custom inline style to attach to root Collection element.                                                                                                                                                                                                                                                                           |\n| verticalOverscanSize      | Number   |           | Enables the `Collection` to vertically \"overscan\" its content similar to how `Grid` does. This can reduce flicker around the edges when a user scrolls quickly. This property defaults to `0`;                                                                                                                                               |\n| width                     | Number   |     ✓     | Width of Collection; this property determines the number of visible (vs virtualized) columns.                                                                                                                                                                                                                                                |\n\n### Public Methods\n\n##### recomputeCellSizesAndPositions\n\nRecomputes cell sizes and positions.\n\nThis function should be called if cell sizes or positions have changed but nothing else has.\nSince Collection only receives `cellCount` (and not the underlying List or Array) it has no way of detecting when the underlying data changes.\n\n### Class names\n\nThe Collection component supports the following static class names\n\n| Property                                           | Description           |\n| :------------------------------------------------- | :-------------------- |\n| ReactVirtualized\\_\\_Collection                     | Main (outer) element  |\n| ReactVirtualized**Collection**innerScrollContainer | Inner scrollable area |\n\n### Examples\n\nBelow is a very basic `Collection` example. It displays an array of objects with fixed row and column sizes.\n[See here](../source/Collection/Collection.example.js) for a more full-featured example.\n\n```jsx\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {Collection} from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\n// Collection data as an array of objects\nconst list = [\n  {name: 'Brian Vaughn', x: 13, y: 34, width: 123, height: 234},\n  // And so on...\n];\n\nfunction cellRenderer({index, key, style}) {\n  return (\n    <div key={key} style={style}>\n      {list[index].name}\n    </div>\n  );\n}\n\nfunction cellSizeAndPositionGetter({index}) {\n  const datum = list[index];\n\n  return {\n    height: datum.height,\n    width: datum.width,\n    x: datum.x,\n    y: datum.y,\n  };\n}\n\n// Render your grid\nReactDOM.render(\n  <Collection\n    cellCount={list.length}\n    cellRenderer={cellRenderer}\n    cellSizeAndPositionGetter={cellSizeAndPositionGetter}\n    height={300}\n    width={300}\n  />,\n  document.getElementById('example'),\n);\n```\n"
  },
  {
    "path": "docs/Column.md",
    "content": "## Column\n\nDescribes the header and cell contents of a table column.\n\n#### Prop Types\n\n| Property             | Type                              | Required? | Description                                                                                                                                   |\n| :------------------- | :-------------------------------- | :-------: | :-------------------------------------------------------------------------------------------------------------------------------------------- |\n| cellDataGetter       | Function                          |           | Callback responsible for returning a cell's data, given its `dataKey`. [Learn more](#celldatagetter)                                          |\n| cellRenderer         | Function                          |           | Callback responsible for rendering a cell's contents. [Learn more](#cellrenderer)                                                             |\n| className            | String                            |           | CSS class to apply to rendered cell container                                                                                                 |\n| columnData           | Object                            |           | Additional data passed to this column's `cellDataGetter`. Use this object to relay action-creators or relational data.                        |\n| dataKey              | any                               |     ✓     | Uniquely identifies the row-data attribute corresponding to this cell (eg this might be \"name\" in an array of user objects).                  |\n| defaultSortDirection | [SortDirection](SortDirection.md) |           | Default sort order when clicked for the first time. Valid options include \"ASC\" and \"DESC\". Defaults to \"ASC\"                                 |\n| disableSort          | Boolean                           |           | If sort is enabled for the table at large, disable it for this column                                                                         |\n| flexGrow             | Number                            |           | Flex grow style; defaults to 0                                                                                                                |\n| flexShrink           | Number                            |           | Flex shrink style; defaults to 1                                                                                                              |\n| headerClassName      | String                            |           | CSS class to apply to this column's header                                                                                                    |\n| headerRenderer       | Function                          |           | Optional callback responsible for rendering a column's header column. [Learn more](#headerrenderer)                                           |\n| headerStyle          | Object                            |           | Optional inline style to apply to this column's header                                                                                        |\n| id                   | String                            |           | Optional id to set on the column header; used for [`aria-describedby`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-describedby) |\n| label                | Node                              |           | Header label for this column                                                                                                                  |\n| maxWidth             | Number                            |           | Maximum width of column; this property will only be used if :flexGrow is greater than 0                                                       |\n| minWidth             | Number                            |           | Minimum width of column                                                                                                                       |\n| style                | Object                            |           | Optional inline style to apply to rendered cell container                                                                                     |\n| width                | Number                            |     ✓     | Flex basis (width) for this column; This value can grow or shrink based on `flexGrow` and `flexShrink` properties                             |\n\n#### cellDataGetter\n\nCallback responsible for returning a cell's data, given its `dataKey`.\nIt should implement the following signature:\n\n```javascript\nfunction ({\n  columnData: any,\n  dataKey: string,\n  rowData: any\n}): any\n```\n\nA [default `cellDataGetter`](https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultCellDataGetter.js) is provided that simply returns the attribute as a String.\nThis function expects to operate on either a vanilla Object or a Map-like object with a get method.\nYou should override this default method if your data is calculated or requires any custom processing.\n\n#### cellRenderer\n\nCallback responsible for rendering a cell's contents.\nIt should implement the following signature:\n\n```javascript\nfunction ({\n  cellData: any,\n  columnData: any,\n  columnIndex: number,\n  dataKey: string,\n  isScrolling: boolean,\n  rowData: any,\n  rowIndex: number\n}): node\n```\n\nA [default `cellRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultCellRenderer.js) is provided that displays an attribute as a simple string\nYou should override this default method if your data is some other type of object or requires custom formatting.\n\n#### headerRenderer\n\nCallback responsible for rendering a cell's header column.\nIt should implement the following signature:\n\n```javascript\nfunction ({\n  columnData: any,\n  dataKey: string,\n  disableSort: boolean,\n  label: any,\n  sortBy: string,\n  sortDirection: SortDirection\n}): element\n```\n\nA [default `headerRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultHeaderRenderer.js) is provided that displays the column `label` along with a sort indicator if the column is sort-enabled and active.\nYou should override this default method if you want to customize the appearance of table columns.\n"
  },
  {
    "path": "docs/ColumnSizer.md",
    "content": "## ColumnSizer\n\nHigh-order component that auto-calculates column-widths for `Grid` cells.\n\n### Prop Types\n\n| Property       | Type     | Required? | Description                                                                                                                                                                                                          |\n| :------------- | :------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| children       | Function |     ✓     | Function responsible for rendering a virtualized Grid. This function should implement the following signature: `({ adjustedWidth: number, getColumnWidth: Function, registerChild: Function }) => PropTypes.element` |\n| columnMaxWidth | Number   |           | Optional maximum allowed column width                                                                                                                                                                                |\n| columnMinWidth | Number   |           | Optional minimum allowed column width                                                                                                                                                                                |\n| width          | Number   |     ✓     | Width of Grid or `Table` child                                                                                                                                                                                       |\n\n### Children function\n\nThe child function is passed the following named parameters:\n\n| Parameter      | Type     | Description                                                                                                                                                      |\n| :------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| adjustedWidth  | Number   | This number reflects the lesser of the overall `Grid` width or the width of all columns. Use this to make your `Grid` shrink to fit sparse content.              |\n| columnWidth    | Number   | This value should be passed to the `Grid`'s `columnWidth` property.                                                                                              |\n| getColumnWidth | Function | This value can be passed to the `Grid`'s `columnWidth` property but it's recommended that you use the `columnWidth` property instead. This is a legacy property. |\n| registerChild  | Function | This function should be set as the child's `ref` property. It enables a set of rows to be refreshed once their data has finished loading.                        |\n\n### Examples\n\nThis example displays a `Grid` that shrinks to fit sparse content (using the `adjustedWidth` parameter). An interactive demo of this component can be seen [here](https://bvaughn.github.io/react-virtualized/#/components/ColumnSizer).\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {ColumnSizer, Grid} from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\n// numColumns, numRows, someCalculatedHeight, and someCalculatedWidth determined here...\n\n// Render your list\nReactDOM.render(\n  <ColumnSizer\n    columnMaxWidth={100}\n    columnMinWidth={50}\n    columnCount={numColumns}\n    width={someCalculatedWidth}>\n    {({adjustedWidth, getColumnWidth, registerChild}) => (\n      <Grid\n        ref={registerChild}\n        columnWidth={getColumnWidth}\n        columnCount={numColumns}\n        height={someCalculatedHeight}\n        cellRenderer={someCellRenderer}\n        rowHeight={50}\n        rowCount={numRows}\n        width={adjustedWidth}\n      />\n    )}\n  </ColumnSizer>,\n  document.getElementById('example'),\n);\n```\n"
  },
  {
    "path": "docs/Grid.md",
    "content": "## Grid\n\nA windowed grid of elements. `Grid` only renders cells necessary to fill itself based on the current horizontal and vertical scroll position. A simple `Grid` example can be seen [here](#basic-grid-example).\n\n### Prop Types\n\n| Property                   | Type               | Required? | Description                                                                                                                                                                                                                                                                                                                                                                                        |\n| :------------------------- | :----------------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| autoContainerWidth         | Boolean            |           | Set the width of the inner scrollable container to 'auto'. This is useful for single-column Grids to ensure that the column doesn't extend below a vertical scrollbar.                                                                                                                                                                                                                             |\n| autoHeight                 | Boolean            |           | Outer `height` of `Grid` is set to \"auto\". This property should only be used in conjunction with the `WindowScroller` HOC.                                                                                                                                                                                                                                                                         |\n| autoWidth                  | Boolean            |           | Outer `width` of `Grid` is set to \"auto\". This property should only be used in conjunction with the `WindowScroller` HOC.                                                                                                                                                                                                                                                                          |\n| cellRangeRenderer          | Function           |           | Responsible for rendering a group of cells given their index ranges. [Learn more](#cellrangerenderer)                                                                                                                                                                                                                                                                                              |\n| cellRenderer               | Function           |     ✓     | Responsible for rendering a cell given an row and column index. [Learn more](#cellrenderer)                                                                                                                                                                                                                                                                                                        |\n| className                  | String             |           | Optional custom CSS class name to attach to root `Grid` element.                                                                                                                                                                                                                                                                                                                                   |\n| columnCount                | Number             |     ✓     | Number of columns in grid.                                                                                                                                                                                                                                                                                                                                                                         |\n| columnWidth                | Number or Function |     ✓     | Either a fixed column width (number) or a function that returns the width of a column given its index: `({ index: number }): number`. If function is used, specify `estimatedColumnSize` for more consistent scrolling behavior.                                                                                                                                                                   |\n| containerProps             | Object             |           | Responsible for adding props to the cell-container, i.e. `onWheel`.                                                                                                                                                                                                                                                                                                                                |\n| containerRole              | string             |           | ARIA role for the cell-container; defaults to \"rowgroup\"                                                                                                                                                                                                                                                                                                                                           |\n| containerStyle             | Object             |           | Optional custom inline style to attach to inner cell-container element.                                                                                                                                                                                                                                                                                                                            |\n| deferredMeasurementCache   | `CellMeasurer`     |           | If CellMeasurer is used to measure this Grid's children, this should be a pointer to its CellMeasurerCache. A shared CellMeasurerCache reference enables Grid and CellMeasurer to share measurement data.                                                                                                                                                                                          |\n| estimatedColumnSize        | Number             |           | Used to estimate the total width of a `Grid` before all of its columns have actually been measured. The estimated total width is adjusted as columns are rendered.                                                                                                                                                                                                                                 |\n| estimatedRowSize           | Number             |           | Used to estimate the total height of a `Grid` before all of its rows have actually been measured. The estimated total height is adjusted as rows are rendered.                                                                                                                                                                                                                                     |\n| height                     | Number             |     ✓     | Height of Grid; this property determines the number of visible (vs virtualized) rows.                                                                                                                                                                                                                                                                                                              |\n| id                         | String             |           | Optional custom id to attach to root `Grid` element.                                                                                                                                                                                                                                                                                                                                               |\n| isScrolling                | Boolean            |           | Override internal is-scrolling state tracking. This property is primarily intended for use with the WindowScroller component.                                                                                                                                                                                                                                                                      |\n| isScrollingOptOut          | Boolean            |           | Prevents re-rendering of visible cells on scroll end.                                                                                                                                                                                                                                                                                                                                              |\n| noContentRenderer          | Function           |           | Optional renderer to be rendered inside the grid when either `rowCount` or `columnCount` is empty: `(): PropTypes.node`                                                                                                                                                                                                                                                                            |\n| onSectionRendered          | Function           |           | Callback invoked with information about the section of the Grid that was just rendered. This callback is only invoked when visible rows have changed: `({ columnOverscanStartIndex: number, columnOverscanStopIndex: number, columnStartIndex: number, columnStopIndex: number, rowOverscanStartIndex: number, rowOverscanStopIndex: number, rowStartIndex: number, rowStopIndex: number }): void` |\n| onScroll                   | Function           |           | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number }): void`                                                                                                                                                                  |\n| onScrollbarPresenceChange  | Function           |           | Called whenever a horizontal or vertical scrollbar is added or removed: `({ horizontal: boolean, size: number, vertical: boolean }): void`                                                                                                                                                                                                                                                         |\n| overscanColumnCount        | Number             |           | Number of columns to render before/after the visible slice of the grid. This can help reduce flickering during scrolling on certain browsers/devices. See [here](overscanUsage.md) for an important note about this property.                                                                                                                                                                      |\n| overscanIndicesGetter      | Function           |           | Responsible for calculating the number of cells to overscan before and after a specified range [Learn more](#overscanindicesgetter)                                                                                                                                                                                                                                                                |\n| overscanRowCount           | Number             |           | Number of rows to render above/below the visible slice of the grid. This can help reduce flickering during scrolling on certain browsers/devices. See [here](overscanUsage.md) for an important note about this property.                                                                                                                                                                          |\n| role                       | String             |           | Optional override of ARIA role default; defaults to `grid`.                                                                                                                                                                                                                                                                                                                                        |\n| rowCount                   | Number             |     ✓     | Number of rows in grid.                                                                                                                                                                                                                                                                                                                                                                            |\n| rowHeight                  | Number or Function |     ✓     | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number`. If function is used, specify `estimatedRowSize` for more consistent scrolling behavior.                                                                                                                                                                          |\n| scrollingResetTimeInterval | Number             |           | Wait this amount of time after the last scroll event before resetting Grid `pointer-events`; defaults to 150ms.                                                                                                                                                                                                                                                                                    |\n| scrollLeft                 | Number             |           | Horizontal offset                                                                                                                                                                                                                                                                                                                                                                                  |\n| scrollToAlignment          | String             |           | Controls the alignment of scrolled-to-cells. The default (\"_auto_\") scrolls the least amount possible to ensure that the specified cell is fully visible. Use \"_start_\" to always align cells to the top/left of the `Grid` and \"_end_\" to align them bottom/right. Use \"_center_\" to align specified cell in the middle of container.                                                             |\n| scrollToColumn             | Number             |           | Column index to ensure visible (by forcefully scrolling if necessary). Takes precedence over `scrollLeft`.                                                                                                                                                                                                                                                                                         |\n| scrollToRow                | Number             |           | Row index to ensure visible (by forcefully scrolling if necessary). Takes precedence over `scrollTop`.                                                                                                                                                                                                                                                                                             |\n| scrollTop                  | Number             |           | Vertical offset                                                                                                                                                                                                                                                                                                                                                                                    |\n| style                      | Object             |           | Optional custom inline style to attach to root `Grid` element.                                                                                                                                                                                                                                                                                                                                     |\n| tabIndex                   | Number             |           | Optional override of tab index default; defaults to `0`.                                                                                                                                                                                                                                                                                                                                           |\n| width                      | Number             |     ✓     | Width of Grid; this property determines the number of visible (vs virtualized) columns.                                                                                                                                                                                                                                                                                                            |\n\n### Public Methods\n\n##### getOffsetForCell ({ alignment: ?string, columnIndex: ?number, rowIndex: ?number })\n\nGets offsets for a given cell and alignment.\n\n##### getTotalRowsHeight\n\nGets estimated total rows' height.\n\n##### getTotalColumnsWidth\n\nGets estimated total columns' width.\n\n##### handleScrollEvent ({ scrollLeft, scrollTop })\n\nThis method handles a scroll event originating from an external scroll control.\nIt's an advanced method and should probably not be used unless you're implementing a custom scroll-bar solution.\n\n##### measureAllCells\n\nPre-measure all columns and rows in a `Grid`.\n\nTypically cells are only measured as needed and estimated sizes are used for cells that have not yet been measured.\nThis method ensures that the next call to getTotalSize() returns an exact size (as opposed to just an estimated one).\n\n##### recomputeGridSize ({ columnIndex: number, rowIndex: number })\n\nRecomputes row heights and column widths after the specified index (both default to 0).\n\nThis function should be called if dynamic column or row sizes have changed but nothing else has.\nSince `Grid` only receives `columnCount` and `rowCount` it has no way of detecting when the underlying data changes.\n\nThis method will also force a render cycle (via `forceUpdate`) to ensure that the updated measurements are reflected in the rendered grid.\n\n##### scrollToCell ({ columnIndex: number, rowIndex: number })\n\nEnsure column and row are visible.\nThis method can be used to safely scroll back to a cell that a user has scrolled away from even if it was previously scrolled to.\n\n##### scrollToPosition ({ scrollLeft, scrollTop })\n\nScroll to the specified offset(s).\nUseful for animating position changes.\n\n### Class names\n\nThe Grid component supports the following static class names\n\n| Property                                     | Description           |\n| :------------------------------------------- | :-------------------- |\n| ReactVirtualized\\_\\_Grid                     | Main (outer) element  |\n| ReactVirtualized**Grid**innerScrollContainer | Inner scrollable area |\n\n### cellRangeRenderer\n\nThis is an advanced property.\nIt is useful for situations where the `Grid` requires additional, overlayed UI (such as a Gantt chart or a calendar application).\nMany use cases can be solved more easily using the `onScroll` callback or the `ScrollSync` HOC.\n\nIf you do want to override `cellRangeRenderer` the easiest way is to decorate the default implementation like so:\n\n```jsx\nimport {defaultCellRangeRenderer, Grid} from 'react-virtualized';\n\nfunction cellRangeRenderer(props) {\n  const children = defaultCellRangeRenderer(props);\n  children.push(<div>My custom overlay</div>);\n  return children;\n}\n\nfunction CustomizedGrid(props) {\n  return <Grid cellRangeRenderer={cellRangeRenderer} {...props} />;\n}\n```\n\nIf you require greater customization, you may want to fork the [`defaultCellRangeRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/defaultCellRangeRenderer.js) function.\n\nThis function accepts the following named parameters:\n\n```js\nfunction cellRangeRenderer({\n  cellCache, // Temporary cell cache used while scrolling\n  cellRenderer, // Cell renderer prop supplied to Grid\n  columnSizeAndPositionManager, // @see CellSizeAndPositionManager,\n  columnStartIndex, // Index of first column (inclusive) to render\n  columnStopIndex, // Index of last column (inclusive) to render\n  horizontalOffsetAdjustment, // Horizontal pixel offset (required for scaling)\n  isScrolling, // The Grid is currently being scrolled\n  rowSizeAndPositionManager, // @see CellSizeAndPositionManager,\n  rowStartIndex, // Index of first row (inclusive) to render\n  rowStopIndex, // Index of last row (inclusive) to render\n  scrollLeft, // Current horizontal scroll offset of Grid\n  scrollTop, // Current vertical scroll offset of Grid\n  styleCache, // Temporary style (size & position) cache used while scrolling\n  verticalOffsetAdjustment, // Vertical pixel offset (required for scaling)\n}) {\n  const renderedCells = [];\n\n  for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {\n    // This contains :offset (top) and :size (height) information for the cell\n    let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex);\n\n    for (\n      let columnIndex = columnStartIndex;\n      columnIndex <= columnStopIndex;\n      columnIndex++\n    ) {\n      // This contains :offset (left) and :size (width) information for the cell\n      let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(\n        columnIndex,\n      );\n\n      // Be sure to adjust cell position in case the total set of cells is too large to be supported by the browser natively.\n      // In this case, Grid will shift cells as a user scrolls to increase cell density.\n      let left = columnDatum.offset + horizontalOffsetAdjustment;\n      let top = rowDatum.offset + verticalOffsetAdjustment;\n\n      // The rest of the information you need to render the cell are contained in the data.\n      // Be sure to provide unique :key attributes.\n      let key = `${rowIndex}-${columnIndex}`;\n      let height = rowDatum.size;\n      let width = columnDatum.size;\n\n      // Now render your cell and additional UI as you see fit.\n      // Add all rendered children to the :renderedCells Array.\n    }\n  }\n\n  return renderedCells;\n}\n```\n\n### overscanIndicesGetter\n\nThis is an advanced property.\nThis function is responsible for calculating the number of cells to overscan before and after a specified range. By default, React Virtualized optimizes the number of cells to overscan based on scroll direction. If you'd like to customize this behavior, you may want to fork the [`defaultOverscanIndicesGetter`](https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/defaultOverscanIndicesGetter.js) function.\n\n```js\nfunction overscanIndicesGetter({\n  direction, // One of \"horizontal\" or \"vertical\"\n  cellCount, // Number of rows or columns in the current axis\n  scrollDirection, // 1 (forwards) or -1 (backwards)\n  overscanCellsCount, // Maximum number of cells to over-render in either direction\n  startIndex, // Begin of range of visible cells\n  stopIndex, // End of range of visible cells\n}) {\n  return {\n    overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),\n    overscanStopIndex: Math.min(cellCount - 1, stopIndex + overscanCellsCount),\n  };\n}\n```\n\n### cellRenderer\n\nResponsible for rendering a single cell, given its row and column index.\nThis function accepts the following named parameters:\n\n```jsx\nfunction cellRenderer({\n  columnIndex, // Horizontal (column) index of cell\n  isScrolling, // The Grid is currently being scrolled\n  isVisible, // This cell is visible within the grid (eg it is not an overscanned cell)\n  key, // Unique key within array of cells\n  parent, // Reference to the parent Grid (instance)\n  rowIndex, // Vertical (row) index of cell\n  style, // Style object to be applied to cell (to position it);\n  // This must be passed through to the rendered cell element.\n}) {\n  // Grid data is a 2d array in this example...\n  const user = list[rowIndex][columnIndex];\n\n  // If cell content is complex, consider rendering a lighter-weight placeholder while scrolling.\n  const content = isScrolling ? '...' : <User user={user} />;\n\n  // Style is required since it specifies how the cell is to be sized and positioned.\n  // React Virtualized depends on this sizing/positioning for proper scrolling behavior.\n  // By default, the grid component provides the following style properties:\n  //    position\n  //    left\n  //    top\n  //    height\n  //    width\n  // You can add additional class names or style properties as you would like.\n  // Key is also required by React to more efficiently manage the array of cells.\n  return (\n    <div key={key} style={style}>\n      {content}\n    </div>\n  );\n}\n```\n\n### Basic `Grid` Example\n\nBelow is a very basic `Grid` example. The grid displays an array of objects with fixed row and column sizes. (Dynamic sizes are also supported but this example is intended to be basic.) [See here](../source/Grid/Grid.example.js) for a more full-featured example with dynamic cell sizes and more.\n\n```jsx\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {Grid} from 'react-virtualized';\n\n// Grid data as an array of arrays\nconst list = [\n  ['Brian Vaughn', 'Software Engineer', 'San Jose', 'CA', 95125 /* ... */],\n  // And so on...\n];\n\nfunction cellRenderer({columnIndex, key, rowIndex, style}) {\n  return (\n    <div key={key} style={style}>\n      {list[rowIndex][columnIndex]}\n    </div>\n  );\n}\n\n// Render your grid\nReactDOM.render(\n  <Grid\n    cellRenderer={cellRenderer}\n    columnCount={list[0].length}\n    columnWidth={100}\n    height={300}\n    rowCount={list.length}\n    rowHeight={30}\n    width={300}\n  />,\n  document.getElementById('example'),\n);\n```\n"
  },
  {
    "path": "docs/InfiniteLoader.md",
    "content": "## InfiniteLoader\n\nA component that manages just-in-time fetching of data as a user scrolls up or down in a list.\n\nNote that this component is intended to assist with row-loading.\nAs such it is best suited for use with `Table` and `List` (although it can also be used with `Grid`).\nThis component is not compatible with the `Collection` component.\n\nThis is an advanced component and can be confusing in certain situations.\n[See below for more information](#edge-cases-and-considerations).\n\n### Prop Types\n\n| Property         | Type     | Required? | Description                                                                                                                                                                                                                                                                                                                                                                                               |\n| :--------------- | :------- | :-------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| children         | Function |     ✓     | Function responsible for rendering a virtualized component. This function should implement the following signature: `({ onRowsRendered: Function, registerChild: Function }) => PropTypes.element`                                                                                                                                                                                                        |\n| isRowLoaded      | Function |     ✓     | Function responsible for tracking the loaded state of each row. It should implement the following signature: `({ index: number }): boolean`                                                                                                                                                                                                                                                               |\n| loadMoreRows     | Function |     ✓     | Callback to be invoked when more rows must be loaded. It should implement the following signature: `({ startIndex: number, stopIndex: number }): Promise`. The returned Promise should be resolved once row data has finished loading. It will be used to determine when to refresh the list with the newly-loaded data. This callback may be called multiple times in reaction to a single scroll event. |\n| minimumBatchSize | Number   |           | Minimum number of rows to be loaded at a time. This property can be used to batch requests to reduce HTTP requests. Defaults to `10`.                                                                                                                                                                                                                                                                     |\n| rowCount         | Number   |     ✓     | Number of rows in list; can be arbitrary high number if actual number is unknown.                                                                                                                                                                                                                                                                                                                         |\n| threshold        | Number   |           | Threshold at which to pre-fetch data. A threshold X means that data will start loading when a user scrolls within X rows. Defaults to `15`.                                                                                                                                                                                                                                                               |\n\n### Public Methods\n\n##### resetLoadMoreRowsCache (autoReload: boolean = false)\n\nReset any cached data about already-loaded rows. This method should be called if any/all loaded data needs to be refetched (eg a filtered list where the search criteria changes). If `autoReload` passed as true, the last loaded batch would be automatically reloaded.\n\n### Children function\n\nThe child function is passed the following named parameters:\n\n| Parameter      | Type     | Description                                                                                                                               |\n| :------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------- |\n| onRowsRendered | Function | This function should be passed as the child's `onRowsRendered` property. It informs the loader when the user is scrolling.                |\n| registerChild  | Function | This function should be set as the child's `ref` property. It enables a set of rows to be refreshed once their data has finished loading. |\n\n### Examples\n\n###### InfiniteLoader and List\n\nThis example uses `InfiniteLoader` to prefetch rows in a `List` list as a user scrolls.\nAn interactive demo can be seen [here](https://bvaughn.github.io/react-virtualized/#/components/InfiniteLoader).\n\n```jsx\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { InfiniteLoader, List } from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\n// This example assumes you have a way to know/load this information\nconst remoteRowCount\n\nconst list = [];\n\nfunction isRowLoaded ({ index }) {\n  return !!list[index];\n}\n\nfunction loadMoreRows ({ startIndex, stopIndex }) {\n  return fetch(`path/to/api?startIndex=${startIndex}&stopIndex=${stopIndex}`)\n    .then(response => {\n      // Store response data in list...\n    })\n}\n\nfunction rowRenderer ({ key, index, style}) {\n  return (\n    <div\n      key={key}\n      style={style}\n    >\n      {list[index]}\n    </div>\n  )\n}\n\n// Render your list\nReactDOM.render(\n  <InfiniteLoader\n    isRowLoaded={isRowLoaded}\n    loadMoreRows={loadMoreRows}\n    rowCount={remoteRowCount}\n  >\n    {({ onRowsRendered, registerChild }) => (\n      <List\n        height={200}\n        onRowsRendered={onRowsRendered}\n        ref={registerChild}\n        rowCount={remoteRowCount}\n        rowHeight={20}\n        rowRenderer={rowRenderer}\n        width={300}\n      />\n    )}\n  </InfiniteLoader>,\n  document.getElementById('example')\n);\n```\n\n###### InfiniteLoader and Grid\n\nIt is not common to use `InfiniteLoader` and `Grid` together but it is possible using an approach like this:\n\n```jsx\nclass MyComponent extends Component {\n  constructor (props, context) {\n    super(props, context)\n\n    this._infiniteLoaderChildFunction = this._infiniteLoaderChildFunction.bind(this)\n    this._onSectionRendered = this._onSectionRendered.bind(this)\n  }\n\n  render () {\n    const { infiniteLoaderProps } = this.props\n\n    <InfiniteLoader {...infiniteLoaderProps}>\n      {this._infiniteLoaderChildFunction}\n    </InfiniteLoader>\n  }\n\n  _infiniteLoaderChildFunction ({ onRowsRendered, registerChild }) {\n    this._onRowsRendered = onRowsRendered\n\n    const { gridProps } = this.props\n\n    return (\n      <Grid\n        {...gridProps}\n        onSectionRendered={this._onSectionRendered}\n        ref={registerChild}\n      />\n    )\n  }\n\n  _onSectionRendered ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) {\n    const startIndex = rowStartIndex * columnCount + columnStartIndex\n    const stopIndex = rowStopIndex * columnCount + columnStopIndex\n\n    this._onRowsRendered({\n      startIndex,\n      stopIndex\n    })\n  }\n}\n```\n\n### Edge Cases and Considerations\n\n###### Tracking loaded (and loading) rows\n\n`InfiniteLoader` is not a stateful component, meaning that it does not keep track of which rows it has requested to be loaded.\nYour component will need to track this information yourself to avoid loading rows multiple times.\nOne way to do this is to to use a map to track the status of each row like so:\n\n```js\n_isRowLoaded ({ index }) {\n  const { loadedRowsMap } = this.state\n\n  // No entry in this map signifies that the row has never been loaded before\n  // An entry (either LOADING or LOADED) can be treated as loaded as far as InfiniteLoader is concerned\n  return !!loadedRowsMap[index]\n}\n```\n\n###### Memoization and rowCount changes\n\n`InfiniteLoader` memoizes calls to `loadMoreRows` in order to avoid multiple calls with the same parameters while a user is scrolling.\nThis can have an unexpected impact though if the underlying collection data changes.\nIn that case it is possible that `InfiniteLoader` will not _know_ to call `loadMoreRows` again because- from its point of view- it already made that call for a given row.\n(React Virtualized components do not know anything about the underlying data after all, only the number of items in the collection.)\n\nThe easiest way to address this is for your application code to call `loadMoreRows` when it detects that the underlying collection may have changed.\nFor example:\n\n```js\n_loadMoreRows ({ startIndex, stopIndex }) {\n  this._loadMoreRowsStartIndex = startIndex\n  this._loadMoreRowsStopIndex = stopIndex\n\n  // Load the rows\n}\n\ncomponentDidUpdate (prevProps, prevState) {\n  // If props/state signals that the underlying collection has changed,\n  // Reload the most recently requested batch of rows:\n  if (...) {\n    this._loadMoreRows({\n      startIndex: this._loadMoreRowsStartIndex,\n      stopIndex: this._loadMoreRowsStopIndex\n    })\n  }\n}\n```\n"
  },
  {
    "path": "docs/List.md",
    "content": "## List\n\nThis component renders a windowed list (rows) of elements.\nIt uses a `Grid` internally to render the rows and all props are relayed to that inner `Grid`.\nThat means that `List` also accepts [`Grid` props](Grid.md) in addition to the props shown below.\n\n### Prop Types\n\n| Property          | Type               | Required? | Description                                                                                                                                                                                                                                                                                               |\n| :---------------- | :----------------- | :-------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| autoHeight        | Boolean            |           | Outer `height` of `List` is set to \"auto\". This property should only be used in conjunction with the `WindowScroller` HOC.                                                                                                                                                                                |\n| className         | String             |           | Optional custom CSS class name to attach to root `List` element.                                                                                                                                                                                                                                          |\n| estimatedRowSize  | Number             |           | Used to estimate the total height of a `List` before all of its rows have actually been measured. The estimated total height is adjusted as rows are rendered.                                                                                                                                            |\n| height            | Number             |     ✓     | Height constraint for list (determines how many actual rows are rendered)                                                                                                                                                                                                                                 |\n| id                | String             |           | Optional custom id to attach to root `List` element.                                                                                                                                                                                                                                                      |\n| noRowsRenderer    | Function           |           | Callback used to render placeholder content when `rowCount` is 0                                                                                                                                                                                                                                          |\n| onRowsRendered    | Function           |           | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex: number, overscanStopIndex: number, startIndex: number, stopIndex: number }): void`                                                                                                             |\n| onScroll          | Function           |           | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, scrollHeight: number, scrollTop: number }): void`                                                                                                                                       |\n| overscanRowCount  | Number             |           | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browsers/devices. See [here](overscanUsage.md) for an important note about this property.                                                                                |\n| rowCount          | Number             |     ✓     | Number of rows in list.                                                                                                                                                                                                                                                                                   |\n| rowHeight         | Number or Function |     ✓     | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number`                                                                                                                                                                          |\n| rowRenderer       | Function           |     ✓     | Responsible for rendering a row. [Learn more](#rowrenderer).                                                                                                                                                                                                                                              |\n| scrollToAlignment | String             |           | Controls the alignment scrolled-to-rows. The default (\"_auto_\") scrolls the least amount possible to ensure that the specified row is fully visible. Use \"_start_\" to always align rows to the top of the list and \"_end_\" to align them bottom. Use \"_center_\" to align them in the middle of container. |\n| scrollToIndex     | Number             |           | Row index to ensure visible (by forcefully scrolling if necessary)                                                                                                                                                                                                                                        |\n| scrollTop         | Number             |           | Forced vertical scroll offset; can be used to synchronize scrolling between components                                                                                                                                                                                                                    |\n| style             | Object             |           | Optional custom inline style to attach to root `List` element.                                                                                                                                                                                                                                            |\n| tabIndex          | Number             |           | Optional override of tab index default; defaults to `0`.                                                                                                                                                                                                                                                  |\n| width             | Number             |     ✓     | Width of the list                                                                                                                                                                                                                                                                                         |\n\n### Public Methods\n\n##### forceUpdateGrid\n\nForcefully re-render the inner `Grid` component.\n\nCalling `forceUpdate` on `List` may not re-render the inner `Grid` since it uses `shallowCompare` as a performance optimization.\nUse this method if you want to manually trigger a re-render.\nThis may be appropriate if the underlying row data has changed but the row sizes themselves have not.\n\n##### getOffsetForRow ({ alignment: ?string, index: ?number })\n\nGets offset for a given row and alignment.\n\n##### measureAllRows\n\nPre-measure all rows in a `List`.\n\nTypically rows are only measured as needed and estimated heights are used for cells that have not yet been measured.\nThis method ensures that the next call to getTotalSize() returns an exact size (as opposed to just an estimated one).\n\n##### recomputeRowHeights (index: number)\n\nRecompute row heights and offsets after the specified index (defaults to 0).\n\n`List` has no way of knowing when its underlying list data has changed since it only receives a `rowHeight` property.\nIf the `rowHeight` is a number it can compare before and after values but if it is a function that comparison is error prone.\nIn the event that a dynamic `rowHeight` function is in use and the row heights have changed this function should be manually called by the \"smart\" container parent.\n\nThis method will also force a render cycle (via `forceUpdate`) to ensure that the updated measurements are reflected in the rendered list.\n\n##### scrollToPosition (scrollTop: number)\n\nScroll to the specified offset.\nUseful for animating position changes.\n\n##### scrollToRow (index: number)\n\nEnsure row is visible.\nThis method can be used to safely scroll back to a cell that a user has scrolled away from even if it was previously scrolled to.\n\n### rowRenderer\n\nResponsible for rendering a single row, given its index.\nThis function accepts the following named parameters:\n\n```jsx\nfunction rowRenderer({\n  index, // Index of row\n  isScrolling, // The List is currently being scrolled\n  isVisible, // This row is visible within the List (eg it is not an overscanned row)\n  key, // Unique key within array of rendered rows\n  parent, // Reference to the parent List (instance)\n  style, // Style object to be applied to row (to position it);\n  // This must be passed through to the rendered row element.\n}) {\n  const user = list[index];\n\n  // If row content is complex, consider rendering a light-weight placeholder while scrolling.\n  const content = isScrolling ? '...' : <User user={user} />;\n\n  // Style is required since it specifies how the row is to be sized and positioned.\n  // React Virtualized depends on this sizing/positioning for proper scrolling behavior.\n  // By default, the List component provides following style properties:\n  //    position\n  //    left\n  //    top\n  //    height\n  //    width\n  // You can add additional class names or style properties as you would like.\n  // Key is also required by React to more efficiently manage the array of rows.\n  return (\n    <div key={key} style={style}>\n      {content}\n    </div>\n  );\n}\n```\n\n### Class names\n\nThe List component supports the following static class names\n\n| Property                 | Description          |\n| :----------------------- | :------------------- |\n| ReactVirtualized\\_\\_List | Main (outer) element |\n\n### Examples\n\nBelow is a simple `List` example. Each row in the virtualized list is rendered through the use of a `rowRenderer` function for performance reasons. This function must return an element that has a unique `key`, applies the `style` and has content fitting within `rowHeight`.\n\n**Note** that it is very important that rows do not have vertical overflow.\nIt would make scrolling the list difficult (as individual items will intercept the scroll events).\nFor this reason it is recommended that your rows use a style like `overflow-y: hidden`.)\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {List} from 'react-virtualized';\n\n// List data as an array of strings\nconst list = [\n  'Brian Vaughn',\n  // And so on...\n];\n\nfunction rowRenderer({\n  key, // Unique key within array of rows\n  index, // Index of row within collection\n  isScrolling, // The List is currently being scrolled\n  isVisible, // This row is visible within the List (eg it is not an overscanned row)\n  style, // Style object to be applied to row (to position it)\n}) {\n  return (\n    <div key={key} style={style}>\n      {list[index]}\n    </div>\n  );\n}\n\n// Render your list\nReactDOM.render(\n  <List\n    width={300}\n    height={300}\n    rowCount={list.length}\n    rowHeight={20}\n    rowRenderer={rowRenderer}\n  />,\n  document.getElementById('example'),\n);\n```\n"
  },
  {
    "path": "docs/Masonry.md",
    "content": "The `Masonry` component efficiently displays dynamically-sized, user-positioned cells using windowing techniques. Cell positions are controlled by an injected `cellPositioner` property. Windowing is vertical; this component does not support horizontal scrolling.\n\n### Overview\n\n#### Measuring and layout\n\nRendering occurs in two phases:\n\n##### Phase 1: Measurement\n\nThis phase uses estimated cell sizes (provided by the `cellMeasurerCache` property) to determine how many cells to measure in a batch. Batch size is chosen using a fast, naive layout algorithm that stacks images in order until the viewport has been filled. After measurement is complete (`componentDidMount` or `componentDidUpdate`) this component evaluates positioned cells in order to determine if another measurement pass is required (eg if actual cell sizes were less than estimated sizes). All measurements are permanently cached (keyed by `keyMapper`) for performance purposes.\n\n##### Phase 2: Layout\n\nThis phase uses the external `cellPositioner` to position cells. At this time the positioner has access to cached size measurements for all cells. The positions it returns are cached by `Masonry` for fast access later.\n\nPhase one is repeated if the user scrolls beyond the current layout's bounds. If the layout is invalidated due to eg a resize, cached positions can be cleared using `recomputeCellPositions()` or `clearCellPositions()`.\n\n#### Animation Constraints\n\n- Simple animations are supported (eg translate/slide into place on initial reveal).\n- More complex animations are not (eg flying from one position to another on resize).\n\n#### Layout Constraints\n\n- This component supports a multi-column layout.\n- Each item can have a unique, lazily-measured height.\n- The width of all items in a column must be equal. (Items may not span multiple columns.)\n- The left position of all items within a column must align.\n- Cell measurements must be synchronous. Size impacts layout and async measurements would require frequent layout invalidation. Support for this may be added in the future but for now the use of the `CellMeasurer` render callback's async `measure` parameter is not supported.\n\n### Prop Types\n\n| Property                   | Type     | Required? | Description                                                                                                                                                                                                  |\n| :------------------------- | :------- | :-------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| cellCount                  | number   |     ✓     | Total number of items                                                                                                                                                                                        |\n| cellMeasurerCache          | mixed    |     ✓     | Caches item measurements. Default sizes help `Masonry` decide how many images to batch-measure. Learn more [here](CellMeasurer.md#cellmeasurercache).                                                        |\n| cellPositioner             | function |     ✓     | Positions a cell given an index: `(index: number) => ({ left: number, top: number })`. [Learn more](#createmasonrycellpositioner)                                                                            |\n| cellRenderer               | function |     ✓     | Responsible for rendering a cell given an index. [Learn more](#cellrenderer)                                                                                                                                 |\n| className                  | string   |           | Optional custom CSS class name to attach to root `Masonry` element.                                                                                                                                          |\n| height                     | number   |     ✓     | Height of the component; this value determines the number of visible items.                                                                                                                                  |\n| id                         | string   |           | Optional custom id to attach to root `Masonry` element.                                                                                                                                                      |\n| keyMapper                  | function |           | Maps an index to a unique id to store cached measurement and position info for a cell. This prevents eg cached measurements from being invalidated when a collection is re-ordered. `(index: number) => any` |\n| onCellsRendered            | function |           | Callback invoked with information about the cells that were most recently rendered. This callback is only invoked when visible cells have changed: `({ startIndex: number, stopIndex: number }): void`       |\n| onScroll                   | function |           | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, scrollHeight: number, scrollTop: number }): void`                                          |\n| overscanByPixels           | number   |           | Render this many additional pixels above and below the viewport. This helps reduce flicker when a user scrolls quickly. Defaults to 20.                                                                      |\n| role                       | string   |           | Optional override of ARIA role default; defaults to \"grid\".                                                                                                                                                  |\n| scrollingResetTimeInterval | number   |           | Wait this amount of time after the last scroll event before resetting `pointer-events`; defaults to 150ms.                                                                                                   |\n| style                      | mixed    |           | Optional custom inline style to attach to root `Masonry` element.                                                                                                                                            |\n| tabIndex                   | number   |           | Optional override of tab index default; defaults to 0.                                                                                                                                                       |\n| width                      | number   |     ✓     | Width of the component; this value determines the number of visible items.                                                                                                                                   |\n| rowDirection               | string   |           | row direction of items, can be `ltr` or `rtl` defaults to `ltr`                                                                                                                                              |\n| scrollTop                  | number   |           | Forced vertical scroll offset; can be used to synchronize scrolling between components                                                                                                                       |\n\n## Public Methods\n\n##### clearCellPositions ()\n\nClears internal position cache and force-updates.\n\n##### recomputeCellPositions ()\n\nResets internal position cache, synchronously re-computes positions, then force-updates.\n\n### cellRenderer\n\nResponsible for rendering a single cell given its index. This function accepts the following named parameters:\n\n```jsx\nfunction cellRenderer({\n  index, // Index of item within the collection\n  isScrolling, // The Grid is currently being scrolled\n  key, // Unique key within array of cells\n  parent, // Reference to the parent Grid (instance)\n  style, // Style object to be applied to cell (to position it);\n  // This must be passed through to the rendered cell element.\n}) {\n  return (\n    <CellMeasurer\n      cache={cellMeasurerCache}\n      index={index}\n      key={key}\n      parent={parent}>\n      <div style={style}>{/* Your content goes here */}</div>\n    </CellMeasurer>\n  );\n}\n```\n\n### createMasonryCellPositioner\n\n`Masonry` provides a built-in positioner for a simple layout. This positioner requires a few configuration settings:\n\n| Property          | Type                | Required? | Description                                  |\n| :---------------- | :------------------ | :-------: | :------------------------------------------- |\n| cellMeasurerCache | `CellMeasurerCache` |     ✓     | Contains cell measurements (eg item height). |\n| columnCount       | number              |     ✓     | Number of columns to use in layout.          |\n| columnWidth       | number              |     ✓     | Column width.                                |\n| spacer            | number              |           | Empty space between columns; defaults to 0.  |\n\nYou can use this layout as shown below:\n\n```js\nconst cellPositioner = createMasonryCellPositioner({\n  cellMeasurerCache: cache,\n  columnCount: 3,\n  columnWidth: 200,\n  spacer: 10,\n});\n\nlet masonryRef;\n\nfunction renderMasonry(props) {\n  return (\n    <Masonry\n      cellMeasurerCache={cache}\n      cellPositioner={cellPositioner}\n      ref={ref => (masonryRef = ref)}\n      {...props}\n    />\n  );\n}\n```\n\nIf any of the configuration settings change due to external changes (eg window resize event) you can update them using the `reset` method as shown below:\n\n```js\ncellPositioner.reset({\n  columnCount: 4,\n  columnWidth: 250,\n  spacer: 15,\n});\n\nmasonryRef.recomputeCellPositions();\n```\n\n### Basic `Masonry` Example\n\nBelow is a very basic `Masonry` example with a naive layout algorithm.\n\n```jsx\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {\n  CellMeasurer,\n  CellMeasurerCache,\n  createMasonryCellPositioner,\n  Masonry,\n} from 'react-virtualized';\n\n// Array of images with captions\nconst list = [];\n\n// Default sizes help Masonry decide how many images to batch-measure\nconst cache = new CellMeasurerCache({\n  defaultHeight: 250,\n  defaultWidth: 200,\n  fixedWidth: true,\n});\n\n// Our masonry layout will use 3 columns with a 10px gutter between\nconst cellPositioner = createMasonryCellPositioner({\n  cellMeasurerCache: cache,\n  columnCount: 3,\n  columnWidth: 200,\n  spacer: 10,\n});\n\nfunction cellRenderer({index, key, parent, style}) {\n  const datum = list[index];\n\n  return (\n    <CellMeasurer cache={cache} index={index} key={key} parent={parent}>\n      <div style={style}>\n        <img\n          src={datum.source}\n          style={{\n            height: datum.imageHeight,\n            width: datum.imageWidth,\n          }}\n        />\n        <h4>{datum.caption}</h4>\n      </div>\n    </CellMeasurer>\n  );\n}\n\n// Render your grid\nReactDOM.render(\n  <Masonry\n    cellCount={list.length}\n    cellMeasurerCache={cache}\n    cellPositioner={cellPositioner}\n    cellRenderer={cellRenderer}\n    height={600}\n    width={800}\n  />,\n  document.getElementById('example'),\n);\n```\n\n### Masonry example with dynamically measured images\n\nItems in the list in basic example can only be measured once, which means client has to know all the\nsizes upfront before displaying. For cases when server cannot deliver such data we need to\npre-measure images. In order to preserve correct optimized layout images can only be displayed one\nby one in the order they appear in the array, not in the order they are loaded.\n\nThese specifics were taken into account in a small library\n[react-virtualized-image-measurer](https://github.com/kirill-konshin/react-virtualized-image-measurer)\nhere is an example with dynamically measured images:\n\n```js\nimport React from 'react';\nimport {render} from 'react-dom';\nimport {\n  CellMeasurer,\n  CellMeasurerCache,\n  createMasonryCellPositioner,\n  Masonry,\n} from 'react-virtualized';\nimport ImageMeasurer from 'react-virtualized-image-measurer';\n\n// Array of images with captions\n//const list = [{image: 'http://...', title: 'Foo'}];\n\n// We need to make sure images are loaded from scratch every time for this demo\nconst noCacheList = list.map(item => ({\n  ...item,\n  image: item.image + '?noCache=' + Math.random(),\n}));\n\nconst columnWidth = 200;\nconst defaultHeight = 250;\nconst defaultWidth = columnWidth;\n\n// Default sizes help Masonry decide how many images to batch-measure\nconst cache = new CellMeasurerCache({\n  defaultHeight,\n  defaultWidth,\n  fixedWidth: true,\n});\n\n// Our masonry layout will use 3 columns with a 10px gutter between\nconst cellPositioner = createMasonryCellPositioner({\n  cellMeasurerCache: cache,\n  columnCount: 3,\n  columnWidth,\n  spacer: 10,\n});\n\nconst MasonryComponent = ({itemsWithSizes}) => {\n  function cellRenderer({index, key, parent, style}) {\n    const {item, size} = itemsWithSizes[index];\n    const height = columnWidth * (size.height / size.width) || defaultHeight;\n\n    return (\n      <CellMeasurer cache={cache} index={index} key={key} parent={parent}>\n        <div style={style}>\n          <img\n            src={item.image}\n            alt={item.title}\n            style={{\n              height: height,\n              width: columnWidth,\n            }}\n          />\n          <h4>{item.title}</h4>\n        </div>\n      </CellMeasurer>\n    );\n  }\n\n  return (\n    <Masonry\n      cellCount={itemsWithSizes.length}\n      cellMeasurerCache={cache}\n      cellPositioner={cellPositioner}\n      cellRenderer={cellRenderer}\n      height={600}\n      width={800}\n    />\n  );\n};\n\n// Render your grid\nrender(\n  <ImageMeasurer\n    items={noCacheList}\n    image={item => item.image}\n    defaultHeight={defaultHeight}\n    defaultWidth={defaultWidth}>\n    {({itemsWithSizes}) => <MasonryComponent itemsWithSizes={itemsWithSizes} />}\n  </ImageMeasurer>,\n  document.getElementById('root'),\n);\n```\n\nLive demo: https://codesandbox.io/s/7y66p25qv6\n"
  },
  {
    "path": "docs/MultiGrid.md",
    "content": "## MultiGrid\n\nDecorates `Grid` and adds fixed columns and/or rows.\nThis is already possible using `ScrollSync` and 2 or more `Grid`s but `MultiGrid` reduces the boilerplate.\n\nThe majority of `MultiGrid` properties (eg `cellRenderer`) are relayed to all child `Grid`s.\nSome properties (eg `columnCount`, `rowCount`) are adjusted slightly to supported fixed rows and columns.\n\n### Prop Types\n\n| Property                    | Type     | Required? | Description                                                                                                                                                               |\n| :-------------------------- | :------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| classNameBottomLeftGrid     | string   |           | Optional custom className to attach to bottom-left `Grid` element.                                                                                                        |\n| classNameBottomRightGrid    | string   |           | Optional custom className to attach to bottom-right `Grid` element.                                                                                                       |\n| classNameTopLeftGrid        | string   |           | Optional custom className to attach to top-left `Grid` element.                                                                                                           |\n| classNameTopRightGrid       | string   |           | Optional custom className to attach to top-right `Grid` element.                                                                                                          |\n| enableFixedColumnScroll     | boolean  |           | Fixed column can be actively scrolled; disabled by default                                                                                                                |\n| enableFixedRowScroll        | boolean  |           | Fixed row can be actively scrolled; disabled by default                                                                                                                   |\n| fixedColumnCount            | number   |           | Number of fixed columns; defaults to `0`                                                                                                                                  |\n| fixedRowCount               | number   |           | Number of fixed rows; defaults to `0`                                                                                                                                     |\n| onScrollbarPresenceChange   | Function |           | Called whenever a horizontal or vertical scrollbar is added or removed from the bottom, right `Grid`.: `({ horizontal: boolean, size: number, vertical: boolean }): void` |\n| style                       | object   |           | Optional custom inline style to attach to root `MultiGrid` element.                                                                                                       |\n| styleBottomLeftGrid         | object   |           | Optional custom inline style to attach to bottom-left `Grid` element.                                                                                                     |\n| styleBottomRightGrid        | object   |           | Optional custom inline style to attach to bottom-right `Grid` element.                                                                                                    |\n| styleTopLeftGrid            | object   |           | Optional custom inline style to attach to top-left `Grid` element.                                                                                                        |\n| styleTopRightGrid           | object   |           | Optional custom inline style to attach to top-right `Grid` element.                                                                                                       |\n| hideTopRightGridScrollbar   | boolean  |           | Optional hides top-right `Grid` scrollbar by adding an additional wrapper. Only useful if `enableFixedRowScroll` is set to `true`                                         |\n| hideBottomLeftGridScrollbar | boolean  |           | Optional hides bottom-left `Grid` scrollbar by adding an additional wrapper. Only useful if `enableFixedColumnScroll` is set to `true`                                    |\n\n### Public Methods\n\n##### forceUpdateGrids\n\nPass-thru that calls `forceUpdate` on all child `Grid`s.\n\n##### measureAllCells\n\nPass-thru that calls `measureAllCells` on all child `Grid`s.\n\n##### recomputeGridSize\n\nPass-thru that calls `recomputeGridSize` on all child `Grid`s.\n\n### Examples\n\n```jsx\nimport {MultiGrid} from 'react-virtualized';\n\nfunction render() {\n  return (\n    <MultiGrid\n      cellRenderer={cellRenderer}\n      columnWidth={75}\n      columnCount={50}\n      fixedColumnCount={2}\n      fixedRowCount={1}\n      height={300}\n      rowHeight={40}\n      rowCount={100}\n      width={width}\n    />\n  );\n}\n```\n"
  },
  {
    "path": "docs/README.md",
    "content": "## Documentation\n\n### Components\n\n- [Collection](Collection.md)\n- [Grid](Grid.md)\n- [List](List.md)\n- [Masonry](Masonry.md)\n- [Table](Table.md)\n  - [Column](Column.md)\n  - [SortDirection](SortDirection.md)\n\n### High-Order Components\n\n- [ArrowKeyStepper](ArrowKeyStepper.md)\n- [AutoSizer](AutoSizer.md)\n- [CellMeasurer](CellMeasurer.md)\n- [ColumnSizer](ColumnSizer.md)\n- [InfiniteLoader](InfiniteLoader.md)\n- [MultiGrid](MultiGrid.md)\n- [ScrollSync](ScrollSync.md)\n- [WindowScroller](WindowScroller.md)\n\n### How-to Guides\n\n- [Customizing classes and styles](customizingStyles.md)\n- [Displaying items in reverse order](reverseList.md)\n- [Using AutoSizer](usingAutoSizer.md)\n- [Creating an infinite-loading list](creatingAnInfiniteLoadingList.md)\n- [Natural sort Table](tableWithNaturalSort.md)\n- [Sorting a Table by multiple columns](multiColumnSortTable.md)\n"
  },
  {
    "path": "docs/ScrollSync.md",
    "content": "## ScrollSync\n\nHigh order component that simplifies the process of synchronizing scrolling between two or more virtualized components.\n\n### Prop Types\n\n| Property | Type     | Required? | Description                                                                                                                                       |\n| :------- | :------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------ |\n| children | Function |     ✓     | Function responsible for rendering 2 or more virtualized components. [See below](#children-function) for details about this function's signature. |\n\n### Children function\n\nThe child function is passed the following named parameters:\n\n| Parameter    | Type     | Description                                                                                                                                                                                                              |\n| :----------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| clientHeight | Number   | Height of the visible portion of the `Grid` (or other scroll-synced component)                                                                                                                                           |\n| clientWidth  | Number   | Width of the visible portion of the `Grid` (or other scroll-synced component)                                                                                                                                            |\n| onScroll     | Function | This function should be passed through to at least one of the virtualized child components. Updates to it will trigger updates to the scroll offset parameters which will in turn update the other virtualized children. |\n| scrollHeight | Number   | Total height of all rows in the `Grid` (or other scroll-synced component)                                                                                                                                                |\n| scrollLeft   | Number   | The current scroll-left offset.                                                                                                                                                                                          |\n| scrollTop    | Number   | The current scroll-top offset.                                                                                                                                                                                           |\n| scrollWidth  | Number   | Total width of all rows in the `Grid` (or other scroll-synced component)                                                                                                                                                 |\n\n### Examples\n\nThis example uses `ScrollSync` to create a fixed row of columns to go along with a scrollable grid.\n\n```jsx\nimport {Grid, List, ScrollSync} from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\nfunction render(props) {\n  return (\n    <ScrollSync>\n      {({\n        clientHeight,\n        clientWidth,\n        onScroll,\n        scrollHeight,\n        scrollLeft,\n        scrollTop,\n        scrollWidth,\n      }) => (\n        <div className=\"Table\">\n          <div className=\"LeftColumn\">\n            <List scrollTop={scrollTop} {...props} />\n          </div>\n          <div className=\"RightColumn\">\n            <Grid onScroll={onScroll} {...props} />\n          </div>\n        </div>\n      )}\n    </ScrollSync>\n  );\n}\n```\n"
  },
  {
    "path": "docs/SortDirection.md",
    "content": "## SortDirection\n\nSpecifies which direction [Table](Table.md) data is currently sorted in.\n\n#### SortDirection.ASC\n\nSort items in ascending order.\nThis means arranging from the lowest value to the highest (e.g. a-z, 0-9).\n\n#### SortDirection.DESC\n\nSort items in descending order.\nThis means arranging from the highest value to the lowest (e.g. z-a, 9-0).\n"
  },
  {
    "path": "docs/Table.md",
    "content": "## Table\n\nTable component with fixed headers and windowed rows for improved performance with large data sets.\nThis component expects explicit `width` and `height` parameters.\n`Table` content can scroll vertically but it is not meant to scroll horizontally.\n\n### Prop Types\n\n| Property          | Type                              | Required? | Description                                                                                                                                                                                                                                                                                               |\n| :---------------- | :-------------------------------- | :-------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| autoHeight        | Boolean                           |           | Outer `height` of `Table` is set to \"auto\". This property should only be used in conjunction with the `WindowScroller` HOC.                                                                                                                                                                               |\n| children          | [Column](Column.md)               |     ✓     | One or more Columns describing the data displayed in this table                                                                                                                                                                                                                                           |\n| className         | String                            |           | Optional custom CSS class name to attach to root `Table` element.                                                                                                                                                                                                                                         |\n| disableHeader     | Boolean                           |           | Do not render the table header (only the rows)                                                                                                                                                                                                                                                            |\n| estimatedRowSize  | Number                            |           | Used to estimate the total height of a `Table` before all of its rows have actually been measured. The estimated total height is adjusted as rows are rendered.                                                                                                                                           |\n| gridClassName     | String                            |           | Optional custom CSS class name to attach to inner Grid element                                                                                                                                                                                                                                            |\n| gridStyle         | Object                            |           | Optional inline style to attach to inner Grid element                                                                                                                                                                                                                                                     |\n| headerClassName   | String                            |           | CSS class to apply to all column headers                                                                                                                                                                                                                                                                  |\n| headerHeight      | Number                            |     ✓     | Fixed height of header row                                                                                                                                                                                                                                                                                |\n| headerRowRenderer | Function                          |           | Responsible for rendering the table header row given an array of columns. [Learn more](#headerrowrenderer)                                                                                                                                                                                                |\n| headerStyle       | Object                            |           | Optional custom inline style to attach to table header columns.                                                                                                                                                                                                                                           |\n| height            | Number                            |     ✓     | Fixed/available height for out DOM element                                                                                                                                                                                                                                                                |\n| id                | String                            |           | Optional custom id to attach to root `Table` element.                                                                                                                                                                                                                                                     |\n| noRowsRenderer    | Function                          |           | Callback used to render placeholder content when :rowCount is 0                                                                                                                                                                                                                                           |\n| onColumnClick     | Function                          |           | Callback invoked when a user clicks on a table column. `({ columnData: any, dataKey: string, event: Event }): void`                                                                                                                                                                                       |\n| onHeaderClick     | Function                          |           | Callback invoked when a user clicks on a table header. `({ columnData: any, dataKey: string, event: Event }): void`                                                                                                                                                                                       |\n| onRowClick        | Function                          |           | Callback invoked when a user clicks on a table row. `({ event: Event, index: number, rowData: any }): void`                                                                                                                                                                                               |\n| onRowDoubleClick  | Function                          |           | Callback invoked when a user double-clicks on a table row. `({ event: Event, index: number, rowData: any }): void`                                                                                                                                                                                        |\n| onRowMouseOut     | Function                          |           | Callback invoked when the mouse leaves a table row. `({ event: Event, index: number, rowData: any }): void`                                                                                                                                                                                               |\n| onRowMouseOver    | Function                          |           | Callback invoked when a user moves the mouse over a table row. `({ event: Event, index: number, rowData: any }): void`                                                                                                                                                                                    |\n| onRowRightClick   | Function                          |           | Callback invoked when a user right-clicks on a table row. `({ event: Event, index: number, rowData: any }): void`                                                                                                                                                                                         |\n| onRowsRendered    | Function                          |           | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex: number, overscanStopIndex: number, startIndex: number, stopIndex: number }): void`                                                                                                             |\n| overscanRowCount  | Number                            |           | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browsers/devices. See [here](overscanUsage.md) for an important note about this property.                                                                                |\n| onScroll          | Function                          |           | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, scrollHeight: number, scrollTop: number }): void`                                                                                                                                       |\n| rowClassName      | String or Function                |           | CSS class to apply to all table rows (including the header row). This value may be either a static string or a function with the signature `({ index: number }): string`. Note that for the header row an index of `-1` is provided.                                                                      |\n| rowCount          | Number                            |     ✓     | Number of rows in table.                                                                                                                                                                                                                                                                                  |\n| rowGetter         | Function                          |     ✓     | Callback responsible for returning a data row given an index. `({ index: int }): any`                                                                                                                                                                                                                     |\n| rowHeight         | Number or Function                |     ✓     | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number`                                                                                                                                                                          |\n| rowRenderer       | Function                          |           | Responsible for rendering a table row given an array of columns. [Learn more](#rowrenderer)                                                                                                                                                                                                               |\n| rowStyle          | Object or Function                |           | Optional custom inline style to attach to table rows. This value may be either a style object or a function with the signature `({ index: number }): Object`. Note that for the header row an index of `-1` is provided.                                                                                  |\n| scrollToAlignment | String                            |           | Controls the alignment scrolled-to-rows. The default (\"_auto_\") scrolls the least amount possible to ensure that the specified row is fully visible. Use \"_start_\" to always align rows to the top of the list and \"_end_\" to align them bottom. Use \"_center_\" to align them in the middle of container. |\n| scrollToIndex     | Number                            |           | Row index to ensure visible (by forcefully scrolling if necessary)                                                                                                                                                                                                                                        |\n| scrollTop         | Number                            |           | Vertical offset                                                                                                                                                                                                                                                                                           |\n| sort              | Function                          |           | Sort function to be called if a sortable header is clicked. `({ defaultSortDirection: string, event: MouseEvent, sortBy: string, sortDirection: SortDirection }): void`                                                                                                                                   |\n| sortBy            | String                            |           | Data is currently sorted by this `dataKey` (if it is sorted at all)                                                                                                                                                                                                                                       |\n| sortDirection     | [SortDirection](SortDirection.md) |           | Data is currently sorted in this direction (if it is sorted at all)                                                                                                                                                                                                                                       |\n| style             | Object                            |           | Optional custom inline style to attach to root `Table` element.                                                                                                                                                                                                                                           |\n| tabIndex          | Number                            |           | Optional override of inner `Grid` tab index default; defaults to `0`.                                                                                                                                                                                                                                     |\n| width             | Number                            |     ✓     | Width of the table                                                                                                                                                                                                                                                                                        |\n\n### Public Methods\n\n##### forceUpdateGrid\n\nForcefully re-render the inner `Grid` component.\n\nCalling `forceUpdate` on `Table` may not re-render the inner `Grid` since it uses `shallowCompare` as a performance optimization.\nUse this method if you want to manually trigger a re-render.\nThis may be appropriate if the underlying row data has changed but the row sizes themselves have not.\n\n##### getOffsetForRow ({ alignment: ?string, index: ?number })\n\nGets offset for a given row and alignment.\n\n##### getScrollbarWidth\n\nGets the scrollbar width used to pad the table-header.\n\n##### measureAllRows\n\nPre-measure all rows in a `Table`.\n\nTypically rows are only measured as needed and estimated heights are used for cells that have not yet been measured.\nThis method ensures that the next call to getTotalSize() returns an exact size (as opposed to just an estimated one).\n\n##### recomputeRowHeights (index: number)\n\nRecompute row heights and offsets after the specified index (defaults to 0).\n\n`Table` has no way of knowing when its underlying list data has changed since it only receives a `rowHeight` property.\nIf the `rowHeight` is a number it can compare before and after values but if it is a function that comparison is error prone.\nIn the event that a dynamic `rowHeight` function is in use and the row heights have changed this function should be manually called by the \"smart\" container parent.\n\nThis method will also force a render cycle (via `forceUpdate`) to ensure that the updated measurements are reflected in the rendered table.\n\n##### scrollToPosition (scrollTop: number)\n\nScroll to the specified offset.\nUseful for animating position changes.\n\n##### scrollToRow (index: number)\n\nEnsure row is visible.\nThis method can be used to safely scroll back to a cell that a user has scrolled away from even if it was previously scrolled to.\n\n### Class names\n\nThe Table component supports the following static class names\n\n| Property                                          | Description                                 |\n| :------------------------------------------------ | :------------------------------------------ |\n| ReactVirtualized\\_\\_Table                         | Main (outer) element                        |\n| ReactVirtualized\\_\\_Table\\_\\_headerColumnn        | Header cell (similar to `thead > tr > th`)  |\n| ReactVirtualized\\_\\_Table\\_\\_headerRow            | Header row (similar to `thead > tr`)        |\n| ReactVirtualized\\_\\_Table\\_\\_row                  | Table row (akin to `tbody > tr`)            |\n| ReactVirtualized\\_\\_Table\\_\\_rowColumn            | Table column (akin to `tbody > tr > td`)    |\n| ReactVirtualized\\_\\_Table\\_\\_sortableHeaderColumn | Applied to header columns that are sortable |\n| ReactVirtualized\\_\\_Table\\_\\_sortableHeaderIcon   | SVG sort indicator                          |\n\n### headerRowRenderer\n\nThis is an advanced property.\nIt is useful for situations where you require additional hooks into `Table` to render additional custom UI elements.\nYou may want to start by forking the [`defaultTableHeaderRowRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultHeaderRowRenderer.js) function.\n\nThis function accepts the following named parameters:\n\n| Property  | Description          |\n| :-------- | :------------------- |\n| className | Header class name    |\n| columns   | Array of React nodes |\n| style     | Header style object  |\n\n### rowRenderer\n\nThis is an advanced property.\nIt is useful for situations where you require additional hooks into `Table` (eg integration with a library like `react-sortable-hoc`).\nIf you do override `rowRenderer` the easiest way is to decorate the default implementation like so:\n\n```jsx\nimport {SortableContainer, SortableElement} from 'react-sortable-hoc';\nimport {defaultTableRowRenderer, Table} from 'react-virtualized';\n\nconst SortableTable = SortableContainer(Table);\nconst SortableTableRowRenderer = SortableElement(defaultTableRowRenderer);\n\nfunction rowRenderer(props) {\n  return <SortableTableRowRenderer {...props} />;\n}\n\nfunction CustomizedTable(props) {\n  return <SortableTable rowRenderer={rowRenderer} {...props} />;\n}\n```\n\nIf you require greater customization, you may want to fork the [`defaultTableRowRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultRowRenderer.js) function.\n\nThis function accepts the following named parameters:\n\n| Property         | Description                                                    |\n| :--------------- | :------------------------------------------------------------- |\n| className        | Row-level class name                                           |\n| columns          | Array of React nodes                                           |\n| index            | Row index                                                      |\n| isScrolling      | Boolean flag indicating if `Table` is currently being scrolled |\n| onRowClick       | Optional row `onClick` handler                                 |\n| onRowDoubleClick | Optional row `onDoubleClick` handler                           |\n| onRowMouseOver   | Optional row `onMouseOver` handler                             |\n| onRowMouseOut    | Optional row `onMouseOut` handler                              |\n| rowData          | Row data                                                       |\n| style            | Row-level style object                                         |\n| key              | Unique key within array of rendered rows                       |\n\n### Examples\n\nBelow is a very basic `Table` example. This table has only 2 columns, each containing a simple string. Both have a fixed width and neither is sortable. [See here](../source/Table/Table.example.js) for a more full-featured example including custom cell renderers, sortable headers, and more.\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {Column, Table} from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\n// Table data as an array of objects\nconst list = [\n  {name: 'Brian Vaughn', description: 'Software engineer'},\n  // And so on...\n];\n\n// Render your table\nReactDOM.render(\n  <Table\n    width={300}\n    height={300}\n    headerHeight={20}\n    rowHeight={30}\n    rowCount={list.length}\n    rowGetter={({index}) => list[index]}>\n    <Column label=\"Name\" dataKey=\"name\" width={100} />\n    <Column width={200} label=\"Description\" dataKey=\"description\" />\n  </Table>,\n  document.getElementById('example'),\n);\n```\n"
  },
  {
    "path": "docs/WindowScroller.md",
    "content": "## WindowScroller\n\nA component that enables a `Table` or `List` component to be scrolled based on the window's scroll positions.\nThis can be used to create layouts similar to Facebook or Twitter news feeds.\n\n**Note** that this component does not currently work with a horizontally-scrolling `Grid` as horizontal scrolls reset the internal `scrollTop`.\nThis may change with a future release but for the time being this component should be used with `Table` or `List` only.\n\n### Prop Types\n\n| Property                   | Type     | Required? | Description                                                                                                                                                                                                                                                |\n| :------------------------- | :------- | :-------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| children                   | Function |     ✓     | Function responsible for rendering children. This function should implement the following signature: `({ height: number, width: number, isScrolling: boolean, scrollTop: number, registerChild: function, onChildScroll: function }) => PropTypes.element` |\n| onResize                   | Function |           | Callback to be invoked on-resize; it is passed the following named parameters: `({ height: number, width: number })`.                                                                                                                                      |\n| onScroll                   | Function |           | Callback to be invoked on-scroll; it is passed the following named parameters: `({ scrollTop: number, scrollLeft: number })`.                                                                                                                              |\n| scrollElement              | any      |           | Element to attach scroll event listeners. Defaults to `window`.                                                                                                                                                                                            |\n| scrollingResetTimeInterval | Number   |           | Wait this amount of time after the last scroll event before resetting WindowScroller `pointer-events`; defaults to 150ms.                                                                                                                                  |\n| serverHeight               | Number   |           | Height used for server-side rendering.                                                                                                                                                                                                                     |\n| serverWidth                | Number   |           | Width used for server-side rendering.                                                                                                                                                                                                                      |\n\n### Render Props\n\n| Property      | Type     | Description                                                                                                |\n| :------------ | :------- | :--------------------------------------------------------------------------------------------------------- |\n| height        | Number   | The height of the viewport.                                                                                |\n| isScrolling   | Boolean  | Indicates if the `Table` or `List` is scrolling                                                            |\n| onChildScroll | Function | Used by the `Table` or `List`'s `onScroll` prop to \"scroll\" the list                                       |\n| registerChild | Function | specify grid container deeper in layout (by default `WindowScroller` uses `ReactDOM.findDOMNode` function) |\n| scrollTop     | Number   | Scroll distance from the page                                                                              |\n\n### Public Methods\n\n##### updatePosition\n\nRecalculates scroll position from the top of page.\n\nThis method is automatically triggered when the component mounts as well as when the browser resizes. It should be manually called if the page header (eg any items in the DOM \"above\" the `WindowScroller`) resizes or changes.\n\n### Examples\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { List, WindowScroller } from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\nReactDOM.render(\n  <WindowScroller>\n    {({ height, isScrolling, onChildScroll, scrollTop }) => (\n      <List\n        autoHeight\n        height={height}\n        isScrolling={isScrolling}\n        onScroll={onChildScroll}\n        rowCount={...}\n        rowHeight={...}\n        rowRenderer={...}\n        scrollTop={scrollTop}\n        width={...}\n      />\n    )}\n  </WindowScroller>,\n  document.getElementById('example')\n);\n```\n\nusing `registerChild`\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { List, WindowScroller } from 'react-virtualized';\nimport 'react-virtualized/styles.css'; // only needs to be imported once\n\nReactDOM.render(\n  <WindowScroller>\n    {({ height, isScrolling, registerChild, scrollTop }) => (\n      <div>\n        <header>\n          Table header\n        </header>\n        <div ref={registerChild}>\n          <List\n            autoHeight\n            height={height}\n            isScrolling={isScrolling}\n            rowCount={...}\n            rowHeight={...}\n            rowRenderer={...}\n            scrollTop={scrollTop}\n            width={...}\n          />\n        </div>\n      </div>\n    )}\n  </WindowScroller>,\n  document.getElementById('example')\n);\n```\n"
  },
  {
    "path": "docs/creatingAnInfiniteLoadingList.md",
    "content": "## Creating an Infinite-Loading List\n\nThe `InfiniteLoader` component was created to help break large data sets down into chunks that could be just-in-time loaded as they were scrolled into view.\nIt can also be used to create an infinite-loading list (eg. Twitter or Facebook).\nHere's a basic example of how you might implement that:\n\n```jsx\nfunction MyComponent({\n  /** Are there more items to load? (This information comes from the most recent API request.) */\n  hasNextPage,\n  /** Are we currently loading a page of items? (This may be an in-flight flag in your Redux store for example.) */\n  isNextPageLoading,\n  /** List of items loaded so far */\n  list,\n  /** Callback function (eg. Redux action-creator) responsible for loading the next page of items */\n  loadNextPage,\n}) {\n  // If there are more items to be loaded then add an extra row to hold a loading indicator.\n  const rowCount = hasNextPage ? list.size + 1 : list.size;\n\n  // Only load 1 page of items at a time.\n  // Pass an empty callback to InfiniteLoader in case it asks us to load more than once.\n  const loadMoreRows = isNextPageLoading ? () => {} : loadNextPage;\n\n  // Every row is loaded except for our loading indicator row.\n  const isRowLoaded = ({index}) => !hasNextPage || index < list.size;\n\n  // Render a list item or a loading indicator.\n  const rowRenderer = ({index, key, style}) => {\n    let content;\n\n    if (!isRowLoaded({index})) {\n      content = 'Loading...';\n    } else {\n      content = list.getIn([index, 'name']);\n    }\n\n    return (\n      <div key={key} style={style}>\n        {content}\n      </div>\n    );\n  };\n\n  return (\n    <InfiniteLoader\n      isRowLoaded={isRowLoaded}\n      loadMoreRows={loadMoreRows}\n      rowCount={rowCount}>\n      {({onRowsRendered, registerChild}) => (\n        <List\n          ref={registerChild}\n          onRowsRendered={onRowsRendered}\n          rowRenderer={rowRenderer}\n          {...otherProps}\n        />\n      )}\n    </InfiniteLoader>\n  );\n}\n```\n"
  },
  {
    "path": "docs/customizingStyles.md",
    "content": "## Customizing Classes and Styles\n\nReact virtual CSS styles are split into their own, separately loaded stylesheet in order to simplify universal/isomorphic usage while also enabling styles to be customized. This stylesheet should be imported somewhere (usually during bootstrapping) like so:\n\n```js\nimport 'react-virtualized/styles.css';\n```\n\nYou can also customize component styles in any of the ways below:\n\n#### Forked Styles\n\nFork the react-virtualized `styles.css` file and load your own, totally custom styles.\n\n#### Global CSS\n\nLoad an additional, external CSS file that defines global classes (eg. `.ReactVirtualized__Table`, `.ReactVirtualized__Table__row`) to append to default inline styles.\n\nLearn more about which class names a component supports in the [API docs](https://github.com/bvaughn/react-virtualized/blob/master/docs/).\n\n#### CSS Modules\n\nIf you are using CSS modules you can specify custom class names to be appended to a component instance (eg. `Table` supports `className`, `headerClassName`, and `rowClassName` properties).\n\nLearn more about which class names are supported in the [API docs](https://github.com/bvaughn/react-virtualized/blob/master/docs/).\n\n#### Inline Styles\n\nreact-virtualized components support inline style props for style overrides.\n\nLearn more about which inline style properties are supported in the [API docs](https://github.com/bvaughn/react-virtualized/blob/master/docs/).\n"
  },
  {
    "path": "docs/multiColumnSortTable.md",
    "content": "By default, `Table` assumes that its data will be sorted by single attribute, in either ascending or descending order.\nFor advanced use cases, you may want to sort by multiple fields.\nThis can be accomplished using the `createMultiSort` utility.\n\n```jsx\nimport {createTableMultiSort, Column, Table} from 'react-virtualized';\n\nfunction sort({sortBy, sortDirection}) {\n  // 'sortBy' is an ordered Array of fields.\n  // 'sortDirection' is a map of field name to \"ASC\" or \"DESC\" directions.\n  // Sort your collection however you'd like.\n  // When you're done, setState() or update your Flux store, etc.\n}\n\nconst sortState = createMultiSort(sort);\n\n// When rendering your header columns,\n// Use the sort state exposed by sortState:\nconst headerRenderer = ({dataKey, label}) => {\n  const showSortIndicator = sortState.sortBy.includes(dataKey);\n  return (\n    <>\n      <span title={label}>{label}</span>\n      {showSortIndicator && (\n        <SortIndicator sortDirection={sortState.sortDirection[dataKey]} />\n      )}\n    </>\n  );\n};\n\n// Connect sortState to Table by way of the 'sort' prop:\n<Table\n  {...tableProps}\n  sort={sortState.sort}\n  sortBy={undefined}\n  sortDirection={undefined}>\n  <Column {...columnProps} headerRenderer={headerRenderer} />\n</Table>;\n```\n\nThe `createMultiSort` utility also accepts default sort-by values:\n\n```js\nconst sortState = createMultiSort(sort, {\n  defaultSortBy: ['firstName', 'lastName'],\n  defaultSortDirection: {\n    firstName: 'ASC',\n    lastName: 'ASC',\n  },\n});\n```\n"
  },
  {
    "path": "docs/overscanUsage.md",
    "content": "The `overscanRowCount` property renders additional rows in the direction the user is scrolling to reduce the chance of a user scrolling faster than virtualized content can be rendered. (See [here](https://bvaughn.github.io/forward-js-2017/#/23/1) for a visual example of why this property exists).\n\nThis property has performance implications though: the higher the value, the more work react-virtualized needs to do in reaction to each scroll event. (See [here](https://bvaughn.github.io/forward-js-2017/#/12/4) for a visual example of how windowing works.)\n\nI suggest using the default value (10) as a starting point and lowering it if possible. Setting the value too high will erase the performance gains achieved by using react-virtualized. You should aim to use the lowest value as possible that still generally avoids any empty space from appearing during normal scrolling actions.\n\nNote that the same advice applies to the `overscanColumnCount` as well.\n"
  },
  {
    "path": "docs/reverseList.md",
    "content": "## Displaying Items in Reverse Order\n\nSometimes it is desirable to display a list in reverse order.\nThe simplest way to do this is to add items to the front of the list (`unshift`) instead of the end (`push`).\nHere is a high level template for doing this:\n\n```jsx\nexport default class Example extends Component {\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      list: [],\n    };\n  }\n\n  componentDidMount() {\n    this._interval = setInterval(::this._updateFeed, 500);\n  }\n\n  componentWillUnmount() {\n    clearInterval(this._interval);\n  }\n\n  render() {\n    const {list} = this.state;\n\n    return (\n      <div className={styles.ListExample}>\n        <List\n          ref=\"List\"\n          className={styles.List}\n          width={300}\n          height={200}\n          rowHeight={60}\n          rowCount={list.length}\n          rowRenderer={::this._rowRenderer}\n        />\n      </div>\n    );\n  }\n\n  _updateFeed() {\n    const list = [...this.state.list];\n\n    list\n      .unshift\n      // Add new item here\n      ();\n\n    this.setState({list});\n\n    // If you want to scroll to the top you can do it like this\n    this.refs.List.scrollToRow(0);\n  }\n\n  _rowRenderer({key, index}) {\n    return (\n      <div key={key} style={style}>\n        {/* Your content goes here */}\n      </div>\n    );\n  }\n}\n```\n\nYou can see a demo of this [here](https://s3.amazonaws.com/brianvaughn/react-virtualized/reverse-list/index.html).\n"
  },
  {
    "path": "docs/tableWithNaturalSort.md",
    "content": "By default, `Table` assumes the sortable rows can be sorted in ascending or descending order.\nClicking a column header the first time will trigger the `sort` callback with a suggested ordering of _ascending_.\nClicking it a second time will call `sort` with a suggested ordering of _descending_.\nClicking a third time will suggest _ascending_.\n\nThis is often desirable but it has a subtle side effect:\nOnce you have sorted a `Table` there is no way to return to an unsorted, \"natural\" order.\n\nFortunately `Table` makes it easy for you to implement this functionality within your application code like so:\n\n```jsx\nexport default class NaturalSortTable extends Component {\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      list: props.list, // Naturally sorted list\n    };\n  }\n\n  componentWillUpdate(nextProps, nextState) {\n    const {sortBy: prevSortBy, sortDirection: prevSortDirection} = this.state;\n\n    if (\n      nextState.sortBy !== prevSortBy ||\n      nextState.sortDirection !== prevSortDirection\n    ) {\n      const {sortBy, sortDirection} = nextState;\n\n      let {list} = this.props;\n\n      if (sortBy) {\n        list = list.sortBy(item => item[sortBy]);\n        if (sortDirection === SortDirection.DESC) {\n          list = list.reverse();\n        }\n      }\n    }\n  }\n\n  render() {\n    const {list, sortBy, sortDirection} = this.state;\n\n    return (\n      <Table\n        {...this.props}\n        sort={this._sort}\n        sortBy={sortBy}\n        sortDirection={sortDirection}>\n        {/* <Column>s go here */}\n      </Table>\n    );\n  }\n\n  _sort({sortBy, sortDirection}) {\n    const {sortBy: prevSortBy, sortDirection: prevSortDirection} = this.state;\n\n    // If list was sorted DESC by this column.\n    // Rather than switch to ASC, return to \"natural\" order.\n    if (prevSortDirection === SortDirection.DESC) {\n      sortBy = null;\n      sortDirection = null;\n    }\n\n    this.setState({sortBy, sortDirection});\n  }\n}\n```\n"
  },
  {
    "path": "docs/upgrades/Version8.md",
    "content": "Before starting, take a quick look at the [Version 8 Roadmap](https://github.com/bvaughn/react-virtualized/issues/386) to familiarize yourself with what has changed.\n\n### Step 1: Rename components (`FlexColumn`, `FlexTable`, `VirtualScroll`)\n\nThe simplest way to update component names is to use the provided codemod:\n\n```sh\ncd /path/to/your/project\n\n# Install jscodeshift (if you have not already)\nnpm i jscodeshift\n\n# Shallow checkout react-virtualized\n# Codemods aren't stored in NPM (to keep the download small)\ngit clone git@github.com:bvaughn/react-virtualized.git --depth 1\n\n# Run the rename migration on your project's source code\njscodeshift -t ./react-virtualized/codemods/7-to-8/rename-components.js ./source\n\n# Remove the shallow checkout\nrm -rf ./react-virtualized\n```\n\n### Step 2: Update cell and row renderers\n\nRenderers in version 8 must handle new `style` & `key` params. This enables react-virtualized to remove one layer of DOM nodes which in turn makes things faster.\n\nDue to the complexity of this change there is no codemod. However you can use the following helper function to adapt your version 7 renderers to version 8 and allow you to update them one at a time:\n\n```jsx\n// Can be used for Grid, List, or Table\nfunction createCellRenderer(cellRenderer) {\n  console.warn('cellRenderer udpate needed');\n\n  return function cellRendererWrapper({key, style, ...rest}) {\n    return (\n      <div className=\"Grid__cell\" key={key} style={style}>\n        {cellRenderer(rest)}\n      </div>\n    );\n  };\n}\n\n// Demonstrates example usage\nfunction renderGrid(props) {\n  const {cellRenderer, ...rest} = props;\n\n  return <Grid cellRenderer={createCellRenderer(cellRenderer)} {...rest} />;\n}\n```\n\n### Step 3: Update cell/row CSS class names and styles\n\nRemoving the DOM wrapper for cells (and rows) means that you can now fully control a cell's styles.\nHowever this means that you'll need to audit your code to ensure that you've migrated the following properties:\n\n| Component       | Properties to be removed                 |\n| :-------------- | :--------------------------------------- |\n| `Grid`          | `cellClassName`, `cellStyle`             |\n| `VirtualScroll` | `rowClassName`, `rowStyle`               |\n| `FlexTable`     | `rowWrapperClassName`, `rowWrapperStyle` |\n\nHere is an example:\n\n```jsx\n// Before\nfunction renderGrid (props) {\n  return (\n    <Grid\n      cellClassName='yourCustomClass'\n      cellStyle={yourCustomStyle}\n      cellRenderer={\n        ({ columnIndex, rowIndex } => (\n          <div>\n            {list[rowIndex][columnIndex]}\n          </div>\n        ))\n      }\n      {...props}\n    />\n  )\n}\n\n// After\nfunction renderGrid (props) {\n  return (\n    <Grid\n      cellRenderer={\n        ({ columnIndex, key, rowIndex, style } => (\n          <div\n            className='yourCustomClass'\n            key={key}\n            style={{\n              ...style,\n              ...yourCustomStyle\n            }}\n          >\n            {list[rowIndex][columnIndex]}\n          </div>\n        ))\n      }\n      {...props}\n    />\n  )\n}\n```\n\n### Step 4: Update CSS class names\n\nTypically this step will not require any action. However if you have customized react-virtualized's global styles, you'll need to be update your overrides as follows:\n\n1. Add the prefix: \\_ReactVirtualized\\_\\_\\_.\n2. Replace _FlexTable_ with _Table_ and _VirtualScroll_ with _List_.\n\n```css\n/* before */\n.Collection,\n.FlexTable,\n.Grid,\n.VirtualScroll {\n}\n\n/* after */\n.ReactVirtualized__Collection,\n.ReactVirtualized__Grid,\n.ReactVirtualized__List,\n.ReactVirtualized__Table {\n}\n```\n\nThe `Grid__cell` CSS class has been removed _entirely_ as a result of the renderer changes (mentioned above). Scan your code for any references to them and adjust as necessary. (Note that the above suggested `cellRenderer` helper function will temporarily preserve the `Grid__cell` class to make migration easier.)\n"
  },
  {
    "path": "docs/usingAutoSizer.md",
    "content": "## Using AutoSizer\n\nThe `AutoSizer` component decorates a React element and automatically manages `width` and `height` properties so that decorated element fills the available space. This simplifies usage of components like `Grid`, `Table`, and `List` that require explicit dimensions.\n\nThis guide covers a few of the most commonly asked questions about using the component.\n\n### Observation\n\nThis component uses [`javascript-detect-element-resize`](https://github.com/sdecima/javascript-detect-element-resize) algorithm, and it does a little direct DOM manipulation to its parent, outside React's VirtualDOM.\n\nIf the parent has style `position: static` (default value), it changes to `position: relative`. It also injects a sibling `div` for size measuring.\n\n#### Why is my `AutoSizer` setting a height of 0?\n\n`AutoSizer` expands to _fill_ its parent but it will not _stretch_ the parent.\nThis is done to prevent problems with flexbox layouts.\nIf `AutoSizer` is reporting a height (or width) of 0- then it's likely that the parent element (or one of its parents) has a height of 0.\nOne easy way to test this is to add a style property (eg `background-color: red;`) to the parent to ensure that it is the correct size.\n(eg You may need to add `height: 100%` or `flex: 1` to the parent.)\n\n#### Can I use AutoSizer to manage only width or height (not both)?\n\nYou can use `AutoSizer` to control only one dimension of its child component using the `disableHeight` or `disableWidth` attributes. For example, a fixed-height component that should grow to fill the available width can be created like so:\n\n```jsx\n<AutoSizer disableHeight>\n  {({width}) => <Component height={200} width={width} {...props} />}\n</AutoSizer>\n```\n\n#### Can I use AutoSizer within a flex container?\n\nWhen using an `AutoSizer` as a direct child of a flex box it usually works out best to wrap it with a div, like so:\n\n```jsx\n<div style={{ display: 'flex' }}>\n  <!-- Other children... -->\n  <div style={{ flex: '1 1 auto' }}>\n    <AutoSizer>\n      {({ height, width }) => (\n        <Component\n          width={width}\n          height={height}\n          {...props}\n        />\n      )}\n    </AutoSizer>\n  </div>\n</div>\n```\n\n#### Can I use AutoSizer with other HOCs like InfiniteLoader?\n\n`AutoSizer` can be used within other react-virtualized HOCs such as `InfiniteLoader` or `ScrollSync` like so:\n\n```jsx\n<InfiniteLoader {...infiniteLoaderProps}>\n  {({onRowsRendered, registerChild}) => (\n    <AutoSizer>\n      {({height, width}) => (\n        <List\n          ref={registerChild}\n          width={width}\n          height={height}\n          onRowsRendered={onRowsRendered}\n          {...listProps}\n        />\n      )}\n    </AutoSizer>\n  )}\n</InfiniteLoader>\n```\n\nYou can see an example of this [here](https://bvaughn.github.io/react-virtualized/#/components/InfiniteLoader).\n\n### Applying Content Security Policy\n\n[The specification of Content Security Policy](https://www.w3.org/TR/2016/REC-CSP2-20161215/#intro)\ndescribes as the following:\n\n> This document defines Content Security Policy, a mechanism web applications\n> can use to mitigate a broad class of content injection vulnerabilities, such\n> as cross-site scripting (XSS).\n\nTo apply Content Security Policy, pass a `nonce` to _react-virtualized_ and add a matching `nonce-source` to the `Content-Security-Policy` field in HTTP header.\n"
  },
  {
    "path": "index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>react-virtualized</title>\n    <meta charset=\"utf-8\">\n    <meta content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\" name=\"viewport\" />\n    <link type=\"image/x-icon\" rel=\"shortcut icon\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"https://unpkg.com/codemirror@5.17.0/lib/codemirror.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"https://codemirror.net/theme/dracula.css\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "jest-puppeteer.config.js",
    "content": "module.exports = {\n  launch: {\n    headless: process.env.HEADLESS !== 'false',\n    devtools: process.env.DEVTOOLS === 'true',\n  },\n};\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  globalSetup: 'jest-environment-puppeteer/setup',\n  globalTeardown: 'jest-environment-puppeteer/teardown',\n  setupFiles: ['./source/jest-setup.js'],\n  roots: ['./source'],\n  coverageReporters: ['lcov'],\n  collectCoverageFrom: [\n    'source/**/*.js',\n    '!source/vendor/**',\n    '!source/demo/**',\n    '!source/jest-*.js',\n    '!source/TestUtils.js',\n    '!**/*.example.js',\n  ],\n  testRegex: '.(jest|e2e|ssr).js$',\n  verbose: true,\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-virtualized\",\n  \"description\": \"React components for efficiently rendering large, scrollable lists and tabular data\",\n  \"author\": \"Brian Vaughn <brian.david.vaughn@gmail.com>\",\n  \"user\": \"bvaughn\",\n  \"version\": \"9.22.6\",\n  \"homepage\": \"https://github.com/bvaughn/react-virtualized\",\n  \"main\": \"dist/commonjs/index.js\",\n  \"module\": \"dist/es/index.js\",\n  \"jsnext:main\": \"dist/es/index.js\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"build:types\": \"flow-copy-source --ignore \\\"**/*.{jest,e2e,ssr,example}.js\\\" source/WindowScroller dist/es/WindowScroller && flow-copy-source --ignore \\\"**/*.{jest,e2e,ssr,example}.js\\\" source/AutoSizer dist/es/AutoSizer\",\n    \"build\": \"yarn run build:commonjs && yarn run build:css && yarn run build:es && yarn run build:demo && yarn run build:umd\",\n    \"build:commonjs\": \"yarn run clean:commonjs && cross-env NODE_ENV=commonjs babel source --out-dir dist/commonjs\",\n    \"build:css\": \"postcss source/styles.css -o styles.css --use autoprefixer\",\n    \"build:demo\": \"yarn run clean:demo && cross-env NODE_ENV=production webpack --config webpack.config.demo.js -p --bail\",\n    \"build:es\": \"yarn run clean:es && yarn run build:types && cross-env NODE_ENV=es babel source --out-dir dist/es\",\n    \"build:umd\": \"yarn run clean:umd && cross-env NODE_ENV=rollup rollup -c\",\n    \"clean\": \"yarn run clean:commonjs && yarn run clean:demo && yarn run clean:es && yarn run clean:umd\",\n    \"clean:commonjs\": \"rimraf dist/commonjs\",\n    \"clean:demo\": \"rimraf build\",\n    \"clean:es\": \"rimraf dist/es\",\n    \"clean:umd\": \"rimraf dist/umd\",\n    \"deploy\": \"gh-pages -d build\",\n    \"lint\": \"eslint 'source/**/*.js'\",\n    \"typecheck\": \"flow check\",\n    \"prettier\": \"prettier --write '{playground,source,docs}/**/*.{js,md}'\",\n    \"prettier:diff\": \"prettier --list-different '{playground,source,docs}/**/*.{js,md}'\",\n    \"postpublish\": \"yarn run deploy\",\n    \"prepublishOnly\": \"yarn run build\",\n    \"start\": \"cross-env NODE_ENV=development webpack-dev-server --hot --config webpack.config.dev.js\",\n    \"test\": \"yarn run test:jest\",\n    \"test:jest\": \"jest --no-watchman --runInBand\",\n    \"test:coverage\": \"jest --no-watchman --maxWorkers 2 --coverage && codecov\",\n    \"watch\": \"watch 'clear && yarn run test -s' source\",\n    \"watch:jest\": \"jest --no-watchman --watch\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"pretty-quick --staged\"\n    }\n  },\n  \"files\": [\n    \"dist\",\n    \"styles.css\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/bvaughn/react-virtualized.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"reactjs\",\n    \"react-component\",\n    \"virtual\",\n    \"list\",\n    \"scrolling\",\n    \"infinite\",\n    \"virtualized\",\n    \"table\",\n    \"fixed\",\n    \"header\",\n    \"flex\",\n    \"flexbox\",\n    \"grid\",\n    \"spreadsheet\"\n  ],\n  \"bugs\": {\n    \"url\": \"https://github.com/bvaughn/react-virtualized/issues\"\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.7.0\",\n    \"@babel/core\": \"^7.7.2\",\n    \"@babel/plugin-external-helpers\": \"^7.2.0\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.7.0\",\n    \"@babel/plugin-transform-flow-comments\": \"^7.12.13\",\n    \"@babel/plugin-transform-modules-commonjs\": \"^7.7.0\",\n    \"@babel/plugin-transform-runtime\": \"^7.6.2\",\n    \"@babel/polyfill\": \"^7.7.0\",\n    \"@babel/preset-env\": \"^7.7.1\",\n    \"@babel/preset-flow\": \"^7.0.0\",\n    \"@babel/preset-react\": \"^7.7.0\",\n    \"@babel/preset-stage-2\": \"^7.0.0\",\n    \"autoprefixer\": \"^9.7.1\",\n    \"babel-eslint\": \"^10.0.3\",\n    \"babel-jest\": \"^24.9.0\",\n    \"babel-loader\": \"8.0.6\",\n    \"babel-plugin-flow-react-proptypes\": \"^26.0.0\",\n    \"babel-plugin-transform-react-remove-prop-types\": \"^0.4.24\",\n    \"codecov\": \"^3.6.1\",\n    \"codemirror\": \"^5.49.2\",\n    \"cross-env\": \"^6.0.3\",\n    \"css-loader\": \"^3.2.0\",\n    \"eslint\": \"^6.6.0\",\n    \"eslint-config-fbjs\": \"^3.1.1\",\n    \"eslint-config-prettier\": \"^6.5.0\",\n    \"eslint-config-react\": \"^1.1.7\",\n    \"eslint-plugin-babel\": \"^5.3.0\",\n    \"eslint-plugin-flowtype\": \"^4.3.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.2.3\",\n    \"eslint-plugin-prettier\": \"^3.1.1\",\n    \"eslint-plugin-react\": \"^7.16.0\",\n    \"eslint-plugin-relay\": \"^1.3.12\",\n    \"extract-text-webpack-plugin\": \"^3.0.2\",\n    \"file-loader\": \"^4.2.0\",\n    \"flow-bin\": \"^0.111.3\",\n    \"flow-copy-source\": \"^2.0.8\",\n    \"gh-pages\": \"^2.1.1\",\n    \"html-webpack-plugin\": \"^3.2.0\",\n    \"husky\": \"^3.0.9\",\n    \"immutable\": \"^4.0.0-rc.12\",\n    \"jest\": \"^24.9.0\",\n    \"jest-environment-puppeteer\": \"^4.3.0\",\n    \"lint-staged\": \"^9.4.2\",\n    \"postcss\": \"^7.0.21\",\n    \"postcss-cli\": \"^6.1.3\",\n    \"postcss-loader\": \"^3.0.0\",\n    \"prettier\": \"1.19.1\",\n    \"pretty-quick\": \"^2.0.1\",\n    \"puppeteer\": \"^2.0.0\",\n    \"react\": \"^17.0.1\",\n    \"react-codemirror\": \"^1.0.0\",\n    \"react-dom\": \"^17.0.1\",\n    \"react-router\": \"^5.2.0\",\n    \"react-router-dom\": \"^5.2.0\",\n    \"react-test-renderer\": \"^17.0.1\",\n    \"rimraf\": \"^6.0.1\",\n    \"rollup\": \"^1.26.5\",\n    \"rollup-plugin-babel\": \"^4.3.3\",\n    \"rollup-plugin-commonjs\": \"^10.1.0\",\n    \"rollup-plugin-node-resolve\": \"^5.2.0\",\n    \"rollup-plugin-replace\": \"^2.2.0\",\n    \"rollup-plugin-uglify\": \"^6.0.3\",\n    \"style-loader\": \"^1.0.0\",\n    \"watch\": \"^1.0.2\",\n    \"webpack\": \"^4.41.2\",\n    \"webpack-cli\": \"^3.3.10\",\n    \"webpack-dev-server\": \"^3.9.0\"\n  },\n  \"dependencies\": {\n    \"@babel/runtime\": \"^7.7.2\",\n    \"clsx\": \"^1.0.4\",\n    \"dom-helpers\": \"^5.1.3\",\n    \"loose-envify\": \"^1.4.0\",\n    \"prop-types\": \"^15.7.2\",\n    \"react-lifecycles-compat\": \"^3.0.4\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\n    \"react-dom\": \"^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\n  },\n  \"browserify\": {\n    \"transform\": [\n      \"loose-envify\"\n    ]\n  }\n}\n"
  },
  {
    "path": "playground/chat-no-resize.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"initial-scale=1, maximum-scale=1\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n      font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n      font-size: 12px;\n      overflow: hidden\n    }\n    .container {\n      width: 100%;\n      height: 100%;\n    }\n    .chat {\n      border: 1px solid #eee;\n      border-radius: 0.5rem;\n    }\n    .item {\n      border-bottom: 1px solid #eee;\n      padding: 0.5rem;\n      box-sizing: border-box;\n    }\n    .item:hover {\n      background-color: #eee;\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"utils.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('chat-no-resize.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/chat-no-resize.js",
    "content": "var cache = new ReactVirtualized.CellMeasurerCache({\n  fixedWidth: true,\n});\n\nvar list;\nvar mostRecentWidth;\n\nfunction rowRenderer(params) {\n  var datum = chatHistory[params.index];\n\n  return React.createElement(\n    ReactVirtualized.CellMeasurer,\n    {\n      cache: cache,\n      columnIndex: 0,\n      key: params.key,\n      parent: params.parent,\n      rowIndex: params.index,\n      width: mostRecentWidth,\n    },\n    React.createElement(\n      'div',\n      {\n        className: 'item',\n        key: params.key,\n        style: params.style,\n      },\n      React.createElement('strong', null, datum.name),\n      ':',\n      datum.text,\n    ),\n  );\n}\n\nfunction cellRenderer(params) {\n  params.index = params.rowIndex;\n\n  return rowRenderer(params);\n}\n\nvar App = React.createClass({\n  render: function() {\n    return React.createElement(\n      'div',\n      {\n        className: 'container',\n      },\n      React.createElement(ReactVirtualized.AutoSizer, {}, function(\n        autoSizerParams,\n      ) {\n        if (mostRecentWidth && mostRecentWidth !== autoSizerParams.width) {\n          cache.clearAll();\n          list.recomputeRowHeights();\n        }\n\n        mostRecentWidth = autoSizerParams.width;\n\n        return React.createElement(ReactVirtualized.List, {\n          className: 'chat',\n          deferredMeasurementCache: cache,\n          height: autoSizerParams.height,\n          ref: function(ref) {\n            list = ref;\n          },\n          rowCount: chatHistory.length,\n          rowHeight: cache.rowHeight,\n          rowRenderer: rowRenderer,\n          width: autoSizerParams.width,\n        });\n      }),\n    );\n  },\n});\n\nvar NAMES = [\n  'Peter Brimer',\n  'Tera Gaona',\n  'Kandy Liston',\n  'Lonna Wrede',\n  'Kristie Yard',\n  'Raul Host',\n  'Yukiko Binger',\n  'Velvet Natera',\n  'Donette Ponton',\n  'Loraine Grim',\n  'Shyla Mable',\n  'Marhta Sing',\n  'Alene Munden',\n  'Holley Pagel',\n  'Randell Tolman',\n  'Wilfred Juneau',\n  'Naida Madson',\n  'Marine Amison',\n  'Glinda Palazzo',\n  'Lupe Island',\n  'Cordelia Trotta',\n  'Samara Berrier',\n  'Era Stepp',\n  'Malka Spradlin',\n  'Edward Haner',\n  'Clemencia Feather',\n  'Loretta Rasnake',\n  'Dana Hasbrouck',\n  'Sanda Nery',\n  'Soo Reiling',\n  'Apolonia Volk',\n  'Liliana Cacho',\n  'Angel Couchman',\n  'Yvonne Adam',\n  'Jonas Curci',\n  'Tran Cesar',\n  'Buddy Panos',\n  'Rosita Ells',\n  'Rosalind Tavares',\n  'Renae Keehn',\n  'Deandrea Bester',\n  'Kelvin Lemmon',\n  'Guadalupe Mccullar',\n  'Zelma Mayers',\n  'Laurel Stcyr',\n  'Edyth Everette',\n  'Marylin Shevlin',\n  'Hsiu Blackwelder',\n  'Mark Ferguson',\n  'Winford Noggle',\n  'Shizuko Gilchrist',\n  'Roslyn Cress',\n  'Nilsa Lesniak',\n  'Agustin Grant',\n  'Earlie Jester',\n  'Libby Daigle',\n  'Shanna Maloy',\n  'Brendan Wilken',\n  'Windy Knittel',\n  'Alice Curren',\n  'Eden Lumsden',\n  'Klara Morfin',\n  'Sherryl Noack',\n  'Gala Munsey',\n  'Stephani Frew',\n  'Twana Anthony',\n  'Mauro Matlock',\n  'Claudie Meisner',\n  'Adrienne Petrarca',\n  'Pearlene Shurtleff',\n  'Rachelle Piro',\n  'Louis Cocco',\n  'Susann Mcsweeney',\n  'Mandi Kempker',\n  'Ola Moller',\n  'Leif Mcgahan',\n  'Tisha Wurster',\n  'Hector Pinkett',\n  'Benita Jemison',\n  'Kaley Findley',\n  'Jim Torkelson',\n  'Freda Okafor',\n  'Rafaela Markert',\n  'Stasia Carwile',\n  'Evia Kahler',\n  'Rocky Almon',\n  'Sonja Beals',\n  'Dee Fomby',\n  'Damon Eatman',\n  'Alma Grieve',\n  'Linsey Bollig',\n  'Stefan Cloninger',\n  'Giovanna Blind',\n  'Myrtis Remy',\n  'Marguerita Dostal',\n  'Junior Baranowski',\n  'Allene Seto',\n  'Margery Caves',\n  'Nelly Moudy',\n  'Felix Sailer',\n];\nvar SENTENCES = [\n  'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',\n  'Phasellus vulputate odio commodo tortor sodales, et vehicula ipsum viverra.',\n  'In et mollis velit, accumsan volutpat libero.',\n  'Nulla rutrum tellus ipsum, eget fermentum sem dictum quis.',\n  'Suspendisse eget vehicula elit.',\n  'Proin ut lacus lacus.',\n  'Aliquam erat volutpat.',\n  'Vivamus ac suscipit est, et elementum lectus.',\n  'Cras tincidunt nisi in urna molestie varius.',\n  'Integer in magna eu nibh imperdiet tristique.',\n  'Curabitur eu pellentesque nisl.',\n  'Etiam non consequat est.',\n  'Duis mi massa, feugiat nec molestie sit amet, suscipit et metus.',\n  'Curabitur ac enim dictum arcu varius fermentum vel sodales dui.',\n  'Ut tristique augue at congue molestie.',\n  'Integer semper sem lorem, scelerisque suscipit lacus consequat nec.',\n  'Etiam euismod efficitur magna nec dignissim.',\n  'Morbi vel neque lectus.',\n  'Etiam ac accumsan elit, et pharetra ex.',\n  'Suspendisse vitae gravida mauris.',\n  'Pellentesque sed laoreet erat.',\n  'Nam aliquet purus quis massa eleifend, et efficitur felis aliquam.',\n  'Fusce faucibus diam erat, sed consectetur urna auctor at.',\n  'Praesent et nulla velit.',\n  'Cras eget enim nec odio feugiat tristique eu quis ante.',\n  'Morbi blandit diam vitae odio sollicitudin finibus.',\n  'Integer ac ante fermentum, placerat orci vel, fermentum lacus.',\n  'Maecenas est elit, semper ut posuere et, congue ut orci.',\n  'Phasellus eget enim vitae nunc luctus sodales a eu erat.',\n  'Curabitur dapibus nisi sed nisi dictum, in imperdiet urna posuere.',\n  'Vivamus commodo odio metus, tincidunt facilisis augue dictum quis.',\n  'Curabitur sagittis a lectus ac sodales.',\n  'Nam eget eros purus.',\n  'Nam scelerisque et ante in porta.',\n  'Proin vitae augue tristique, malesuada nisl ut, fermentum nisl.',\n  'Nulla bibendum quam id velit blandit dictum.',\n  'Cras tempus ac dolor ut convallis.',\n  'Sed vel ipsum est.',\n  'Nulla ut leo vestibulum, ultricies sapien ac, pellentesque dolor.',\n  'Etiam ultricies maximus tempus.',\n  'Donec dignissim mi ac libero feugiat, vitae lacinia odio viverra.',\n  'Curabitur condimentum tellus sit amet neque posuere, condimentum tempus purus eleifend.',\n  'Donec tempus, augue id hendrerit pretium, mauris leo congue nulla, ac iaculis erat nunc in dolor.',\n  'Praesent vel lectus venenatis, elementum mauris vitae, ullamcorper nulla.',\n  'Maecenas non diam cursus, imperdiet massa eget, pellentesque ex.',\n  'Vestibulum luctus risus vel augue auctor blandit.',\n  'Nullam augue diam, pulvinar sed sapien et, hendrerit venenatis risus.',\n  'Quisque sollicitudin nulla nec tellus feugiat hendrerit.',\n  'Vestibulum a eros accumsan, lacinia eros non, pretium diam.',\n  'Aenean iaculis augue sit amet scelerisque aliquam.',\n  'Donec ornare felis et dui hendrerit, eget bibendum nibh interdum.',\n  'Maecenas tellus magna, tristique vitae orci vel, auctor tincidunt nisi.',\n  'Fusce non libero quis velit porttitor maximus at eget enim.',\n  'Sed in aliquet tellus.',\n  'Etiam a tortor erat.',\n  'Donec nec diam vel tellus egestas lobortis.',\n  'Vivamus dictum erat nulla, sit amet accumsan dolor scelerisque eu.',\n  'In nec eleifend ex, pellentesque dapibus sapien.',\n  'Duis a mollis nisi.',\n  'Sed ornare nisl sit amet dolor pellentesque, eu fermentum leo interdum.',\n  'Sed eget mauris condimentum, molestie justo eu, feugiat felis.',\n  'Nunc suscipit leo non dui blandit, ac malesuada ex consequat.',\n  'Morbi varius placerat congue.',\n  'Praesent id velit in nunc elementum aliquet.',\n  'Sed luctus justo vitae nibh bibendum blandit.',\n  'Sed et sapien turpis.',\n  'Nulla ac eros vestibulum, mollis ante eu, rutrum nulla.',\n  'Sed cursus magna ut vehicula rutrum.',\n  'Ut consectetur feugiat consectetur.',\n  'Nulla nec ligula posuere neque sollicitudin rutrum a a dui.',\n  'Nulla ut quam odio.',\n  'Integer dignissim sapien et orci sodales volutpat.',\n  'Nullam a sapien leo.',\n  'Praesent cursus semper purus, vitae gravida risus dapibus mattis.',\n  'Sed pellentesque nulla lorem, in commodo arcu feugiat sed.',\n  'Phasellus blandit arcu non diam varius ornare.',\n];\nvar chatHistory = [];\n\nfor (var i = 0; i < 1000; i++) {\n  var name = NAMES[Math.floor(Math.random() * NAMES.length)];\n  var sentences = Math.ceil(Math.random() * 5);\n  var texts = [];\n\n  for (var x = 0; x < sentences; x++) {\n    texts.push(SENTENCES[Math.floor(Math.random() * SENTENCES.length)]);\n  }\n\n  chatHistory.push({\n    name,\n    text: texts.join(' '),\n  });\n}\n\nconst container = document.getElementById('mount');\n\nReactDOM.render(React.createElement(App), container);\n"
  },
  {
    "path": "playground/chat.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"initial-scale=1, maximum-scale=1\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n      font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n      font-size: 12px;\n      overflow: hidden\n    }\n    .container {\n      width: 100%;\n      height: 100%;\n    }\n    .chat {\n      border: 1px solid #eee;\n      border-radius: 0.5rem;\n    }\n    .item {\n      border-bottom: 1px solid #eee;\n      padding: 0.5rem;\n      box-sizing: border-box;\n    }\n    .item:hover {\n      background-color: #eee;\n    }\n\n    .floatingMessage {\n      position: absolute;\n      top: 1.5rem;\n      left: 50%;\n      transform: translateX(-50%);\n      background-color: rgba(0, 255, 0, 0.5);\n      border-radius: 0.25rem;\n      padding: 0.5rem 1rem;\n      opacity: 0;\n      -webkit-animation: floatingMessageAnmation 4s 1;\n      animation: floatingMessageAnimation 4s 1;\n    }\n\n    @keyframes floatingMessageAnimation {\n      0% {\n        opacity: 1;\n      }\n      50% {\n        opacity: 1;\n      }\n      100% {\n        opacity: 0;\n      }\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"utils.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('chat.js');\n  </script>\n\n  <div class='floatingMessage'>\n    Click body to resize ...\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "playground/chat.js",
    "content": "var cache = new ReactVirtualized.CellMeasurerCache({\n  fixedWidth: true,\n});\n\nvar list;\nvar mostRecentWidth;\n\nfunction rowRenderer(params) {\n  var datum = chatHistory[params.index];\n\n  return React.createElement(\n    ReactVirtualized.CellMeasurer,\n    {\n      cache: cache,\n      columnIndex: 0,\n      key: params.key,\n      parent: params.parent,\n      rowIndex: params.index,\n      width: mostRecentWidth,\n    },\n    React.createElement(\n      'div',\n      {\n        className: 'item',\n        key: params.key,\n        style: params.style,\n      },\n      React.createElement('strong', null, datum.name),\n      ':',\n      datum.text,\n    ),\n  );\n}\n\nfunction cellRenderer(params) {\n  params.index = params.rowIndex;\n\n  return rowRenderer(params);\n}\n\nvar App = React.createClass({\n  render: function() {\n    return React.createElement(\n      'div',\n      {\n        className: 'container',\n      },\n      React.createElement(ReactVirtualized.AutoSizer, {}, function(\n        autoSizerParams,\n      ) {\n        if (mostRecentWidth && mostRecentWidth !== autoSizerParams.width) {\n          cache.clearAll();\n          list.recomputeRowHeights();\n        }\n\n        mostRecentWidth = autoSizerParams.width;\n\n        return React.createElement(ReactVirtualized.List, {\n          className: 'chat',\n          deferredMeasurementCache: cache,\n          height: autoSizerParams.height,\n          ref: function(ref) {\n            list = ref;\n          },\n          rowCount: chatHistory.length,\n          rowHeight: cache.rowHeight,\n          rowRenderer: rowRenderer,\n          width: autoSizerParams.width,\n        });\n      }),\n    );\n  },\n});\n\nvar NAMES = [\n  'Peter Brimer',\n  'Tera Gaona',\n  'Kandy Liston',\n  'Lonna Wrede',\n  'Kristie Yard',\n  'Raul Host',\n  'Yukiko Binger',\n  'Velvet Natera',\n  'Donette Ponton',\n  'Loraine Grim',\n  'Shyla Mable',\n  'Marhta Sing',\n  'Alene Munden',\n  'Holley Pagel',\n  'Randell Tolman',\n  'Wilfred Juneau',\n  'Naida Madson',\n  'Marine Amison',\n  'Glinda Palazzo',\n  'Lupe Island',\n  'Cordelia Trotta',\n  'Samara Berrier',\n  'Era Stepp',\n  'Malka Spradlin',\n  'Edward Haner',\n  'Clemencia Feather',\n  'Loretta Rasnake',\n  'Dana Hasbrouck',\n  'Sanda Nery',\n  'Soo Reiling',\n  'Apolonia Volk',\n  'Liliana Cacho',\n  'Angel Couchman',\n  'Yvonne Adam',\n  'Jonas Curci',\n  'Tran Cesar',\n  'Buddy Panos',\n  'Rosita Ells',\n  'Rosalind Tavares',\n  'Renae Keehn',\n  'Deandrea Bester',\n  'Kelvin Lemmon',\n  'Guadalupe Mccullar',\n  'Zelma Mayers',\n  'Laurel Stcyr',\n  'Edyth Everette',\n  'Marylin Shevlin',\n  'Hsiu Blackwelder',\n  'Mark Ferguson',\n  'Winford Noggle',\n  'Shizuko Gilchrist',\n  'Roslyn Cress',\n  'Nilsa Lesniak',\n  'Agustin Grant',\n  'Earlie Jester',\n  'Libby Daigle',\n  'Shanna Maloy',\n  'Brendan Wilken',\n  'Windy Knittel',\n  'Alice Curren',\n  'Eden Lumsden',\n  'Klara Morfin',\n  'Sherryl Noack',\n  'Gala Munsey',\n  'Stephani Frew',\n  'Twana Anthony',\n  'Mauro Matlock',\n  'Claudie Meisner',\n  'Adrienne Petrarca',\n  'Pearlene Shurtleff',\n  'Rachelle Piro',\n  'Louis Cocco',\n  'Susann Mcsweeney',\n  'Mandi Kempker',\n  'Ola Moller',\n  'Leif Mcgahan',\n  'Tisha Wurster',\n  'Hector Pinkett',\n  'Benita Jemison',\n  'Kaley Findley',\n  'Jim Torkelson',\n  'Freda Okafor',\n  'Rafaela Markert',\n  'Stasia Carwile',\n  'Evia Kahler',\n  'Rocky Almon',\n  'Sonja Beals',\n  'Dee Fomby',\n  'Damon Eatman',\n  'Alma Grieve',\n  'Linsey Bollig',\n  'Stefan Cloninger',\n  'Giovanna Blind',\n  'Myrtis Remy',\n  'Marguerita Dostal',\n  'Junior Baranowski',\n  'Allene Seto',\n  'Margery Caves',\n  'Nelly Moudy',\n  'Felix Sailer',\n];\nvar SENTENCES = [\n  'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',\n  'Phasellus vulputate odio commodo tortor sodales, et vehicula ipsum viverra.',\n  'In et mollis velit, accumsan volutpat libero.',\n  'Nulla rutrum tellus ipsum, eget fermentum sem dictum quis.',\n  'Suspendisse eget vehicula elit.',\n  'Proin ut lacus lacus.',\n  'Aliquam erat volutpat.',\n  'Vivamus ac suscipit est, et elementum lectus.',\n  'Cras tincidunt nisi in urna molestie varius.',\n  'Integer in magna eu nibh imperdiet tristique.',\n  'Curabitur eu pellentesque nisl.',\n  'Etiam non consequat est.',\n  'Duis mi massa, feugiat nec molestie sit amet, suscipit et metus.',\n  'Curabitur ac enim dictum arcu varius fermentum vel sodales dui.',\n  'Ut tristique augue at congue molestie.',\n  'Integer semper sem lorem, scelerisque suscipit lacus consequat nec.',\n  'Etiam euismod efficitur magna nec dignissim.',\n  'Morbi vel neque lectus.',\n  'Etiam ac accumsan elit, et pharetra ex.',\n  'Suspendisse vitae gravida mauris.',\n  'Pellentesque sed laoreet erat.',\n  'Nam aliquet purus quis massa eleifend, et efficitur felis aliquam.',\n  'Fusce faucibus diam erat, sed consectetur urna auctor at.',\n  'Praesent et nulla velit.',\n  'Cras eget enim nec odio feugiat tristique eu quis ante.',\n  'Morbi blandit diam vitae odio sollicitudin finibus.',\n  'Integer ac ante fermentum, placerat orci vel, fermentum lacus.',\n  'Maecenas est elit, semper ut posuere et, congue ut orci.',\n  'Phasellus eget enim vitae nunc luctus sodales a eu erat.',\n  'Curabitur dapibus nisi sed nisi dictum, in imperdiet urna posuere.',\n  'Vivamus commodo odio metus, tincidunt facilisis augue dictum quis.',\n  'Curabitur sagittis a lectus ac sodales.',\n  'Nam eget eros purus.',\n  'Nam scelerisque et ante in porta.',\n  'Proin vitae augue tristique, malesuada nisl ut, fermentum nisl.',\n  'Nulla bibendum quam id velit blandit dictum.',\n  'Cras tempus ac dolor ut convallis.',\n  'Sed vel ipsum est.',\n  'Nulla ut leo vestibulum, ultricies sapien ac, pellentesque dolor.',\n  'Etiam ultricies maximus tempus.',\n  'Donec dignissim mi ac libero feugiat, vitae lacinia odio viverra.',\n  'Curabitur condimentum tellus sit amet neque posuere, condimentum tempus purus eleifend.',\n  'Donec tempus, augue id hendrerit pretium, mauris leo congue nulla, ac iaculis erat nunc in dolor.',\n  'Praesent vel lectus venenatis, elementum mauris vitae, ullamcorper nulla.',\n  'Maecenas non diam cursus, imperdiet massa eget, pellentesque ex.',\n  'Vestibulum luctus risus vel augue auctor blandit.',\n  'Nullam augue diam, pulvinar sed sapien et, hendrerit venenatis risus.',\n  'Quisque sollicitudin nulla nec tellus feugiat hendrerit.',\n  'Vestibulum a eros accumsan, lacinia eros non, pretium diam.',\n  'Aenean iaculis augue sit amet scelerisque aliquam.',\n  'Donec ornare felis et dui hendrerit, eget bibendum nibh interdum.',\n  'Maecenas tellus magna, tristique vitae orci vel, auctor tincidunt nisi.',\n  'Fusce non libero quis velit porttitor maximus at eget enim.',\n  'Sed in aliquet tellus.',\n  'Etiam a tortor erat.',\n  'Donec nec diam vel tellus egestas lobortis.',\n  'Vivamus dictum erat nulla, sit amet accumsan dolor scelerisque eu.',\n  'In nec eleifend ex, pellentesque dapibus sapien.',\n  'Duis a mollis nisi.',\n  'Sed ornare nisl sit amet dolor pellentesque, eu fermentum leo interdum.',\n  'Sed eget mauris condimentum, molestie justo eu, feugiat felis.',\n  'Nunc suscipit leo non dui blandit, ac malesuada ex consequat.',\n  'Morbi varius placerat congue.',\n  'Praesent id velit in nunc elementum aliquet.',\n  'Sed luctus justo vitae nibh bibendum blandit.',\n  'Sed et sapien turpis.',\n  'Nulla ac eros vestibulum, mollis ante eu, rutrum nulla.',\n  'Sed cursus magna ut vehicula rutrum.',\n  'Ut consectetur feugiat consectetur.',\n  'Nulla nec ligula posuere neque sollicitudin rutrum a a dui.',\n  'Nulla ut quam odio.',\n  'Integer dignissim sapien et orci sodales volutpat.',\n  'Nullam a sapien leo.',\n  'Praesent cursus semper purus, vitae gravida risus dapibus mattis.',\n  'Sed pellentesque nulla lorem, in commodo arcu feugiat sed.',\n  'Phasellus blandit arcu non diam varius ornare.',\n];\nvar chatHistory = [];\n\nfor (var i = 0; i < 1000; i++) {\n  var name = NAMES[Math.floor(Math.random() * NAMES.length)];\n  var sentences = Math.ceil(Math.random() * 5);\n  var texts = [];\n\n  for (var x = 0; x < sentences; x++) {\n    texts.push(SENTENCES[Math.floor(Math.random() * SENTENCES.length)]);\n  }\n\n  chatHistory.push({\n    name,\n    text: texts.join(' '),\n  });\n}\n\nconst container = document.getElementById('mount');\n\nReactDOM.render(React.createElement(App), container);\n\ndocument.body.addEventListener('click', function() {\n  const bodyWidth = document.body.getBoundingClientRect().width;\n  const minWidth = 300;\n\n  container.style.display = 'inline-block';\n  container.style.maxWidth = `${minWidth +\n    Math.round(Math.random() * (bodyWidth - minWidth))}px`;\n});\n"
  },
  {
    "path": "playground/grid-test.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"https://unpkg.com/fps-measurer/dist/umd/fps-measurer.js\"></script>\n  <script src=\"utils.js\"></script>\n  <script src=\"tests.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('grid-test.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/grid-test.js",
    "content": "function cellRenderer(params) {\n  return React.createElement(\n    'div',\n    {\n      className: 'item',\n      key: params.key,\n      style: params.style,\n    },\n    params.columnIndex,\n  );\n}\n\nvar App = React.createClass({\n  render: function() {\n    return React.createElement(ReactVirtualized.AutoSizer, null, function(\n      params,\n    ) {\n      return React.createElement(ReactVirtualized.Grid, {\n        columnCount: 1000,\n        columnWidth: 35,\n        height: params.height,\n        overscanRowCount: 0,\n        cellRenderer: cellRenderer,\n        rowHeight: 30,\n        rowCount: 5000,\n        width: params.width,\n      });\n    });\n  },\n});\n\nReactDOM.render(React.createElement(App), document.querySelector('#mount'));\n\nconst testCase = createScrollingTestCase(\n  document.querySelector('.ReactVirtualized__Grid'),\n);\nconst TestRunner = FpsMeasurer.TestRunner;\nconst testRunner = new TestRunner(testCase, 5);\n\ndocument.body.addEventListener('click', function(event) {\n  if (testRunner.isRunning()) {\n    testRunner.stop();\n  } else {\n    testRunner.start();\n  }\n});\n"
  },
  {
    "path": "playground/grid.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n    }\n    .cell {\n      display: flex;\n      flex-direction: row;\n    }\n    .button,\n    .input,\n    .image {\n      width: 100%;\n      height: 100%;\n    }\n    .button {\n      appearance: none;\n      border: none;\n      background-color: #DDD;\n      border-radius: 4px;\n      display: flex;\n      align-content: center;\n    }\n    .input {\n      box-sizing: border-box;\n    }\n    .image {\n      height: auto;\n      width: 100%;\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"utils.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('grid.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/grid.js",
    "content": "var REACT_VIRTUALIZED_BANNER =\n  'https://cloud.githubusercontent.com/assets/29597/11737732/0ca1e55e-9f91-11e5-97f3-098f2f8ed866.png';\n\nfunction getColumnWidth(params) {\n  switch (params.index % 3) {\n    case 0:\n      return 65;\n    case 1:\n      return 65;\n    case 2:\n      return 100;\n  }\n}\n\nfunction cellRenderer(params) {\n  var key = `c:${params.columnIndex}, r:${params.rowIndex}`;\n  switch (params.columnIndex % 3) {\n    case 0:\n      return React.DOM.input({\n        className: 'input',\n        defaultValue: key,\n        key: params.key,\n        onChange: function() {},\n        style: params.style,\n      });\n    case 1:\n      return React.DOM.button(\n        {\n          className: 'button',\n          key: params.key,\n          style: params.style,\n        },\n        key,\n      );\n    case 2:\n      return React.DOM.img({\n        className: 'image',\n        key: params.key,\n        src: REACT_VIRTUALIZED_BANNER,\n        style: params.style,\n      });\n  }\n}\n\nvar App = React.createClass({\n  render: function() {\n    return React.createElement(ReactVirtualized.AutoSizer, null, function(\n      params,\n    ) {\n      return React.createElement(ReactVirtualized.Grid, {\n        columnCount: 1000,\n        columnWidth: getColumnWidth,\n        height: params.height,\n        overscanRowCount: 0,\n        cellRenderer: cellRenderer,\n        rowHeight: 30,\n        rowCount: 1000,\n        width: params.width,\n      });\n    });\n  },\n});\n\nReactDOM.render(React.createElement(App), document.querySelector('#mount'));\n"
  },
  {
    "path": "playground/helper.js",
    "content": "function loadStyle(source, callback) {\n  var link = document.createElement('link');\n  link.setAttribute('rel', 'stylesheet');\n  link.setAttribute('href', source);\n  link.onload = callback;\n  document.head.appendChild(link);\n}\n\nfunction loadScript(source) {\n  var script = document.createElement('script');\n  script.setAttribute('src', source);\n  script.async = false;\n  document.head.appendChild(script);\n}\n\nfunction loadScriptsAndStyles(source) {\n  var baseDir = 'https://unpkg.com/react-virtualized/';\n  var sourceParam = getUrlParam('source');\n\n  if (sourceParam) {\n    baseDir =\n      sourceParam === 'local'\n        ? '../'\n        : `https://unpkg.com/react-virtualized@${sourceParam}/`;\n  }\n\n  var styleSource = baseDir + 'styles.css';\n  var scriptSource = baseDir + 'dist/umd/react-virtualized.js';\n  var appSource = source;\n\n  loadStyle(styleSource, function() {\n    loadScript(scriptSource);\n    loadScript(appSource);\n  });\n}\n\nfunction loadReact() {\n  var baseDir = 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2';\n  var reactParam = getUrlParam('react');\n\n  if (reactParam) {\n    baseDir =\n      reactParam === 'latest'\n        ? 'http://react.zpao.com/builds/master/latest'\n        : `https://cdnjs.cloudflare.com/ajax/libs/react/${reactParam}`;\n  }\n\n  loadScript(`${baseDir}/react.min.js`);\n  loadScript(`${baseDir}/react-dom.min.js`);\n}\n"
  },
  {
    "path": "playground/hover.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n      font-family: -apple-system, BlinkMacSystemFont,  \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\",  \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",  sans-serif;\n      font-size: 12px;\n    }\n    * {\n      box-sizing: border-box;\n    }\n    .item {\n      height: 100%;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      border: 1px solid #eee;\n    }\n    .item:hover {\n      background-color: rgba(0, 0, 0, .2);\n    }\n    .hoveredItem {\n      background-color: rgba(0, 0, 0, .1);\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"utils.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('hover.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/hover.js",
    "content": "var App = React.createClass({\n  getInitialState: function() {\n    return {};\n  },\n\n  render: function() {\n    var cellRenderer = this._cellRenderer;\n\n    return React.createElement(\n      ReactVirtualized.AutoSizer,\n      {\n        ref: 'AutoSizer',\n      },\n      function(params) {\n        return React.createElement(ReactVirtualized.Grid, {\n          columnCount: 1000,\n          columnWidth: 100,\n          height: params.height,\n          ref: 'Grid',\n          cellRenderer: cellRenderer,\n          rowHeight: 30,\n          rowCount: 1000,\n          width: params.width,\n        });\n      },\n    );\n  },\n\n  _cellRenderer(params) {\n    var columnIndex = params.columnIndex;\n    var rowIndex = params.rowIndex;\n    var key = `c:${columnIndex}, r:${rowIndex}`;\n    var setState = this.setState.bind(this);\n    var grid = this.refs.AutoSizer.refs.Grid;\n\n    var className =\n      columnIndex === this.state.hoveredColumnIndex ||\n      rowIndex === this.state.hoveredRowIndex\n        ? 'item hoveredItem'\n        : 'item';\n\n    return React.DOM.div(\n      {\n        className: className,\n        key: params.key,\n        onMouseOver: function() {\n          setState({\n            hoveredColumnIndex: columnIndex,\n            hoveredRowIndex: rowIndex,\n          });\n          grid.forceUpdate();\n        },\n        style: params.style,\n      },\n      key,\n    );\n  },\n});\n\nReactDOM.render(React.createElement(App), document.querySelector('#mount'));\n"
  },
  {
    "path": "playground/render-counters.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n      font-family: -apple-system, BlinkMacSystemFont,  \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\",  \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",  sans-serif;\n      font-size: 12px;\n    }\n    * {\n      box-sizing: border-box;\n    }\n    .item {\n      height: 100%;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      border: 1px solid #eee;\n    }\n    .item:hover {\n      background-color: rgba(0, 0, 0, .2);\n    }\n    .hoveredItem {\n      background-color: rgba(0, 0, 0, .1);\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"utils.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('render-counters.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/render-counters.js",
    "content": "const {PureComponent} = React;\nconst {AutoSizer, List} = ReactVirtualized;\n\nclass ListExample extends PureComponent {\n  render() {\n    return React.createElement(AutoSizer, null, ({height, width}) =>\n      React.createElement(List, {\n        height: height,\n        overscanRowCount: 0,\n        rowCount: 1000,\n        rowHeight: 30,\n        rowRenderer: this._rowRenderer,\n        width: width,\n      }),\n    );\n  }\n\n  _rowRenderer({index, isScrolling, key, style}) {\n    return React.createElement(Row, {\n      index: index,\n      key: key,\n      style: style,\n    });\n  }\n}\n\nclass Row extends PureComponent {\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      counter: 0,\n    };\n\n    this._renderCount = 0;\n  }\n\n  render() {\n    this._renderCount++;\n\n    const {counter} = this.state;\n    const {index, style} = this.props;\n\n    return React.createElement(\n      'div',\n      {\n        onClick: () => {\n          this.setState(state => {\n            counter: state.counter++;\n          });\n        },\n        style: style,\n      },\n      'Row ',\n      index,\n      ', ',\n      this._renderCount,\n    );\n  }\n}\n\nReactDOM.render(\n  React.createElement(ListExample),\n  document.querySelector('#mount'),\n);\n"
  },
  {
    "path": "playground/scroll-sync.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"utils.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('scroll-sync.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/scroll-sync.js",
    "content": "function cellRenderer(params) {\n  return React.createElement(\n    'div',\n    {\n      className: 'item',\n      key: params.key,\n      style: params.style,\n    },\n    params.columnIndex,\n  );\n}\n\nvar App = React.createClass({\n  render: function() {\n    return React.createElement(ReactVirtualized.AutoSizer, null, function(\n      autoSizerParams,\n    ) {\n      return React.createElement(ReactVirtualized.ScrollSync, null, function(\n        scrollSyncParams,\n      ) {\n        return React.createElement(\n          'div',\n          {\n            style: {\n              height: autoSizerParams.height,\n              width: autoSizerParams.width,\n            },\n          },\n          React.createElement(ReactVirtualized.Grid, {\n            cellRenderer: cellRenderer,\n            columnCount: 1000,\n            columnWidth: 35,\n            height: autoSizerParams.height / 2,\n            key: 0,\n            overscanRowCount: 0,\n            rowHeight: 30,\n            rowCount: 5000,\n            scrollLeft: scrollSyncParams.scrollLeft,\n            width: autoSizerParams.width,\n          }),\n          React.createElement(ReactVirtualized.Grid, {\n            cellRenderer: cellRenderer,\n            columnCount: 1000,\n            columnWidth: 35,\n            height: autoSizerParams.height / 2,\n            key: 1,\n            overscanRowCount: 0,\n            onScroll: scrollSyncParams.onScroll,\n            rowHeight: 30,\n            rowCount: 5000,\n            width: autoSizerParams.width,\n          }),\n        );\n      });\n    });\n  },\n});\n\nReactDOM.render(React.createElement(App), document.querySelector('#mount'));\n"
  },
  {
    "path": "playground/table.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"https://unpkg.com/fps-measurer/dist/umd/fps-measurer.js\"></script>\n  <script src=\"utils.js\"></script>\n  <script src=\"tests.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('table.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/table.js",
    "content": "const NUM_COLUMNS = 40;\n\nfunction rowGetter(params) {\n  return new Array(NUM_COLUMNS).fill('').map(function(_, index) {\n    return index;\n  });\n}\n\nvar App = React.createClass({\n  render: function() {\n    const flexColumns = [];\n\n    for (var i = 0; i < NUM_COLUMNS; i++) {\n      flexColumns.push(\n        React.createElement(ReactVirtualized.Column, {\n          dataKey: i,\n          flexGrow: 1,\n          key: i,\n          width: 50,\n        }),\n      );\n    }\n\n    return React.createElement(ReactVirtualized.AutoSizer, null, function(\n      params,\n    ) {\n      return React.createElement(\n        ReactVirtualized.Table,\n        {\n          height: params.height,\n          overscanRowCount: 0,\n          rowGetter,\n          rowHeight: 30,\n          rowCount: 1000,\n          width: params.width,\n        },\n        null,\n        flexColumns,\n      );\n    });\n  },\n});\n\nReactDOM.render(React.createElement(App), document.querySelector('#mount'));\n\nconst testCase = createScrollingTestCase(\n  document.querySelector('.ReactVirtualized__Grid'),\n);\nconst TestRunner = FpsMeasurer.TestRunner;\nconst testRunner = new TestRunner(testCase, 5);\n\ndocument.body.addEventListener('click', function(event) {\n  if (testRunner.isRunning()) {\n    testRunner.stop();\n  } else {\n    testRunner.start();\n  }\n});\n"
  },
  {
    "path": "playground/tests.js",
    "content": "function createScrollingTestCase(component) {\n  var scrollDown = getUrlParam('direction') !== 'up';\n\n  return function testCase(completedCallback) {\n    component.scrollTop = scrollDown ? 0 : component.scrollHeight;\n\n    var maxScrollTop = component.scrollHeight;\n\n    var interval = 1;\n    var scrollTop = component.scrollTop;\n\n    function incrementScrollDown() {\n      if (!testRunner.isRunning()) {\n        return;\n      }\n\n      interval *= 1.05;\n      scrollTop = Math.min(scrollTop + interval, maxScrollTop);\n\n      component.scrollTop = scrollTop;\n\n      if (scrollTop < maxScrollTop) {\n        requestAnimationFrame(incrementScrollDown);\n      } else {\n        completedCallback();\n      }\n    }\n\n    function incrementScrollUp() {\n      if (!testRunner.isRunning()) {\n        return;\n      }\n\n      interval *= 1.05;\n      scrollTop = Math.max(scrollTop - interval, 0);\n\n      component.scrollTop = scrollTop;\n\n      if (scrollTop > 0) {\n        requestAnimationFrame(incrementScrollUp);\n      } else {\n        completedCallback();\n      }\n    }\n\n    if (scrollDown) {\n      incrementScrollDown();\n    } else {\n      incrementScrollUp();\n    }\n  };\n}\n"
  },
  {
    "path": "playground/tree.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>foo</title>\n  <style type=\"text/css\">\n    body, html, #mount {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n      font-family: monospace;\n    }\n    * {\n      box-sizing: border-box;\n    }\n    ul {\n       margin: 0;\n       list-style: none;\n    }\n    li {\n    }\n    .item {\n      height: 20px;\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      white-space: pre;\n      user-select: none;\n    }\n  </style>\n</head>\n<body>\n  <div id=\"mount\"></div>\n\n  <script src=\"utils.js\"></script>\n  <script src=\"helper.js\"></script>\n  <script>\n    loadReact();\n    loadScriptsAndStyles('tree.js');\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "playground/tree.js",
    "content": "var ROW_HEIGHT = 20;\nvar RANDOM_WORDS = [\n  'abstrusity',\n  'advertisable',\n  'bellwood',\n  'benzole',\n  'boreum',\n  'brenda',\n  'cassiopeian',\n  'chansonnier',\n  'cleric',\n  'conclusional',\n  'conventicle',\n  'copalm',\n  'cornopion',\n  'crossbar',\n  'disputative',\n  'djilas',\n  'ebracteate',\n  'ephemerally',\n  'epidemical',\n  'evasive',\n  'eyeglasses',\n  'farragut',\n  'fenny',\n  'ferryman',\n  'fluently',\n  'foreigner',\n  'genseng',\n  'glaiket',\n  'haunch',\n  'histogeny',\n  'illocution',\n  'imprescriptible',\n  'inapproachable',\n  'incisory',\n  'intrusiveness',\n  'isoceraunic',\n  'japygid',\n  'juiciest',\n  'jump',\n  'kananga',\n  'leavening',\n  'legerdemain',\n  'licence',\n  'licia',\n  'luanda',\n  'malaga',\n  'mathewson',\n  'nonhumus',\n  'nonsailor',\n  'nummary',\n  'nyregyhza',\n  'onanist',\n  'opis',\n  'orphrey',\n  'paganising',\n  'pebbling',\n  'penchi',\n  'photopia',\n  'pinocle',\n  'principally',\n  'prosector.',\n  'radiosensitive',\n  'redbrick',\n  'reexposure',\n  'revived',\n  'subexternal',\n  'sukarnapura',\n  'supersphenoid',\n  'tabularizing',\n  'territorialism',\n  'tester',\n  'thalassography',\n  'tuberculise',\n  'uncranked',\n  'undersawyer',\n  'unimpartible',\n  'unsubdivided',\n  'untwining',\n  'unwaived',\n  'webfoot',\n  'wedeling',\n  'wellingborough',\n  'whiffet',\n  'whipstall',\n  'wot',\n  'yonkersite',\n  'zonary',\n];\nvar data = createRandomizedData();\n\nfunction renderItem(item, keyPrefix) {\n  var onClick = function(event) {\n    event.stopPropagation();\n    item.expanded = !item.expanded;\n    List.recomputeRowHeights();\n    List.forceUpdate();\n  };\n\n  var props = {key: keyPrefix};\n  var children = [];\n  var itemText;\n\n  if (item.expanded) {\n    props.onClick = onClick;\n    itemText = '[-] ' + item.name;\n    children = item.children.map(function(child, index) {\n      return renderItem(child, keyPrefix + '-' + index);\n    });\n  } else if (item.children.length) {\n    props.onClick = onClick;\n    itemText = '[+] ' + item.name;\n  } else {\n    itemText = '    ' + item.name;\n  }\n\n  children.unshift(\n    React.DOM.div(\n      {\n        className: 'item',\n        key: 'label',\n        style: {\n          cursor: item.children.length ? 'pointer' : 'auto',\n        },\n      },\n      itemText,\n    ),\n  );\n\n  return React.DOM.ul(null, React.DOM.li(props, children));\n}\n\nfunction getExpandedItemCount(item) {\n  var count = 1;\n\n  if (item.expanded) {\n    count += item.children\n      .map(getExpandedItemCount)\n      .reduce(function(total, count) {\n        return total + count;\n      }, 0);\n  }\n\n  return count;\n}\n\nvar List;\nfunction setRef(ref) {\n  List = ref;\n}\n\nfunction cellRenderer(params) {\n  var renderedCell = renderItem(data[params.index], params.index);\n\n  return React.DOM.ul(\n    {\n      key: params.key,\n      style: params.style,\n    },\n    renderedCell,\n  );\n}\n\nfunction rowHeight(params) {\n  return getExpandedItemCount(data[params.index]) * ROW_HEIGHT;\n}\n\nvar App = React.createClass({\n  render: function() {\n    return React.createElement(ReactVirtualized.AutoSizer, null, function(\n      params,\n    ) {\n      return React.createElement(ReactVirtualized.List, {\n        height: params.height,\n        overscanRowCount: 10,\n        ref: setRef,\n        rowHeight: rowHeight,\n        rowRenderer: cellRenderer,\n        rowCount: data.length,\n        width: params.width,\n      });\n    });\n  },\n});\n\nReactDOM.render(React.createElement(App), document.querySelector('#mount'));\n\nfunction createRandomizedData() {\n  var data = [];\n\n  for (var i = 0; i < 10000; i++) {\n    data.push(createRandomizedItem(0));\n  }\n\n  return data;\n}\n\nfunction createRandomizedItem(depth) {\n  var item = {};\n  item.children = [];\n  item.name = RANDOM_WORDS[Math.floor(Math.random() * RANDOM_WORDS.length)];\n\n  var numChildren = depth < 3 ? Math.floor(Math.random() * 5) : 0;\n  for (var i = 0; i < numChildren; i++) {\n    item.children.push(createRandomizedItem(depth + 1));\n  }\n\n  item.expanded = numChildren > 0 && Math.random() < 0.25;\n\n  return item;\n}\n"
  },
  {
    "path": "playground/utils.js",
    "content": "function getUrlParams() {\n  var search = window.location.search;\n\n  return search.length\n    ? search\n        .substr(1)\n        .split('&')\n        .reduce(function(reduced, value) {\n          var matches = value.split('=');\n          reduced[matches[0]] = matches[1];\n          return reduced;\n        }, {})\n    : {};\n}\n\nfunction getUrlParam(key) {\n  return getUrlParams()[key];\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  autoprefixer: {\n    browsers: ['last 2 version', 'Firefox 15', 'iOS 8'],\n  },\n  // The plugins section is used by postcss-loader with webpack\n  plugins: [require('autoprefixer')],\n};\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import nodeResolve from 'rollup-plugin-node-resolve';\nimport commonjs from 'rollup-plugin-commonjs';\nimport babel from 'rollup-plugin-babel';\nimport replace from 'rollup-plugin-replace';\nimport {uglify} from 'rollup-plugin-uglify';\n\nexport default {\n  input: './source/index.js',\n  output: {\n    file: 'dist/umd/react-virtualized.js',\n    format: 'umd',\n    name: 'ReactVirtualized',\n    globals: {\n      react: 'React',\n      'react-dom': 'ReactDOM',\n    },\n  },\n  external: ['react', 'react-dom'],\n  plugins: [\n    nodeResolve(),\n    commonjs({\n      include: 'node_modules/**',\n    }),\n    babel({\n      exclude: 'node_modules/**',\n    }),\n    replace({\n      'process.env.NODE_ENV': JSON.stringify('development'),\n    }),\n    uglify({\n      mangle: false,\n      output: {\n        comments: true,\n        beautify: true,\n      },\n    }),\n  ],\n};\n"
  },
  {
    "path": "source/ArrowKeyStepper/ArrowKeyStepper.example.css",
    "content": ".Grid {\n  border: 1px solid #e0e0e0;\n}\n\n.Cell {\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  text-align: center;\n  border: none;\n  border-right: 1px solid #e0e0e0;\n  border-bottom: 1px solid #e0e0e0;\n  outline: none;\n}\n\n.FocusedCell {\n  background-color: #e0e0e0;\n  font-weight: bold;\n}\n\n.Radio {\n  margin-left: 0.5rem;\n}\n\n.checkboxLabel {\n  display: flex;\n  align-items: center;\n}\n.checkbox {\n  margin-right: 5px;\n}\n"
  },
  {
    "path": "source/ArrowKeyStepper/ArrowKeyStepper.example.js",
    "content": "/** @flow */\n\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport ArrowKeyStepper, {type ScrollIndices} from './';\nimport AutoSizer from '../AutoSizer';\nimport Grid from '../Grid';\nimport clsx from 'clsx';\nimport styles from './ArrowKeyStepper.example.css';\n\ntype State = {\n  mode: 'edges' | 'cells',\n  isClickable: boolean,\n  scrollToColumn: number,\n  scrollToRow: number,\n};\n\nexport default class ArrowKeyStepperExample extends React.PureComponent<\n  {},\n  State,\n> {\n  state = {\n    mode: 'edges',\n    isClickable: true,\n    scrollToColumn: 0,\n    scrollToRow: 0,\n  };\n\n  render() {\n    const {mode, isClickable, scrollToColumn, scrollToRow} = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"ArrowKeyStepper\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/ArrowKeyStepper/ArrowKeyStepper.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/ArrowKeyStepper.md\"\n        />\n\n        <ContentBoxParagraph>\n          This high-order component decorates a <code>List</code>,{' '}\n          <code>Table</code>, or <code>Grid</code> and responds to arrow-key\n          events by scrolling one row or column at a time. Focus in the `Grid`\n          below and use the left, right, up, or down arrow keys to move around\n          within the grid.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          Note that unlike the other HOCs in react-virtualized, the{' '}\n          <code>ArrowKeyStepper</code> adds a <code>&lt;div&gt;</code> element\n          around its children in order to attach a key-down event handler.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <strong>mode</strong>:\n          <label>\n            <input\n              aria-label='Set mode equal to \"cells\"'\n              checked={mode === 'cells'}\n              className={styles.Radio}\n              type=\"radio\"\n              onChange={event =>\n                event.target.checked && this.setState({mode: 'cells'})\n              }\n              value=\"cells\"\n            />\n            cells\n          </label>\n          <label>\n            <input\n              aria-label='Set mode equal to \"edges\"'\n              checked={mode === 'edges'}\n              className={styles.Radio}\n              type=\"radio\"\n              onChange={event =>\n                event.target.checked && this.setState({mode: 'edges'})\n              }\n              value=\"edges\"\n            />\n            edges (default)\n          </label>\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Enable click selection? (resets selection)\"\n              className={styles.checkbox}\n              type=\"checkbox\"\n              checked={isClickable}\n              onChange={this._onClickableChange}\n            />\n            Enable click selection? (resets selection)\n          </label>\n        </ContentBoxParagraph>\n\n        <ArrowKeyStepper\n          columnCount={100}\n          isControlled={isClickable}\n          onScrollToChange={isClickable ? this._selectCell : undefined}\n          mode={mode}\n          rowCount={100}\n          scrollToColumn={scrollToColumn}\n          scrollToRow={scrollToRow}>\n          {({onSectionRendered, scrollToColumn, scrollToRow}) => (\n            <div>\n              <ContentBoxParagraph>\n                {`Most-recently-stepped column: ${scrollToColumn}, row: ${scrollToRow}`}\n              </ContentBoxParagraph>\n\n              <AutoSizer disableHeight>\n                {({width}) => (\n                  <Grid\n                    className={styles.Grid}\n                    columnWidth={this._getColumnWidth}\n                    columnCount={100}\n                    height={200}\n                    onSectionRendered={onSectionRendered}\n                    cellRenderer={({columnIndex, key, rowIndex, style}) =>\n                      this._cellRenderer({\n                        columnIndex,\n                        key,\n                        rowIndex,\n                        scrollToColumn,\n                        scrollToRow,\n                        style,\n                      })\n                    }\n                    rowHeight={this._getRowHeight}\n                    rowCount={100}\n                    scrollToColumn={scrollToColumn}\n                    scrollToRow={scrollToRow}\n                    width={width}\n                  />\n                )}\n              </AutoSizer>\n            </div>\n          )}\n        </ArrowKeyStepper>\n      </ContentBox>\n    );\n  }\n\n  _getColumnWidth = ({index}: {index: number}) => {\n    return (1 + (index % 3)) * 60;\n  };\n\n  _getRowHeight = ({index}: {index: number}) => {\n    return (1 + (index % 3)) * 30;\n  };\n\n  _cellRenderer = ({\n    columnIndex,\n    key,\n    rowIndex,\n    scrollToColumn,\n    scrollToRow,\n    style,\n  }: {\n    columnIndex: number,\n    key: string,\n    rowIndex: number,\n    scrollToColumn: number,\n    scrollToRow: number,\n    style: Object,\n  }) => {\n    const className = clsx(styles.Cell, {\n      [styles.FocusedCell]:\n        columnIndex === scrollToColumn && rowIndex === scrollToRow,\n    });\n\n    return (\n      <span\n        role=\"none\"\n        className={className}\n        key={key}\n        onClick={\n          this.state.isClickable &&\n          (() =>\n            this._selectCell({\n              scrollToColumn: columnIndex,\n              scrollToRow: rowIndex,\n            }))\n        }\n        style={style}>\n        {`r:${rowIndex}, c:${columnIndex}`}\n      </span>\n    );\n  };\n\n  _selectCell = ({scrollToColumn, scrollToRow}: ScrollIndices) => {\n    this.setState({scrollToColumn, scrollToRow});\n  };\n\n  _onClickableChange = (event: Event) => {\n    if (event.target instanceof HTMLInputElement) {\n      this.setState({\n        isClickable: event.target.checked,\n        scrollToColumn: 0,\n        scrollToRow: 0,\n      });\n    }\n  };\n}\n"
  },
  {
    "path": "source/ArrowKeyStepper/ArrowKeyStepper.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport ArrowKeyStepper from './ArrowKeyStepper';\nimport {Simulate} from 'react-dom/test-utils';\n\nfunction renderTextContent(scrollToColumn, scrollToRow) {\n  return `scrollToColumn:${scrollToColumn}, scrollToRow:${scrollToRow}`;\n}\n\nfunction ChildComponent({scrollToColumn, scrollToRow}) {\n  return <div>{renderTextContent(scrollToColumn, scrollToRow)}</div>;\n}\n\ndescribe('ArrowKeyStepper', () => {\n  function renderHelper(props = {}) {\n    let onSectionRenderedCallback;\n\n    const component = render(\n      <ArrowKeyStepper columnCount={10} mode=\"edges\" rowCount={10} {...props}>\n        {({onSectionRendered, scrollToColumn, scrollToRow}) => {\n          onSectionRenderedCallback = onSectionRendered;\n\n          return (\n            <ChildComponent\n              scrollToColumn={scrollToColumn}\n              scrollToRow={scrollToRow}\n            />\n          );\n        }}\n      </ArrowKeyStepper>,\n    );\n    const node = findDOMNode(component);\n\n    return {\n      component,\n      node,\n      onSectionRendered: onSectionRenderedCallback,\n    };\n  }\n\n  function assertCurrentScrollTo(node, scrollToColumn, scrollToRow) {\n    expect(node.textContent).toEqual(\n      renderTextContent(scrollToColumn, scrollToRow),\n    );\n  }\n\n  it('should use a custom :className if one is specified', () => {\n    const {node} = renderHelper({className: 'foo'});\n    expect(node.className).toEqual('foo');\n  });\n\n  it('should update :scrollToColumn and :scrollToRow in response to arrow keys', () => {\n    const {node} = renderHelper();\n    assertCurrentScrollTo(node, 0, 0);\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 0, 1);\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    assertCurrentScrollTo(node, 1, 1);\n    Simulate.keyDown(node, {key: 'ArrowUp'});\n    assertCurrentScrollTo(node, 1, 0);\n    Simulate.keyDown(node, {key: 'ArrowLeft'});\n    assertCurrentScrollTo(node, 0, 0);\n  });\n\n  it('should not scroll past the row and column boundaries provided', () => {\n    const {node} = renderHelper({\n      columnCount: 2,\n      rowCount: 2,\n    });\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 0, 1);\n    Simulate.keyDown(node, {key: 'ArrowUp'});\n    Simulate.keyDown(node, {key: 'ArrowUp'});\n    Simulate.keyDown(node, {key: 'ArrowUp'});\n    assertCurrentScrollTo(node, 0, 0);\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    assertCurrentScrollTo(node, 1, 0);\n    Simulate.keyDown(node, {key: 'ArrowLeft'});\n    Simulate.keyDown(node, {key: 'ArrowLeft'});\n    Simulate.keyDown(node, {key: 'ArrowLeft'});\n    assertCurrentScrollTo(node, 0, 0);\n  });\n\n  it('should accept initial :scrollToColumn and :scrollToRow values via props', () => {\n    const {node} = renderHelper({\n      mode: 'cells',\n      scrollToColumn: 2,\n      scrollToRow: 4,\n    });\n    assertCurrentScrollTo(node, 2, 4);\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 2, 5);\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    assertCurrentScrollTo(node, 3, 5);\n  });\n\n  it('should accept updated :scrollToColumn and :scrollToRow values via props', () => {\n    const {node} = renderHelper({\n      mode: 'cells',\n      scrollToColumn: 2,\n      scrollToRow: 4,\n    });\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 2, 5);\n    renderHelper({\n      mode: 'cells',\n      scrollToColumn: 1,\n      scrollToRow: 1,\n    });\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    assertCurrentScrollTo(node, 2, 1);\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 2, 2);\n  });\n\n  it('should accept updated :scrollToColumn and :scrollToRow values via setScrollIndexes()', () => {\n    const {component, node} = renderHelper({\n      mode: 'cells',\n      scrollToColumn: 2,\n      scrollToRow: 4,\n    });\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 2, 5);\n    component.setScrollIndexes({\n      scrollToColumn: 1,\n      scrollToRow: 1,\n    });\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    assertCurrentScrollTo(node, 2, 1);\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 2, 2);\n  });\n\n  it('should not update :scrollToColumn or :scrollToRow when :disabled', () => {\n    const {node} = renderHelper({\n      disabled: true,\n    });\n    assertCurrentScrollTo(node, 0, 0);\n    Simulate.keyDown(node, {key: 'ArrowDown'});\n    assertCurrentScrollTo(node, 0, 0);\n    Simulate.keyDown(node, {key: 'ArrowRight'});\n    assertCurrentScrollTo(node, 0, 0);\n  });\n\n  it('should call :onScrollToChange for key down', () => {\n    [true, false].forEach(() => {\n      const onScrollToChange = jest.fn();\n      const {node} = renderHelper({\n        isControlled: true,\n        onScrollToChange,\n      });\n\n      expect(onScrollToChange.mock.calls).toHaveLength(0);\n\n      Simulate.keyDown(node, {key: 'ArrowDown'});\n\n      expect(onScrollToChange.mock.calls).toHaveLength(1);\n\n      const {scrollToColumn, scrollToRow} = onScrollToChange.mock.calls[0][0];\n      expect(scrollToColumn).toEqual(0);\n      expect(scrollToRow).toEqual(1);\n    });\n  });\n\n  it('should not call :onScrollToChange for prop update', () => {\n    let numCalls = 0;\n    const onScrollToChange = () => {\n      numCalls++;\n    };\n    const {node} = renderHelper({\n      onScrollToChange,\n      scrollToColumn: 0,\n      scrollToRow: 0,\n    });\n\n    renderHelper({\n      isControlled: true,\n      onScrollToChange,\n      node,\n      scrollToColumn: 0,\n      scrollToRow: 1,\n    });\n    expect(numCalls).toEqual(0);\n  });\n\n  describe('mode === \"edges\"', () => {\n    it('should update :scrollToColumn and :scrollToRow relative to the most recent :onSectionRendered event', () => {\n      const {node, onSectionRendered} = renderHelper();\n      onSectionRendered({\n        // Simulate a scroll\n        columnStartIndex: 0,\n        columnStopIndex: 4,\n        rowStartIndex: 4,\n        rowStopIndex: 6,\n      });\n      Simulate.keyDown(node, {key: 'ArrowDown'});\n      assertCurrentScrollTo(node, 0, 7);\n\n      onSectionRendered({\n        // Simulate a scroll\n        columnStartIndex: 5,\n        columnStopIndex: 10,\n        rowStartIndex: 2,\n        rowStopIndex: 4,\n      });\n      Simulate.keyDown(node, {key: 'ArrowUp'});\n      assertCurrentScrollTo(node, 0, 1);\n\n      onSectionRendered({\n        // Simulate a scroll\n        columnStartIndex: 4,\n        columnStopIndex: 8,\n        rowStartIndex: 5,\n        rowStopIndex: 10,\n      });\n      Simulate.keyDown(node, {key: 'ArrowRight'});\n      assertCurrentScrollTo(node, 9, 1);\n\n      onSectionRendered({\n        // Simulate a scroll\n        columnStartIndex: 2,\n        columnStopIndex: 4,\n        rowStartIndex: 2,\n        rowStopIndex: 4,\n      });\n      Simulate.keyDown(node, {key: 'ArrowLeft'});\n      assertCurrentScrollTo(node, 1, 1);\n    });\n  });\n\n  describe('mode === \"cells\"', () => {\n    it('should update :scrollToColumn and :scrollToRow relative to the most recent :onSectionRendered event', () => {\n      const {node, onSectionRendered} = renderHelper({\n        mode: 'cells',\n        scrollToColumn: 5,\n        scrollToRow: 5,\n      });\n\n      onSectionRendered({\n        // Simulate a scroll\n        columnStartIndex: 10,\n        columnStopIndex: 10,\n        rowStartIndex: 15,\n        rowStopIndex: 15,\n      });\n      Simulate.keyDown(node, {key: 'ArrowUp'});\n      assertCurrentScrollTo(node, 5, 4);\n      Simulate.keyDown(node, {key: 'ArrowDown'});\n      assertCurrentScrollTo(node, 5, 5);\n\n      onSectionRendered({\n        // Simulate a scroll\n        columnStartIndex: 10,\n        columnStopIndex: 10,\n        rowStartIndex: 15,\n        rowStopIndex: 15,\n      });\n      Simulate.keyDown(node, {key: 'ArrowRight'});\n      assertCurrentScrollTo(node, 6, 5);\n      Simulate.keyDown(node, {key: 'ArrowLeft'});\n      assertCurrentScrollTo(node, 5, 5);\n    });\n  });\n});\n"
  },
  {
    "path": "source/ArrowKeyStepper/ArrowKeyStepper.js",
    "content": "/** @flow */\n\nimport type {RenderedSection} from '../Grid';\nimport type {ScrollIndices} from './types';\n\nimport * as React from 'react';\nimport {polyfill} from 'react-lifecycles-compat';\n\n/**\n * This HOC decorates a virtualized component and responds to arrow-key events by scrolling one row or column at a time.\n */\n\ntype ChildrenParams = {\n  onSectionRendered: (params: RenderedSection) => void,\n  scrollToColumn: number,\n  scrollToRow: number,\n};\n\ntype Props = {\n  children: (params: ChildrenParams) => React.Element<*>,\n  className?: string,\n  columnCount: number,\n  disabled: boolean,\n  isControlled: boolean,\n  mode: 'cells' | 'edges',\n  onScrollToChange?: (params: ScrollIndices) => void,\n  rowCount: number,\n  scrollToColumn: number,\n  scrollToRow: number,\n};\n\ntype State = ScrollIndices & {\n  instanceProps: {\n    prevScrollToColumn: number,\n    prevScrollToRow: number,\n  },\n};\n\nclass ArrowKeyStepper extends React.PureComponent<Props, State> {\n  static defaultProps = {\n    disabled: false,\n    isControlled: false,\n    mode: 'edges',\n    scrollToColumn: 0,\n    scrollToRow: 0,\n  };\n\n  state = {\n    scrollToColumn: 0,\n    scrollToRow: 0,\n    instanceProps: {\n      prevScrollToColumn: 0,\n      prevScrollToRow: 0,\n    },\n  };\n\n  _columnStartIndex = 0;\n  _columnStopIndex = 0;\n  _rowStartIndex = 0;\n  _rowStopIndex = 0;\n\n  static getDerivedStateFromProps(\n    nextProps: Props,\n    prevState: State,\n  ): $Shape<State> {\n    if (nextProps.isControlled) {\n      return {};\n    }\n\n    if (\n      nextProps.scrollToColumn !== prevState.instanceProps.prevScrollToColumn ||\n      nextProps.scrollToRow !== prevState.instanceProps.prevScrollToRow\n    ) {\n      return {\n        ...prevState,\n        scrollToColumn: nextProps.scrollToColumn,\n        scrollToRow: nextProps.scrollToRow,\n        instanceProps: {\n          prevScrollToColumn: nextProps.scrollToColumn,\n          prevScrollToRow: nextProps.scrollToRow,\n        },\n      };\n    }\n\n    return {};\n  }\n\n  setScrollIndexes({scrollToColumn, scrollToRow}: ScrollIndices) {\n    this.setState({\n      scrollToRow,\n      scrollToColumn,\n    });\n  }\n\n  render() {\n    const {className, children} = this.props;\n    const {scrollToColumn, scrollToRow} = this._getScrollState();\n\n    return (\n      <div className={className} onKeyDown={this._onKeyDown}>\n        {children({\n          onSectionRendered: this._onSectionRendered,\n          scrollToColumn,\n          scrollToRow,\n        })}\n      </div>\n    );\n  }\n\n  _onKeyDown = (event: KeyboardEvent) => {\n    const {columnCount, disabled, mode, rowCount} = this.props;\n\n    if (disabled) {\n      return;\n    }\n\n    const {\n      scrollToColumn: scrollToColumnPrevious,\n      scrollToRow: scrollToRowPrevious,\n    } = this._getScrollState();\n\n    let {scrollToColumn, scrollToRow} = this._getScrollState();\n\n    // The above cases all prevent default event event behavior.\n    // This is to keep the grid from scrolling after the snap-to update.\n    switch (event.key) {\n      case 'ArrowDown':\n        scrollToRow =\n          mode === 'cells'\n            ? Math.min(scrollToRow + 1, rowCount - 1)\n            : Math.min(this._rowStopIndex + 1, rowCount - 1);\n        break;\n      case 'ArrowLeft':\n        scrollToColumn =\n          mode === 'cells'\n            ? Math.max(scrollToColumn - 1, 0)\n            : Math.max(this._columnStartIndex - 1, 0);\n        break;\n      case 'ArrowRight':\n        scrollToColumn =\n          mode === 'cells'\n            ? Math.min(scrollToColumn + 1, columnCount - 1)\n            : Math.min(this._columnStopIndex + 1, columnCount - 1);\n        break;\n      case 'ArrowUp':\n        scrollToRow =\n          mode === 'cells'\n            ? Math.max(scrollToRow - 1, 0)\n            : Math.max(this._rowStartIndex - 1, 0);\n        break;\n    }\n\n    if (\n      scrollToColumn !== scrollToColumnPrevious ||\n      scrollToRow !== scrollToRowPrevious\n    ) {\n      event.preventDefault();\n\n      this._updateScrollState({scrollToColumn, scrollToRow});\n    }\n  };\n\n  _onSectionRendered = ({\n    columnStartIndex,\n    columnStopIndex,\n    rowStartIndex,\n    rowStopIndex,\n  }: RenderedSection) => {\n    this._columnStartIndex = columnStartIndex;\n    this._columnStopIndex = columnStopIndex;\n    this._rowStartIndex = rowStartIndex;\n    this._rowStopIndex = rowStopIndex;\n  };\n\n  _getScrollState() {\n    return this.props.isControlled ? this.props : this.state;\n  }\n\n  _updateScrollState({scrollToColumn, scrollToRow}: ScrollIndices) {\n    const {isControlled, onScrollToChange} = this.props;\n\n    if (typeof onScrollToChange === 'function') {\n      onScrollToChange({scrollToColumn, scrollToRow});\n    }\n\n    if (!isControlled) {\n      this.setState({scrollToColumn, scrollToRow});\n    }\n  }\n}\n\npolyfill(ArrowKeyStepper);\n\nexport default ArrowKeyStepper;\n"
  },
  {
    "path": "source/ArrowKeyStepper/index.js",
    "content": "// @flow\n\nexport type {ScrollIndices} from './types';\n\nexport {default} from './ArrowKeyStepper';\nexport {default as ArrowKeyStepper} from './ArrowKeyStepper';\n"
  },
  {
    "path": "source/ArrowKeyStepper/types.js",
    "content": "// @flow\n\nexport type ScrollIndices = {\n  scrollToColumn: number,\n  scrollToRow: number,\n};\n"
  },
  {
    "path": "source/AutoSizer/AutoSizer.example.css",
    "content": ".AutoSizerWrapper {\n  flex: 1 1 auto;\n}\n\n.List {\n  border: 1px solid #e0e0e0;\n}\n\n.row {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  padding: 0 25px;\n  background-color: #fff;\n  border-bottom: 1px solid #e0e0e0;\n}\n\n.checkboxLabel {\n  display: flex;\n  align-items: center;\n}\n.checkbox {\n  margin-right: 5px;\n}\n"
  },
  {
    "path": "source/AutoSizer/AutoSizer.example.js",
    "content": "/** @flow */\n\nimport {List as ImmutableList} from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport AutoSizer from './AutoSizer';\nimport List, {type RowRendererParams} from '../List';\nimport styles from './AutoSizer.example.css';\n\ntype State = {\n  hideDescription: boolean,\n};\n\nexport default class AutoSizerExample extends React.PureComponent<{}, State> {\n  static contextTypes = {\n    list: PropTypes.instanceOf(ImmutableList).isRequired,\n  };\n\n  state = {\n    hideDescription: false,\n  };\n\n  render() {\n    const {list} = this.context;\n    const {hideDescription} = this.state;\n\n    return (\n      <ContentBox\n        {...this.props}\n        style={{\n          height: 400,\n        }}>\n        <ContentBoxHeader\n          text=\"AutoSizer\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/AutoSizer/AutoSizer.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/AutoSizer.md\"\n        />\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Hide description (to show resize)?\"\n              className={styles.checkbox}\n              type=\"checkbox\"\n              checked={hideDescription}\n              onChange={event =>\n                this.setState({hideDescription: event.target.checked})\n              }\n            />\n            Hide description (to show resize)?\n          </label>\n        </ContentBoxParagraph>\n\n        {!hideDescription && (\n          <ContentBoxParagraph>\n            This component decorates <code>List</code>, <code>Table</code>, or\n            any other component and automatically manages its width and height.\n            It uses Sebastian Decima's{' '}\n            <a\n              href=\"https://github.com/sdecima/javascript-detect-element-resize\"\n              target=\"_blank\">\n              element resize event\n            </a>{' '}\n            to determine the appropriate size. In this example{' '}\n            <code>AutoSizer</code> grows to fill the remaining width and height\n            of this flex column.\n          </ContentBoxParagraph>\n        )}\n\n        <div className={styles.AutoSizerWrapper}>\n          <AutoSizer>\n            {({width, height}) => (\n              <List\n                className={styles.List}\n                height={height}\n                rowCount={list.size}\n                rowHeight={30}\n                rowRenderer={this._rowRenderer}\n                width={width}\n              />\n            )}\n          </AutoSizer>\n        </div>\n      </ContentBox>\n    );\n  }\n\n  _rowRenderer = ({index, key, style}: RowRendererParams) => {\n    const {list} = this.context;\n    const row = list.get(index);\n\n    return (\n      <div key={key} className={styles.row} style={style}>\n        {row.name}\n      </div>\n    );\n  };\n}\n"
  },
  {
    "path": "source/AutoSizer/AutoSizer.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport AutoSizer from './AutoSizer';\n\nfunction DefaultChildComponent({height, width, foo, bar}) {\n  return (\n    <div>{`width:${width}, height:${height}, foo:${foo}, bar:${bar}`}</div>\n  );\n}\n\ndescribe('AutoSizer', () => {\n  function getMarkup({\n    bar = 123,\n    ChildComponent = DefaultChildComponent,\n    className = undefined,\n    defaultHeight = undefined,\n    defaultWidth = undefined,\n    disableHeight = false,\n    disableWidth = false,\n    foo = 456,\n    height = 100,\n    onResize,\n    paddingBottom = 0,\n    paddingLeft = 0,\n    paddingRight = 0,\n    paddingTop = 0,\n    style = undefined,\n    width = 200,\n  } = {}) {\n    const wrapperStyle = {\n      boxSizing: 'border-box',\n      height,\n      paddingBottom,\n      paddingLeft,\n      paddingRight,\n      paddingTop,\n      width,\n    };\n\n    mockOffsetSize(width, height);\n\n    return (\n      <div style={wrapperStyle}>\n        <AutoSizer\n          className={className}\n          defaultHeight={defaultHeight}\n          defaultWidth={defaultWidth}\n          disableHeight={disableHeight}\n          disableWidth={disableWidth}\n          onResize={onResize}\n          style={style}>\n          {({height, width}) => (\n            <ChildComponent\n              width={disableWidth ? undefined : width}\n              height={disableHeight ? undefined : height}\n              bar={bar}\n              foo={foo}\n            />\n          )}\n        </AutoSizer>\n      </div>\n    );\n  }\n\n  // AutoSizer uses offsetWidth and offsetHeight.\n  // Jest runs in JSDom which doesn't support measurements APIs.\n  function mockOffsetSize(width, height) {\n    Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {\n      configurable: true,\n      value: height,\n    });\n    Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {\n      configurable: true,\n      value: width,\n    });\n  }\n\n  it('should relay properties to ChildComponent or React child', () => {\n    const rendered = findDOMNode(render(getMarkup()));\n    expect(rendered.textContent).toContain('foo:456');\n    expect(rendered.textContent).toContain('bar:123');\n  });\n\n  it('should set the correct initial width and height of ChildComponent or React child', () => {\n    const rendered = findDOMNode(render(getMarkup()));\n    expect(rendered.textContent).toContain('height:100');\n    expect(rendered.textContent).toContain('width:200');\n  });\n\n  it('should account for padding when calculating the available width and height', () => {\n    const rendered = findDOMNode(\n      render(\n        getMarkup({\n          paddingBottom: 10,\n          paddingLeft: 4,\n          paddingRight: 4,\n          paddingTop: 15,\n        }),\n      ),\n    );\n    expect(rendered.textContent).toContain('height:75');\n    expect(rendered.textContent).toContain('width:192');\n  });\n\n  it('should not update :width if :disableWidth is true', () => {\n    const rendered = findDOMNode(render(getMarkup({disableWidth: true})));\n    expect(rendered.textContent).toContain('height:100');\n    expect(rendered.textContent).toContain('width:undefined');\n  });\n\n  it('should not update :height if :disableHeight is true', () => {\n    const rendered = findDOMNode(render(getMarkup({disableHeight: true})));\n    expect(rendered.textContent).toContain('height:undefined');\n    expect(rendered.textContent).toContain('width:200');\n  });\n\n  async function simulateResize({element, height, width}) {\n    mockOffsetSize(width, height);\n\n    // Trigger detectElementResize library by faking a scroll event\n    // TestUtils Simulate doesn't work here in JSDom so we manually dispatch\n    const trigger = element.querySelector('.contract-trigger');\n    trigger.dispatchEvent(new Event('scroll'));\n\n    // Allow requestAnimationFrame to be invoked before continuing\n    await new Promise(resolve => setTimeout(resolve, 100));\n  }\n\n  it('should update :height after a resize event', async done => {\n    const rendered = findDOMNode(\n      render(\n        getMarkup({\n          height: 100,\n          width: 200,\n        }),\n      ),\n    );\n    expect(rendered.textContent).toContain('height:100');\n    expect(rendered.textContent).toContain('width:200');\n    await simulateResize({element: rendered, height: 400, width: 300});\n    expect(rendered.textContent).toContain('height:400');\n    expect(rendered.textContent).toContain('width:300');\n    done();\n  });\n\n  describe('onResize and (re)render', () => {\n    it('should trigger when size changes', async done => {\n      const onResize = jest.fn();\n      const ChildComponent = jest\n        .fn()\n        .mockImplementation(DefaultChildComponent);\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            ChildComponent,\n            height: 100,\n            onResize,\n            width: 200,\n          }),\n        ),\n      );\n      ChildComponent.mockClear(); // TODO Improve initial check in version 10; see AutoSizer render()\n      expect(onResize).toHaveBeenCalledTimes(1);\n      await simulateResize({element: rendered, height: 400, width: 300});\n      expect(ChildComponent).toHaveBeenCalledTimes(1);\n      expect(onResize).toHaveBeenCalledTimes(2);\n      done();\n    });\n\n    it('should only trigger when height changes for disableWidth == true', async done => {\n      const onResize = jest.fn();\n      const ChildComponent = jest\n        .fn()\n        .mockImplementation(DefaultChildComponent);\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            ChildComponent,\n            disableWidth: true,\n            height: 100,\n            onResize,\n            width: 200,\n          }),\n        ),\n      );\n      ChildComponent.mockClear(); // TODO Improve initial check in version 10; see AutoSizer render()\n      expect(onResize).toHaveBeenCalledTimes(1);\n      await simulateResize({element: rendered, height: 100, width: 300});\n      expect(ChildComponent).toHaveBeenCalledTimes(0);\n      expect(onResize).toHaveBeenCalledTimes(1);\n      await simulateResize({element: rendered, height: 200, width: 300});\n      expect(ChildComponent).toHaveBeenCalledTimes(1);\n      expect(onResize).toHaveBeenCalledTimes(2);\n      done();\n    });\n\n    it('should only trigger when width changes for disableHeight == true', async done => {\n      const onResize = jest.fn();\n      const ChildComponent = jest\n        .fn()\n        .mockImplementation(DefaultChildComponent);\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            ChildComponent,\n            disableHeight: true,\n            height: 100,\n            onResize,\n            width: 200,\n          }),\n        ),\n      );\n      ChildComponent.mockClear(); // TODO Improve initial check in version 10; see AutoSizer render()\n      expect(onResize).toHaveBeenCalledTimes(1);\n      await simulateResize({element: rendered, height: 200, width: 200});\n      expect(ChildComponent).toHaveBeenCalledTimes(0);\n      expect(onResize).toHaveBeenCalledTimes(1);\n      await simulateResize({element: rendered, height: 200, width: 300});\n      expect(ChildComponent).toHaveBeenCalledTimes(1);\n      expect(onResize).toHaveBeenCalledTimes(2);\n      done();\n    });\n  });\n\n  describe('className and style', () => {\n    it('should use a custom :className if specified', () => {\n      const rendered = findDOMNode(render(getMarkup({className: 'foo'})));\n      expect(rendered.firstChild.className).toContain('foo');\n    });\n\n    it('should use a custom :style if specified', () => {\n      const style = {backgroundColor: 'red'};\n      const rendered = findDOMNode(render(getMarkup({style})));\n      expect(rendered.firstChild.style.backgroundColor).toEqual('red');\n    });\n  });\n});\n"
  },
  {
    "path": "source/AutoSizer/AutoSizer.js",
    "content": "/** @flow */\n\nimport * as React from 'react';\nimport createDetectElementResize from '../vendor/detectElementResize';\n\ntype Size = {\n  height: number,\n  width: number,\n};\n\ntype Props = {\n  /** Function responsible for rendering children.*/\n  children: Size => React.Element<*>,\n\n  /** Optional custom CSS class name to attach to root AutoSizer element.  */\n  className?: string,\n\n  /** Default height to use for initial render; useful for SSR */\n  defaultHeight?: number,\n\n  /** Default width to use for initial render; useful for SSR */\n  defaultWidth?: number,\n\n  /** Disable dynamic :height property */\n  disableHeight: boolean,\n\n  /** Disable dynamic :width property */\n  disableWidth: boolean,\n\n  /** Nonce of the inlined stylesheet for Content Security Policy */\n  nonce?: string,\n\n  /** Callback to be invoked on-resize */\n  onResize: Size => void,\n\n  /** Optional inline style */\n  style: ?Object,\n};\n\ntype State = {\n  height: number,\n  width: number,\n};\n\ntype ResizeHandler = (element: HTMLElement, onResize: () => void) => void;\n\ntype DetectElementResize = {\n  addResizeListener: ResizeHandler,\n  removeResizeListener: ResizeHandler,\n};\n\nexport default class AutoSizer extends React.Component<Props, State> {\n  static defaultProps = {\n    onResize: () => {},\n    disableHeight: false,\n    disableWidth: false,\n    style: {},\n  };\n\n  state = {\n    height: this.props.defaultHeight || 0,\n    width: this.props.defaultWidth || 0,\n  };\n\n  _parentNode: ?HTMLElement;\n  _autoSizer: ?HTMLElement;\n  _window: ?any; // uses any instead of Window because Flow doesn't have window type\n  _detectElementResize: DetectElementResize;\n\n  componentDidMount() {\n    const {nonce} = this.props;\n    if (\n      this._autoSizer &&\n      this._autoSizer.parentNode &&\n      this._autoSizer.parentNode.ownerDocument &&\n      this._autoSizer.parentNode.ownerDocument.defaultView &&\n      this._autoSizer.parentNode instanceof\n        this._autoSizer.parentNode.ownerDocument.defaultView.HTMLElement\n    ) {\n      // Delay access of parentNode until mount.\n      // This handles edge-cases where the component has already been unmounted before its ref has been set,\n      // As well as libraries like react-lite which have a slightly different lifecycle.\n      this._parentNode = this._autoSizer.parentNode;\n      this._window = this._autoSizer.parentNode.ownerDocument.defaultView;\n\n      // Defer requiring resize handler in order to support server-side rendering.\n      // See issue #41\n      this._detectElementResize = createDetectElementResize(\n        nonce,\n        this._window,\n      );\n      this._detectElementResize.addResizeListener(\n        this._parentNode,\n        this._onResize,\n      );\n\n      this._onResize();\n    }\n  }\n\n  componentWillUnmount() {\n    if (this._detectElementResize && this._parentNode) {\n      this._detectElementResize.removeResizeListener(\n        this._parentNode,\n        this._onResize,\n      );\n    }\n  }\n\n  render() {\n    const {\n      children,\n      className,\n      disableHeight,\n      disableWidth,\n      style,\n    } = this.props;\n    const {height, width} = this.state;\n\n    // Outer div should not force width/height since that may prevent containers from shrinking.\n    // Inner component should overflow and use calculated width/height.\n    // See issue #68 for more information.\n    const outerStyle: Object = {overflow: 'visible'};\n    const childParams: Object = {};\n\n    if (!disableHeight) {\n      outerStyle.height = 0;\n      childParams.height = height;\n    }\n\n    if (!disableWidth) {\n      outerStyle.width = 0;\n      childParams.width = width;\n    }\n\n    /**\n     * TODO: Avoid rendering children before the initial measurements have been collected.\n     * At best this would just be wasting cycles.\n     * Add this check into version 10 though as it could break too many ref callbacks in version 9.\n     * Note that if default width/height props were provided this would still work with SSR.\n    if (\n      height !== 0 &&\n      width !== 0\n    ) {\n      child = children({ height, width })\n    }\n    */\n\n    return (\n      <div\n        className={className}\n        ref={this._setRef}\n        style={{\n          ...outerStyle,\n          ...style,\n        }}>\n        {children(childParams)}\n      </div>\n    );\n  }\n\n  _onResize = () => {\n    const {disableHeight, disableWidth, onResize} = this.props;\n\n    if (this._parentNode) {\n      // Guard against AutoSizer component being removed from the DOM immediately after being added.\n      // This can result in invalid style values which can result in NaN values if we don't handle them.\n      // See issue #150 for more context.\n\n      const height = this._parentNode.offsetHeight || 0;\n      const width = this._parentNode.offsetWidth || 0;\n\n      const win = this._window || window;\n      const style = win.getComputedStyle(this._parentNode) || {};\n      const paddingLeft = parseInt(style.paddingLeft, 10) || 0;\n      const paddingRight = parseInt(style.paddingRight, 10) || 0;\n      const paddingTop = parseInt(style.paddingTop, 10) || 0;\n      const paddingBottom = parseInt(style.paddingBottom, 10) || 0;\n\n      const newHeight = height - paddingTop - paddingBottom;\n      const newWidth = width - paddingLeft - paddingRight;\n\n      if (\n        (!disableHeight && this.state.height !== newHeight) ||\n        (!disableWidth && this.state.width !== newWidth)\n      ) {\n        this.setState({\n          height: height - paddingTop - paddingBottom,\n          width: width - paddingLeft - paddingRight,\n        });\n\n        onResize({height, width});\n      }\n    }\n  };\n\n  _setRef = (autoSizer: ?HTMLElement) => {\n    this._autoSizer = autoSizer;\n  };\n}\n"
  },
  {
    "path": "source/AutoSizer/AutoSizer.ssr.js",
    "content": "/**\n * @jest-environment node\n */\n\nimport * as React from 'react';\nimport * as ReactDOMServer from 'react-dom/server';\nimport AutoSizer from './AutoSizer';\n\ntest('should render content with default widths and heights initially', () => {\n  const rendered = ReactDOMServer.renderToString(\n    <AutoSizer defaultHeight={100} defaultWidth={200}>\n      {({height, width}) => <div>{`height:${height};width:${width}`}</div>}\n    </AutoSizer>,\n  );\n  expect(rendered).toContain('height:100');\n  expect(rendered).toContain('width:200');\n});\n"
  },
  {
    "path": "source/AutoSizer/index.js",
    "content": "// @flow\n\nexport {default} from './AutoSizer';\nexport {default as AutoSizer} from './AutoSizer';\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.DynamicHeightGrid.example.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport CellMeasurer from './CellMeasurer';\nimport CellMeasurerCache from './CellMeasurerCache';\nimport Grid from '../Grid';\nimport styles from './CellMeasurer.example.css';\n\nexport default class DynamicHeightGrid extends React.PureComponent {\n  static propTypes = {\n    getClassName: PropTypes.func.isRequired,\n    getContent: PropTypes.func.isRequired,\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n    width: PropTypes.number.isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._cache = new CellMeasurerCache({\n      defaultWidth: 150,\n      fixedWidth: true,\n    });\n\n    this._cellRenderer = this._cellRenderer.bind(this);\n  }\n\n  render() {\n    const {width} = this.props;\n\n    return (\n      <Grid\n        className={styles.BodyGrid}\n        columnCount={50}\n        columnWidth={150}\n        deferredMeasurementCache={this._cache}\n        height={400}\n        overscanColumnCount={0}\n        overscanRowCount={2}\n        cellRenderer={this._cellRenderer}\n        rowCount={1000}\n        rowHeight={this._cache.rowHeight}\n        width={width}\n      />\n    );\n  }\n\n  _cellRenderer({columnIndex, key, parent, rowIndex, style}) {\n    const {getClassName, getContent, list} = this.props;\n\n    const datum = list.get((rowIndex + columnIndex) % list.size);\n    const classNames = getClassName({columnIndex, rowIndex});\n    const content = getContent({index: rowIndex, datum});\n\n    return (\n      <CellMeasurer\n        cache={this._cache}\n        columnIndex={columnIndex}\n        key={key}\n        parent={parent}\n        rowIndex={rowIndex}>\n        <div\n          className={classNames}\n          style={{\n            ...style,\n            width: 150,\n          }}>\n          {content}\n        </div>\n      </CellMeasurer>\n    );\n  }\n}\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.DynamicHeightList.example.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport CellMeasurer from './CellMeasurer';\nimport CellMeasurerCache from './CellMeasurerCache';\nimport List from '../List';\nimport styles from './CellMeasurer.example.css';\n\nexport default class DynamicHeightList extends React.PureComponent {\n  static propTypes = {\n    getClassName: PropTypes.func.isRequired,\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n    width: PropTypes.number.isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._cache = new CellMeasurerCache({\n      fixedWidth: true,\n      minHeight: 50,\n    });\n\n    this._rowRenderer = this._rowRenderer.bind(this);\n  }\n\n  render() {\n    const {width} = this.props;\n\n    return (\n      <List\n        className={styles.BodyGrid}\n        deferredMeasurementCache={this._cache}\n        height={400}\n        overscanRowCount={0}\n        rowCount={1000}\n        rowHeight={this._cache.rowHeight}\n        rowRenderer={this._rowRenderer}\n        width={width}\n      />\n    );\n  }\n\n  _rowRenderer({index, key, parent, style}) {\n    const {getClassName, list} = this.props;\n\n    const datum = list.get(index % list.size);\n    const classNames = getClassName({columnIndex: 0, rowIndex: index});\n\n    const imageWidth = 300;\n    const imageHeight = datum.size * (1 + (index % 3));\n\n    const source = `https://www.fillmurray.com/${imageWidth}/${imageHeight}`;\n\n    return (\n      <CellMeasurer\n        cache={this._cache}\n        columnIndex={0}\n        key={key}\n        rowIndex={index}\n        parent={parent}>\n        {({measure, registerChild}) => (\n          <div ref={registerChild} className={classNames} style={style}>\n            <img\n              onLoad={measure}\n              src={source}\n              style={{\n                width: imageWidth,\n              }}\n            />\n          </div>\n        )}\n      </CellMeasurer>\n    );\n  }\n}\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.DynamicHeightTableColumn.example.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport CellMeasurer from './CellMeasurer';\nimport CellMeasurerCache from './CellMeasurerCache';\nimport {Column, Table} from '../Table';\nimport styles from './CellMeasurer.example.css';\n\nexport default class DynamicHeightTableColumn extends React.PureComponent {\n  static propTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n    width: PropTypes.number.isRequired,\n  };\n\n  _cache = new CellMeasurerCache({\n    fixedWidth: true,\n    minHeight: 25,\n  });\n\n  _lastRenderedWidth = this.props.width;\n\n  render() {\n    const {width} = this.props;\n\n    if (this._lastRenderedWidth !== this.props.width) {\n      this._lastRenderedWidth = this.props.width;\n      this._cache.clearAll();\n    }\n\n    return (\n      <Table\n        deferredMeasurementCache={this._cache}\n        headerHeight={20}\n        height={400}\n        overscanRowCount={2}\n        rowClassName={styles.tableRow}\n        rowHeight={this._cache.rowHeight}\n        rowGetter={this._rowGetter}\n        rowCount={1000}\n        width={width}>\n        <Column\n          className={styles.tableColumn}\n          dataKey=\"name\"\n          label=\"Name\"\n          width={125}\n        />\n        <Column\n          className={styles.tableColumn}\n          dataKey=\"color\"\n          label=\"Color\"\n          width={75}\n        />\n        <Column\n          width={width - 200}\n          dataKey=\"random\"\n          label=\"Dynamic text\"\n          cellRenderer={this._columnCellRenderer}\n        />\n      </Table>\n    );\n  }\n\n  _columnCellRenderer = ({dataKey, parent, rowIndex}) => {\n    const {list} = this.props;\n\n    const datum = list.get(rowIndex % list.size);\n    const content = rowIndex % 5 === 0 ? '' : datum.randomLong;\n\n    return (\n      <CellMeasurer\n        cache={this._cache}\n        columnIndex={0}\n        key={dataKey}\n        parent={parent}\n        rowIndex={rowIndex}>\n        <div\n          className={styles.tableColumn}\n          style={{\n            whiteSpace: 'normal',\n          }}>\n          {content}\n        </div>\n      </CellMeasurer>\n    );\n  };\n\n  _rowGetter = ({index}) => {\n    const {list} = this.props;\n\n    return list.get(index % list.size);\n  };\n}\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.DynamicWidthGrid.example.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport CellMeasurer from './CellMeasurer';\nimport CellMeasurerCache from './CellMeasurerCache';\nimport Grid from '../Grid';\nimport styles from './CellMeasurer.example.css';\n\nexport default class DynamicWidthGrid extends React.PureComponent {\n  static propTypes = {\n    getClassName: PropTypes.func.isRequired,\n    getContent: PropTypes.func.isRequired,\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n    width: PropTypes.number.isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._cache = new CellMeasurerCache({\n      defaultWidth: 100,\n      fixedHeight: true,\n    });\n\n    this._cellRenderer = this._cellRenderer.bind(this);\n  }\n\n  render() {\n    const {width} = this.props;\n\n    return (\n      <Grid\n        className={styles.BodyGrid}\n        columnCount={1000}\n        columnWidth={this._cache.columnWidth}\n        deferredMeasurementCache={this._cache}\n        height={400}\n        overscanColumnCount={0}\n        overscanRowCount={2}\n        cellRenderer={this._cellRenderer}\n        rowCount={50}\n        rowHeight={35}\n        width={width}\n      />\n    );\n  }\n\n  _cellRenderer({columnIndex, key, parent, rowIndex, style}) {\n    const {getClassName, getContent, list} = this.props;\n\n    const datum = list.get((rowIndex + columnIndex) % list.size);\n    const classNames = getClassName({columnIndex, rowIndex});\n    const content = getContent({index: columnIndex, datum, long: false});\n\n    return (\n      <CellMeasurer\n        cache={this._cache}\n        columnIndex={columnIndex}\n        key={key}\n        parent={parent}\n        rowIndex={rowIndex}>\n        <div\n          className={classNames}\n          style={{\n            ...style,\n            height: 35,\n            whiteSpace: 'nowrap',\n          }}>\n          {content}\n        </div>\n      </CellMeasurer>\n    );\n  }\n}\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.DynamicWidthMultiGrid.example.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport CellMeasurer from './CellMeasurer';\nimport CellMeasurerCache from './CellMeasurerCache';\nimport MultiGrid from '../MultiGrid';\nimport styles from './CellMeasurer.example.css';\n\nexport default class DynamicWidthMultiGrid extends React.PureComponent {\n  static propTypes = {\n    getClassName: PropTypes.func.isRequired,\n    getContent: PropTypes.func.isRequired,\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n    width: PropTypes.number.isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._cache = new CellMeasurerCache({\n      defaultHeight: 30,\n      defaultWidth: 150,\n      fixedHeight: true,\n    });\n\n    this._cellRenderer = this._cellRenderer.bind(this);\n  }\n\n  render() {\n    const {width} = this.props;\n\n    return (\n      <MultiGrid\n        className={styles.BodyGrid}\n        columnCount={50}\n        columnWidth={this._cache.columnWidth}\n        deferredMeasurementCache={this._cache}\n        fixedColumnCount={1}\n        fixedRowCount={0}\n        height={400}\n        overscanColumnCount={0}\n        overscanRowCount={0}\n        cellRenderer={this._cellRenderer}\n        rowCount={50}\n        rowHeight={30}\n        width={width}\n      />\n    );\n  }\n\n  _cellRenderer({columnIndex, key, parent, rowIndex, style}) {\n    const {getClassName, getContent, list} = this.props;\n\n    const datum = list.get((rowIndex + columnIndex) % list.size);\n    const classNames = getClassName({columnIndex, rowIndex});\n    let content = getContent({index: rowIndex, datum, long: false});\n\n    if (columnIndex === 0) {\n      content = content.substr(0, 50);\n    }\n\n    return (\n      <CellMeasurer\n        cache={this._cache}\n        columnIndex={columnIndex}\n        key={key}\n        parent={parent}\n        rowIndex={rowIndex}>\n        <div\n          className={classNames}\n          style={{\n            ...style,\n            whiteSpace: 'nowrap',\n          }}>\n          {content}\n        </div>\n      </CellMeasurer>\n    );\n  }\n}\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.example.css",
    "content": ".GridRow {\n  margin-top: 15px;\n  display: flex;\n  flex-direction: row;\n}\n.GridColumn {\n  display: flex;\n  flex-direction: column;\n  flex: 1 1 auto;\n}\n.LeftSideGridContainer {\n  flex: 0 0 50px;\n}\n\n.BodyGrid {\n  width: 100%;\n  border: 1px solid #e0e0e0;\n}\n\n.evenRow,\n.oddRow {\n  border-bottom: 1px solid #e0e0e0;\n}\n.oddRow {\n  background-color: #fafafa;\n}\n\n.cell {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  padding: 0.5em 1em;\n}\n.cell {\n  border-right: 1px solid #e0e0e0;\n  border-bottom: 1px solid #e0e0e0;\n}\n\n.uniformSizeCell {\n  padding: 0.5rem;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  overflow: hidden;\n}\n\n.tableRow {\n  border-bottom: 1px solid #eee;\n}\n.tableColumn {\n  padding: 5px 15px 5px 0;\n}\n\n.Tab {\n  border: 1px solid #ddd;\n  border-radius: 4px;\n  padding: 4px 6px;\n  outline: none;\n  background: #eee;\n  margin: 4px;\n  cursor: pointer;\n}\n\n.ActiveTab {\n  background-color: #4db6ac;\n  border: 1px solid #3ca59b;\n  color: rgba(255, 255, 255, 0.8);\n  cursor: default;\n}"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.example.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport AutoSizer from '../AutoSizer';\nimport clsx from 'clsx';\nimport styles from './CellMeasurer.example.css';\nimport DynamicWidthGrid from './CellMeasurer.DynamicWidthGrid.example.js';\nimport DynamicHeightGrid from './CellMeasurer.DynamicHeightGrid.example.js';\nimport DynamicWidthMultiGrid from './CellMeasurer.DynamicWidthMultiGrid.example.js';\nimport DynamicHeightList from './CellMeasurer.DynamicHeightList.example.js';\nimport DynamicHeightTableColumn from './CellMeasurer.DynamicHeightTableColumn.example.js';\n\nconst demoComponents = [\n  DynamicWidthGrid,\n  DynamicHeightGrid,\n  DynamicWidthMultiGrid,\n  DynamicHeightList,\n  DynamicHeightTableColumn,\n];\n\nexport default class CellMeasurerExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      currentTab: 0,\n    };\n\n    this._onClick = this._onClick.bind(this);\n  }\n\n  render() {\n    const {list} = this.context;\n    const {currentTab} = this.state;\n\n    const buttonProps = {\n      currentTab,\n      onClick: this._onClick,\n    };\n\n    const DemoComponent = demoComponents[currentTab];\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"CellMeasurer\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/CellMeasurer/CellMeasurer.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/CellMeasurer.md\"\n        />\n\n        <ContentBoxParagraph>\n          This component can be used to just-in-time measure dynamic content\n          (eg. messages in a chat interface).\n        </ContentBoxParagraph>\n\n        <AutoSizer disableHeight>\n          {({width}) => (\n            <div style={{width}}>\n              <div>\n                <strong>Grid</strong>:\n                <Tab id={0} {...buttonProps}>\n                  dynamic width text\n                </Tab>\n                <Tab id={1} {...buttonProps}>\n                  dynamic height text\n                </Tab>\n                <strong>MultiGrid</strong>:\n                <Tab id={2} {...buttonProps}>\n                  dynamic width text\n                </Tab>\n                <strong>List</strong>:\n                <Tab id={3} {...buttonProps}>\n                  dynamic height image\n                </Tab>\n                <strong>Table</strong>:\n                <Tab id={4} {...buttonProps}>\n                  mixed fixed and dynamic height text\n                </Tab>\n              </div>\n\n              <DemoComponent\n                getClassName={getClassName}\n                getContent={getContent}\n                list={list}\n                width={width}\n              />\n            </div>\n          )}\n        </AutoSizer>\n      </ContentBox>\n    );\n  }\n\n  _onClick(id) {\n    this.setState({\n      currentTab: id,\n    });\n  }\n}\n\nfunction getClassName({columnIndex, rowIndex}) {\n  const rowClass = rowIndex % 2 === 0 ? styles.evenRow : styles.oddRow;\n\n  return clsx(rowClass, styles.cell, {\n    [styles.centeredCell]: columnIndex > 2,\n  });\n}\n\nfunction getContent({index, datum, long = true}) {\n  switch (index % 3) {\n    case 0:\n      return datum.color;\n    case 1:\n      return datum.name;\n    case 2:\n      return long ? datum.randomLong : datum.random;\n  }\n}\n\nfunction Tab({children, currentTab, id, onClick}) {\n  const classNames = clsx(styles.Tab, {\n    [styles.ActiveTab]: currentTab === id,\n  });\n\n  return (\n    <button className={classNames} onClick={() => onClick(id)}>\n      {children}\n    </button>\n  );\n}\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport CellMeasurer from './CellMeasurer';\nimport CellMeasurerCache, {\n  DEFAULT_HEIGHT,\n  DEFAULT_WIDTH,\n} from './CellMeasurerCache';\n\n// Accounts for the fact that JSDom doesn't support measurements.\nfunction mockClientWidthAndHeight(\n  {height, width},\n  object = HTMLElement.prototype,\n) {\n  const heightFn = jest.fn().mockReturnValue(height);\n  const widthFn = jest.fn().mockReturnValue(width);\n\n  Object.defineProperty(object, 'offsetHeight', {\n    configurable: true,\n    get: heightFn,\n  });\n\n  Object.defineProperty(object, 'offsetWidth', {\n    configurable: true,\n    get: widthFn,\n  });\n\n  return {\n    heightFn,\n    widthFn,\n  };\n}\n\nfunction createParent({cache, invalidateCellSizeAfterRender = jest.fn()} = {}) {\n  return {\n    invalidateCellSizeAfterRender,\n    props: {\n      deferredMeasurementCache: cache,\n    },\n  };\n}\n\nfunction renderHelper({\n  cache = new CellMeasurerCache({\n    fixedWidth: true,\n  }),\n  children = <div />,\n  parent,\n} = {}) {\n  render(\n    <CellMeasurer\n      cache={cache}\n      columnIndex={0}\n      parent={parent}\n      rowIndex={0}\n      style={{}}>\n      {children}\n    </CellMeasurer>,\n  );\n}\n\ndescribe('CellMeasurer', () => {\n  it('componentDidMount() should measure content that is not already in the cache', () => {\n    const cache = new CellMeasurerCache({fixedWidth: true});\n    const parent = createParent({cache});\n\n    const {heightFn, widthFn} = mockClientWidthAndHeight({\n      height: 20,\n      width: 100,\n    });\n\n    expect(heightFn).toHaveBeenCalledTimes(0);\n    expect(widthFn).toHaveBeenCalledTimes(0);\n    expect(cache.has(0, 0)).toBe(false);\n\n    renderHelper({cache, parent});\n\n    expect(parent.invalidateCellSizeAfterRender).toHaveBeenCalled();\n    expect(heightFn).toHaveBeenCalledTimes(1);\n    expect(widthFn).toHaveBeenCalledTimes(1);\n    expect(cache.has(0, 0)).toBe(true);\n    expect(cache.getWidth(0, 0)).toBe(100);\n    expect(cache.getHeight(0, 0)).toBe(20);\n  });\n\n  it('componentDidMount() should not measure content that is already in the cache', () => {\n    const cache = new CellMeasurerCache({fixedWidth: true});\n    cache.set(0, 0, 100, 20);\n\n    const parent = createParent({cache});\n\n    const {heightFn, widthFn} = mockClientWidthAndHeight({\n      height: 20,\n      width: 100,\n    });\n\n    expect(cache.has(0, 0)).toBe(true);\n\n    renderHelper({cache, parent});\n\n    expect(parent.invalidateCellSizeAfterRender).not.toHaveBeenCalled();\n    expect(heightFn).toHaveBeenCalledTimes(0);\n    expect(widthFn).toHaveBeenCalledTimes(0);\n  });\n\n  it('componentDidUpdate() should measure content that is not already in the cache', () => {\n    const cache = new CellMeasurerCache({fixedWidth: true});\n    const parent = createParent({cache});\n\n    renderHelper({cache, parent});\n\n    cache.clear(0, 0);\n    parent.invalidateCellSizeAfterRender.mockReset();\n\n    expect(cache.has(0, 0)).toBe(false);\n    expect(cache.getWidth(0, 0)).toBe(DEFAULT_WIDTH);\n    expect(cache.getHeight(0, 0)).toBe(DEFAULT_HEIGHT);\n\n    const {heightFn, widthFn} = mockClientWidthAndHeight({\n      height: 20,\n      width: 100,\n    });\n\n    renderHelper({cache, parent});\n\n    expect(cache.has(0, 0)).toBe(true);\n\n    expect(parent.invalidateCellSizeAfterRender).toHaveBeenCalled();\n    expect(heightFn).toHaveBeenCalledTimes(1);\n    expect(widthFn).toHaveBeenCalledTimes(1);\n    expect(cache.getWidth(0, 0)).toBe(100);\n    expect(cache.getHeight(0, 0)).toBe(20);\n  });\n\n  it('componentDidUpdate() should not measure content that is already in the cache', () => {\n    const cache = new CellMeasurerCache({fixedWidth: true});\n    cache.set(0, 0, 100, 20);\n\n    const parent = createParent({cache});\n\n    expect(cache.has(0, 0)).toBe(true);\n\n    const {heightFn, widthFn} = mockClientWidthAndHeight({\n      height: 20,\n      width: 100,\n    });\n\n    renderHelper({cache, parent});\n    renderHelper({cache, parent});\n\n    expect(parent.invalidateCellSizeAfterRender).not.toHaveBeenCalled();\n    expect(heightFn).toHaveBeenCalledTimes(0);\n    expect(widthFn).toHaveBeenCalledTimes(0);\n  });\n\n  it('registerChild() should measure content that is not already in the cache', () => {\n    const cache = new CellMeasurerCache({fixedWidth: true});\n    const parent = createParent({cache});\n\n    const element = document.createElement('div');\n    const {heightFn, widthFn} = mockClientWidthAndHeight(\n      {\n        height: 20,\n        width: 100,\n      },\n      element,\n    );\n\n    expect(heightFn).toHaveBeenCalledTimes(0);\n    expect(widthFn).toHaveBeenCalledTimes(0);\n    expect(cache.has(0, 0)).toBe(false);\n\n    renderHelper({\n      cache,\n      parent,\n      children({registerChild}) {\n        registerChild(element);\n        return null;\n      },\n    });\n\n    expect(parent.invalidateCellSizeAfterRender).toHaveBeenCalled();\n    expect(heightFn).toHaveBeenCalledTimes(1);\n    expect(widthFn).toHaveBeenCalledTimes(1);\n    expect(cache.has(0, 0)).toBe(true);\n    expect(cache.getWidth(0, 0)).toBe(100);\n    expect(cache.getHeight(0, 0)).toBe(20);\n  });\n\n  it('registerChild() should not measure content that is already in the cache', () => {\n    const cache = new CellMeasurerCache({fixedWidth: true});\n    cache.set(0, 0, 100, 20);\n\n    const parent = createParent({cache});\n\n    const element = document.createElement('div');\n    const {heightFn, widthFn} = mockClientWidthAndHeight(\n      {\n        height: 20,\n        width: 100,\n      },\n      element,\n    );\n\n    expect(cache.has(0, 0)).toBe(true);\n\n    renderHelper({\n      cache,\n      parent,\n      children({registerChild}) {\n        registerChild(element);\n        return null;\n      },\n    });\n\n    expect(parent.invalidateCellSizeAfterRender).not.toHaveBeenCalled();\n    expect(heightFn).toHaveBeenCalledTimes(0);\n    expect(widthFn).toHaveBeenCalledTimes(0);\n  });\n\n  it('should pass a :measure param to a function child', () => {\n    const cache = new CellMeasurerCache({\n      fixedWidth: true,\n    });\n\n    const children = jest.fn().mockReturnValue(<div />);\n\n    renderHelper({cache, children});\n\n    expect(children).toHaveBeenCalled();\n\n    const params = children.mock.calls[0][0];\n\n    expect(typeof params.measure === 'function').toBe(true);\n  });\n\n  it('should still update cache without a parent Grid', () => {\n    jest.spyOn(console, 'warn');\n\n    mockClientWidthAndHeight({height: 20, width: 100});\n\n    const cache = new CellMeasurerCache({\n      fixedWidth: true,\n    });\n\n    renderHelper({cache}); // No parent Grid\n\n    expect(cache.has(0, 0)).toBe(true);\n\n    expect(console.warn).not.toHaveBeenCalled();\n  });\n\n  // See issue #593\n  it('should explicitly set width/height style to \"auto\" before re-measuring', () => {\n    const cache = new CellMeasurerCache({\n      fixedWidth: true,\n    });\n    const parent = createParent({cache});\n    const child = jest\n      .fn()\n      .mockReturnValue(<div style={{width: 100, height: 30}} />);\n\n    let measurer;\n    const node = findDOMNode(\n      render(\n        <CellMeasurer\n          ref={ref => {\n            measurer = ref;\n          }}\n          cache={cache}\n          columnIndex={0}\n          parent={parent}\n          rowIndex={0}\n          style={{}}>\n          {child}\n        </CellMeasurer>,\n      ),\n    );\n\n    const styleHeights = [30];\n    const styleWidths = [100];\n    Object.defineProperties(node.style, {\n      height: {\n        get: () => styleHeights[styleHeights.length - 1],\n        set: value => styleHeights.push(value),\n      },\n      width: {\n        get: () => styleWidths[styleWidths.length - 1],\n        set: value => styleWidths.push(value),\n      },\n    });\n\n    const {height, width} = measurer._getCellMeasurements(node);\n    expect(height).toBeGreaterThan(0);\n    expect(width).toBeGreaterThan(0);\n    expect(styleHeights).toEqual([30, 'auto', 30]);\n    expect(styleWidths).toEqual([100, 100]);\n  });\n\n  // See issue #660\n  it('should reset width/height style values after measuring with style \"auto\"', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n    });\n    const parent = createParent({cache});\n    const child = jest\n      .fn()\n      .mockReturnValue(<div style={{width: 100, height: 30}} />);\n\n    const node = findDOMNode(\n      render(\n        <CellMeasurer\n          cache={cache}\n          columnIndex={0}\n          parent={parent}\n          rowIndex={0}\n          style={{}}>\n          {child}\n        </CellMeasurer>,\n      ),\n    );\n\n    node.style.width = 200;\n    node.style.height = 60;\n\n    child.mock.calls[0][0].measure();\n\n    expect(node.style.height).toBe('30px');\n    expect(node.style.width).toBe('100px');\n  });\n});\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurer.js",
    "content": "/** @flow */\nimport * as React from 'react';\nimport type {CellMeasureCache} from './types';\nimport {cloneElement} from 'react';\n\ntype Children = (params: {measure: () => void}) => React.Element<*>;\n\ntype Cell = {\n  columnIndex: number,\n  rowIndex: number,\n};\n\ntype Props = {\n  cache: CellMeasureCache,\n  children: Children | React.Element<*>,\n  columnIndex?: number,\n  index?: number,\n  parent: {\n    invalidateCellSizeAfterRender?: (cell: Cell) => void,\n    recomputeGridSize?: (cell: Cell) => void,\n  },\n  rowIndex?: number,\n};\n\n/**\n * Wraps a cell and measures its rendered content.\n * Measurements are stored in a per-cell cache.\n * Cached-content is not be re-measured.\n */\nexport default class CellMeasurer extends React.PureComponent<Props> {\n  static __internalCellMeasurerFlag = false;\n\n  _child: {current: HTMLElement | null} = React.createRef();\n\n  componentDidMount() {\n    this._maybeMeasureCell();\n  }\n\n  componentDidUpdate() {\n    this._maybeMeasureCell();\n  }\n\n  render() {\n    const {children} = this.props;\n\n    const resolvedChildren =\n      typeof children === 'function'\n        ? children({measure: this._measure, registerChild: this._registerChild})\n        : children;\n\n    if (resolvedChildren === null) {\n      return resolvedChildren;\n    }\n\n    return cloneElement(resolvedChildren, {\n      ref: node => {\n        if (typeof resolvedChildren.ref === 'function') {\n          resolvedChildren.ref(node);\n        } else if (resolvedChildren.ref) {\n          resolvedChildren.ref.current = node;\n        }\n        this._child.current = node;\n      },\n    });\n  }\n\n  _getCellMeasurements() {\n    const {cache} = this.props;\n\n    const node = this._child.current;\n\n    // TODO Check for a bad combination of fixedWidth and missing numeric width or vice versa with height\n\n    if (\n      node &&\n      node.ownerDocument &&\n      node.ownerDocument.defaultView &&\n      node instanceof node.ownerDocument.defaultView.HTMLElement\n    ) {\n      const styleWidth = node.style.width;\n      const styleHeight = node.style.height;\n\n      // If we are re-measuring a cell that has already been measured,\n      // It will have a hard-coded width/height from the previous measurement.\n      // The fact that we are measuring indicates this measurement is probably stale,\n      // So explicitly clear it out (eg set to \"auto\") so we can recalculate.\n      // See issue #593 for more info.\n      // Even if we are measuring initially- if we're inside of a MultiGrid component,\n      // Explicitly clear width/height before measuring to avoid being tainted by another Grid.\n      // eg top/left Grid renders before bottom/right Grid\n      // Since the CellMeasurerCache is shared between them this taints derived cell size values.\n      if (!cache.hasFixedWidth()) {\n        node.style.width = 'auto';\n      }\n      if (!cache.hasFixedHeight()) {\n        node.style.height = 'auto';\n      }\n\n      const height = Math.ceil(node.offsetHeight);\n      const width = Math.ceil(node.offsetWidth);\n\n      // Reset after measuring to avoid breaking styles; see #660\n      if (styleWidth) {\n        node.style.width = styleWidth;\n      }\n      if (styleHeight) {\n        node.style.height = styleHeight;\n      }\n\n      return {height, width};\n    } else {\n      return {height: 0, width: 0};\n    }\n  }\n\n  _maybeMeasureCell() {\n    const {\n      cache,\n      columnIndex = 0,\n      parent,\n      rowIndex = this.props.index || 0,\n    } = this.props;\n\n    if (!cache.has(rowIndex, columnIndex)) {\n      const {height, width} = this._getCellMeasurements();\n\n      cache.set(rowIndex, columnIndex, width, height);\n\n      // If size has changed, let Grid know to re-render.\n      if (\n        parent &&\n        typeof parent.invalidateCellSizeAfterRender === 'function'\n      ) {\n        parent.invalidateCellSizeAfterRender({\n          columnIndex,\n          rowIndex,\n        });\n      }\n    }\n  }\n\n  _measure = () => {\n    const {\n      cache,\n      columnIndex = 0,\n      parent,\n      rowIndex = this.props.index || 0,\n    } = this.props;\n\n    const {height, width} = this._getCellMeasurements();\n\n    if (\n      height !== cache.getHeight(rowIndex, columnIndex) ||\n      width !== cache.getWidth(rowIndex, columnIndex)\n    ) {\n      cache.set(rowIndex, columnIndex, width, height);\n\n      if (parent && typeof parent.recomputeGridSize === 'function') {\n        parent.recomputeGridSize({\n          columnIndex,\n          rowIndex,\n        });\n      }\n    }\n  };\n\n  _registerChild = element => {\n    if (element && !(element instanceof Element)) {\n      console.warn(\n        'CellMeasurer registerChild expects to be passed Element or null',\n      );\n    }\n    this._child.current = element;\n    if (element) {\n      this._maybeMeasureCell();\n    }\n  };\n}\n\n// Used for DEV mode warning check\nif (process.env.NODE_ENV !== 'production') {\n  CellMeasurer.__internalCellMeasurerFlag = true;\n}\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurerCache.jest.js",
    "content": "import CellMeasurerCache, {\n  DEFAULT_HEIGHT,\n  DEFAULT_WIDTH,\n} from './CellMeasurerCache';\n\ndescribe('CellMeasurerCache', () => {\n  it('should override defaultHeight/defaultWidth if minHeight/minWidth are greater', () => {\n    const cache = new CellMeasurerCache({\n      defaultHeight: 20,\n      defaultWidth: 100,\n      fixedHeight: true,\n      fixedWidth: true,\n      minHeight: 30,\n      minWidth: 150,\n    });\n    cache.set(0, 0, 50, 10);\n    expect(cache.getHeight(0, 0)).toBe(30);\n    expect(cache.getWidth(0, 0)).toBe(150);\n    expect(cache.rowHeight({index: 0})).toBe(30);\n    expect(cache.columnWidth({index: 0})).toBe(150);\n  });\n\n  it('should correctly report cache status', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    expect(cache.has(0, 0)).toBe(false);\n  });\n\n  it('should cache cells', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    cache.set(0, 0, 100, 20);\n    expect(cache.has(0, 0)).toBe(true);\n  });\n\n  it('should return the correct default sizes for uncached cells if specified', () => {\n    spyOn(console, 'warn'); // Ignore warning about variable width and height\n\n    const cache = new CellMeasurerCache({\n      defaultHeight: 20,\n      defaultWidth: 100,\n      minHeight: 15,\n      minWidth: 80,\n    });\n    expect(cache.getWidth(0, 0)).toBe(100);\n    expect(cache.getHeight(0, 0)).toBe(20);\n    cache.set(0, 0, 70, 10);\n    expect(cache.getWidth(0, 0)).toBe(80);\n    expect(cache.getHeight(0, 0)).toBe(15);\n  });\n\n  it('should clear a single cached cell', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    cache.set(0, 0, 100, 20);\n    cache.set(1, 0, 100, 20);\n    expect(cache.has(0, 0)).toBe(true);\n    expect(cache.has(1, 0)).toBe(true);\n    cache.clear(0, 0);\n    expect(cache.has(0, 0)).toBe(false);\n    expect(cache.has(1, 0)).toBe(true);\n  });\n\n  it('should clear a single cached row cell in column 0 when columnIndex param is absent', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    cache.set(0, 0, 100, 20);\n    cache.set(1, 0, 100, 20);\n    expect(cache.has(0, 0)).toBe(true);\n    expect(cache.has(1, 0)).toBe(true);\n    cache.clear(0);\n    expect(cache.has(0, 0)).toBe(false);\n    expect(cache.has(1, 0)).toBe(true);\n  });\n\n  it('should clear all cached cells', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    cache.set(0, 0, 100, 20);\n    cache.set(1, 0, 100, 20);\n    expect(cache.has(0, 0)).toBe(true);\n    expect(cache.has(1, 0)).toBe(true);\n    cache.clearAll();\n    expect(cache.has(0, 0)).toBe(false);\n    expect(cache.has(1, 0)).toBe(false);\n  });\n\n  it('should clear row and column counts when clearing all cells', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    cache.set(0, 0, 100, 20);\n    cache.set(1, 0, 100, 20);\n    expect(cache._rowCount).toBe(2);\n    expect(cache._columnCount).toBe(1);\n    cache.clearAll();\n    expect(cache._rowCount).toBe(0);\n    expect(cache._columnCount).toBe(0);\n  });\n\n  it('should support a custom :keyMapper', () => {\n    const keyMapper = jest.fn();\n    keyMapper.mockReturnValue('a');\n\n    spyOn(console, 'warn'); // Ignore warning about variable width and height\n\n    const cache = new CellMeasurerCache({\n      defaultHeight: 30,\n      defaultWidth: 50,\n      keyMapper,\n    });\n    cache.set(0, 0, 100, 20);\n    expect(cache.has(0, 0)).toBe(true);\n\n    // Changing the returned key should cause cache misses\n    keyMapper.mockReset();\n    keyMapper.mockReturnValue('b');\n    expect(cache.has(0, 0)).toBe(false);\n    expect(cache.columnWidth(0)).toBe(50);\n    expect(cache.rowHeight(0)).toBe(30);\n    expect(keyMapper.mock.calls).toHaveLength(3);\n\n    // Restoring it should fix\n    keyMapper.mockReset();\n    keyMapper.mockReturnValue('a');\n    expect(cache.has(0, 0)).toBe(true);\n    expect(cache.columnWidth(0)).toBe(100);\n    expect(cache.rowHeight(0)).toBe(20);\n    expect(keyMapper.mock.calls).toHaveLength(3);\n  });\n\n  it('should provide a Grid-compatible :columnWidth method', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n    });\n    expect(cache.columnWidth({index: 0})).toBe(DEFAULT_WIDTH);\n    cache.set(0, 0, 100, 50);\n    expect(cache.columnWidth({index: 0})).toBe(100);\n    expect(cache.columnWidth({index: 1})).toBe(DEFAULT_WIDTH);\n    cache.set(1, 0, 75, 50);\n    expect(cache.columnWidth({index: 0})).toBe(100);\n    cache.set(2, 0, 125, 50);\n    expect(cache.columnWidth({index: 0})).toBe(125);\n  });\n\n  it('should provide a Grid-compatible :rowHeight method', () => {\n    const cache = new CellMeasurerCache({\n      fixedWidth: true,\n    });\n    expect(cache.rowHeight({index: 0})).toBe(DEFAULT_HEIGHT);\n    cache.set(0, 0, 100, 50);\n    expect(cache.rowHeight({index: 0})).toBe(50);\n    expect(cache.rowHeight({index: 1})).toBe(DEFAULT_HEIGHT);\n    cache.set(0, 1, 100, 25);\n    expect(cache.rowHeight({index: 0})).toBe(50);\n    cache.set(0, 2, 100, 75);\n    expect(cache.rowHeight({index: 0})).toBe(75);\n  });\n\n  it('should return the :defaultWidth for :columnWidth if not measured', () => {\n    const cache = new CellMeasurerCache({\n      defaultWidth: 25,\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    expect(cache.columnWidth({index: 0})).toBe(25);\n  });\n\n  it('should return the :defaultHeight for :rowHeight if not measured', () => {\n    const cache = new CellMeasurerCache({\n      defaultHeight: 25,\n      fixedHeight: true,\n      fixedWidth: true,\n    });\n    expect(cache.rowHeight({index: 0})).toBe(25);\n  });\n\n  it('should recalculate cached :columnWidth when cells are cleared', () => {\n    const cache = new CellMeasurerCache({\n      fixedHeight: true,\n    });\n    expect(cache.columnWidth({index: 0})).toBe(DEFAULT_WIDTH);\n    cache.set(0, 0, 125, 50);\n    expect(cache.columnWidth({index: 0})).toBe(125);\n    cache.set(1, 0, 150, 50);\n    expect(cache.columnWidth({index: 0})).toBe(150);\n    cache.clear(1, 0);\n    expect(cache.columnWidth({index: 0})).toBe(125);\n    cache.clear(0, 0);\n    expect(cache.columnWidth({index: 0})).toBe(DEFAULT_WIDTH);\n    cache.set(0, 0, 125, 50);\n    expect(cache.columnWidth({index: 0})).toBe(125);\n    cache.clearAll();\n    expect(cache.columnWidth({index: 0})).toBe(DEFAULT_WIDTH);\n  });\n\n  it('should recalculate cached :rowHeight when cells are cleared', () => {\n    const cache = new CellMeasurerCache({\n      fixedWidth: true,\n    });\n    expect(cache.rowHeight({index: 0})).toBe(DEFAULT_HEIGHT);\n    cache.set(0, 0, 125, 50);\n    expect(cache.rowHeight({index: 0})).toBe(50);\n    cache.set(0, 1, 150, 75);\n    expect(cache.rowHeight({index: 0})).toBe(75);\n    cache.clear(0, 1);\n    expect(cache.rowHeight({index: 0})).toBe(50);\n    cache.clear(0, 0);\n    expect(cache.rowHeight({index: 0})).toBe(DEFAULT_HEIGHT);\n    cache.set(0, 0, 125, 50);\n    expect(cache.rowHeight({index: 0})).toBe(50);\n    cache.clearAll();\n    expect(cache.rowHeight({index: 0})).toBe(DEFAULT_HEIGHT);\n  });\n\n  describe('DEV mode', () => {\n    it('should warn about dynamic width and height configurations', () => {\n      spyOn(console, 'warn');\n\n      const cache = new CellMeasurerCache({\n        fixedHeight: false,\n        fixedWidth: false,\n      });\n\n      expect(cache.hasFixedHeight()).toBe(false);\n      expect(cache.hasFixedWidth()).toBe(false);\n      expect(console.warn).toHaveBeenCalledWith(\n        \"CellMeasurerCache should only measure a cell's width or height. \" +\n          'You have configured CellMeasurerCache to measure both. ' +\n          'This will result in poor performance.',\n      );\n    });\n\n    it('should warn about dynamic width with a defaultWidth of 0', () => {\n      spyOn(console, 'warn');\n\n      const cache = new CellMeasurerCache({\n        defaultWidth: 0,\n        fixedHeight: true,\n      });\n\n      expect(cache.getWidth(0, 0)).toBe(0);\n      expect(console.warn).toHaveBeenCalledWith(\n        'Fixed width CellMeasurerCache should specify a :defaultWidth greater than 0. ' +\n          'Failing to do so will lead to unnecessary layout and poor performance.',\n      );\n    });\n\n    it('should warn about dynamic height with a defaultHeight of 0', () => {\n      spyOn(console, 'warn');\n\n      const cache = new CellMeasurerCache({\n        defaultHeight: 0,\n        fixedWidth: true,\n      });\n\n      expect(cache.getHeight(0, 0)).toBe(0);\n      expect(console.warn).toHaveBeenCalledWith(\n        'Fixed height CellMeasurerCache should specify a :defaultHeight greater than 0. ' +\n          'Failing to do so will lead to unnecessary layout and poor performance.',\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "source/CellMeasurer/CellMeasurerCache.js",
    "content": "/** @flow */\n\nimport type {CellMeasureCache} from './types';\n\nexport const DEFAULT_HEIGHT = 30;\nexport const DEFAULT_WIDTH = 100;\n\n// Enables more intelligent mapping of a given column and row index to an item ID.\n// This prevents a cell cache from being invalidated when its parent collection is modified.\ntype KeyMapper = (rowIndex: number, columnIndex: number) => any;\n\ntype CellMeasurerCacheParams = {\n  defaultHeight?: number,\n  defaultWidth?: number,\n  fixedHeight?: boolean,\n  fixedWidth?: boolean,\n  minHeight?: number,\n  minWidth?: number,\n  keyMapper?: KeyMapper,\n};\n\ntype Cache = {\n  [key: any]: number,\n};\n\ntype IndexParam = {\n  index: number,\n};\n\n/**\n * Caches measurements for a given cell.\n */\nexport default class CellMeasurerCache implements CellMeasureCache {\n  _cellHeightCache: Cache = {};\n  _cellWidthCache: Cache = {};\n  _columnWidthCache: Cache = {};\n  _rowHeightCache: Cache = {};\n  _defaultHeight: number;\n  _defaultWidth: number;\n  _minHeight: number;\n  _minWidth: number;\n  _keyMapper: KeyMapper;\n  _hasFixedHeight: boolean;\n  _hasFixedWidth: boolean;\n  _columnCount = 0;\n  _rowCount = 0;\n\n  constructor(params: CellMeasurerCacheParams = {}) {\n    const {\n      defaultHeight,\n      defaultWidth,\n      fixedHeight,\n      fixedWidth,\n      keyMapper,\n      minHeight,\n      minWidth,\n    } = params;\n\n    this._hasFixedHeight = fixedHeight === true;\n    this._hasFixedWidth = fixedWidth === true;\n    this._minHeight = minHeight || 0;\n    this._minWidth = minWidth || 0;\n    this._keyMapper = keyMapper || defaultKeyMapper;\n\n    this._defaultHeight = Math.max(\n      this._minHeight,\n      typeof defaultHeight === 'number' ? defaultHeight : DEFAULT_HEIGHT,\n    );\n    this._defaultWidth = Math.max(\n      this._minWidth,\n      typeof defaultWidth === 'number' ? defaultWidth : DEFAULT_WIDTH,\n    );\n\n    if (process.env.NODE_ENV !== 'production') {\n      if (this._hasFixedHeight === false && this._hasFixedWidth === false) {\n        console.warn(\n          \"CellMeasurerCache should only measure a cell's width or height. \" +\n            'You have configured CellMeasurerCache to measure both. ' +\n            'This will result in poor performance.',\n        );\n      }\n\n      if (this._hasFixedHeight === false && this._defaultHeight === 0) {\n        console.warn(\n          'Fixed height CellMeasurerCache should specify a :defaultHeight greater than 0. ' +\n            'Failing to do so will lead to unnecessary layout and poor performance.',\n        );\n      }\n\n      if (this._hasFixedWidth === false && this._defaultWidth === 0) {\n        console.warn(\n          'Fixed width CellMeasurerCache should specify a :defaultWidth greater than 0. ' +\n            'Failing to do so will lead to unnecessary layout and poor performance.',\n        );\n      }\n    }\n  }\n\n  clear(rowIndex: number, columnIndex: number = 0) {\n    const key = this._keyMapper(rowIndex, columnIndex);\n\n    delete this._cellHeightCache[key];\n    delete this._cellWidthCache[key];\n\n    this._updateCachedColumnAndRowSizes(rowIndex, columnIndex);\n  }\n\n  clearAll() {\n    this._cellHeightCache = {};\n    this._cellWidthCache = {};\n    this._columnWidthCache = {};\n    this._rowHeightCache = {};\n    this._rowCount = 0;\n    this._columnCount = 0;\n  }\n\n  columnWidth = ({index}: IndexParam) => {\n    const key = this._keyMapper(0, index);\n\n    return this._columnWidthCache[key] !== undefined\n      ? this._columnWidthCache[key]\n      : this._defaultWidth;\n  };\n\n  get defaultHeight(): number {\n    return this._defaultHeight;\n  }\n\n  get defaultWidth(): number {\n    return this._defaultWidth;\n  }\n\n  hasFixedHeight(): boolean {\n    return this._hasFixedHeight;\n  }\n\n  hasFixedWidth(): boolean {\n    return this._hasFixedWidth;\n  }\n\n  getHeight(rowIndex: number, columnIndex: number = 0): number {\n    if (this._hasFixedHeight) {\n      return this._defaultHeight;\n    } else {\n      const key = this._keyMapper(rowIndex, columnIndex);\n\n      return this._cellHeightCache[key] !== undefined\n        ? Math.max(this._minHeight, this._cellHeightCache[key])\n        : this._defaultHeight;\n    }\n  }\n\n  getWidth(rowIndex: number, columnIndex: number = 0): number {\n    if (this._hasFixedWidth) {\n      return this._defaultWidth;\n    } else {\n      const key = this._keyMapper(rowIndex, columnIndex);\n\n      return this._cellWidthCache[key] !== undefined\n        ? Math.max(this._minWidth, this._cellWidthCache[key])\n        : this._defaultWidth;\n    }\n  }\n\n  has(rowIndex: number, columnIndex: number = 0): boolean {\n    const key = this._keyMapper(rowIndex, columnIndex);\n\n    return this._cellHeightCache[key] !== undefined;\n  }\n\n  rowHeight = ({index}: IndexParam) => {\n    const key = this._keyMapper(index, 0);\n\n    return this._rowHeightCache[key] !== undefined\n      ? this._rowHeightCache[key]\n      : this._defaultHeight;\n  };\n\n  set(\n    rowIndex: number,\n    columnIndex: number,\n    width: number,\n    height: number,\n  ): void {\n    const key = this._keyMapper(rowIndex, columnIndex);\n\n    if (columnIndex >= this._columnCount) {\n      this._columnCount = columnIndex + 1;\n    }\n    if (rowIndex >= this._rowCount) {\n      this._rowCount = rowIndex + 1;\n    }\n\n    // Size is cached per cell so we don't have to re-measure if cells are re-ordered.\n    this._cellHeightCache[key] = height;\n    this._cellWidthCache[key] = width;\n\n    this._updateCachedColumnAndRowSizes(rowIndex, columnIndex);\n  }\n\n  _updateCachedColumnAndRowSizes(rowIndex: number, columnIndex: number) {\n    // :columnWidth and :rowHeight are derived based on all cells in a column/row.\n    // Pre-cache these derived values for faster lookup later.\n    // Reads are expected to occur more frequently than writes in this case.\n    // Only update non-fixed dimensions though to avoid doing unnecessary work.\n    if (!this._hasFixedWidth) {\n      let columnWidth = 0;\n      for (let i = 0; i < this._rowCount; i++) {\n        columnWidth = Math.max(columnWidth, this.getWidth(i, columnIndex));\n      }\n      const columnKey = this._keyMapper(0, columnIndex);\n      this._columnWidthCache[columnKey] = columnWidth;\n    }\n    if (!this._hasFixedHeight) {\n      let rowHeight = 0;\n      for (let i = 0; i < this._columnCount; i++) {\n        rowHeight = Math.max(rowHeight, this.getHeight(rowIndex, i));\n      }\n      const rowKey = this._keyMapper(rowIndex, 0);\n      this._rowHeightCache[rowKey] = rowHeight;\n    }\n  }\n}\n\nfunction defaultKeyMapper(rowIndex: number, columnIndex: number) {\n  return `${rowIndex}-${columnIndex}`;\n}\n"
  },
  {
    "path": "source/CellMeasurer/index.js",
    "content": "/** @flow */\nimport CellMeasurer from './CellMeasurer';\nimport CellMeasurerCache from './CellMeasurerCache';\n\nexport default CellMeasurer;\nexport {CellMeasurer, CellMeasurerCache};\n"
  },
  {
    "path": "source/CellMeasurer/types.js",
    "content": "// @flow\n\nexport interface CellMeasureCache {\n  hasFixedWidth(): boolean;\n  hasFixedHeight(): boolean;\n  has(rowIndex: number, columnIndex: number): boolean;\n  set(\n    rowIndex: number,\n    columnIndex: number,\n    width: number,\n    height: number,\n  ): void;\n  getHeight(rowIndex: number, columnIndex?: number): number;\n  getWidth(rowIndex: number, columnIndex?: number): number;\n}\n"
  },
  {
    "path": "source/Collection/Collection.example.css",
    "content": ".collection {\n  background-color: #fff;\n}\n\n.cell {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  border-radius: .25rem;\n  color: #fff;\n}\n\n.noCells {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 1em;\n  color: #bdbdbd;\n}\n"
  },
  {
    "path": "source/Collection/Collection.example.js",
    "content": "/** @flow */\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport Immutable from 'immutable';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\nimport AutoSizer from '../AutoSizer';\nimport Collection from './Collection';\nimport styles from './Collection.example.css';\n\n// Defines a pattern of sizes and positions for a range of 10 rotating cells\n// These cells cover an area of 600 (wide) x 400 (tall)\nconst GUTTER_SIZE = 3;\nconst CELL_WIDTH = 75;\n\nexport default class CollectionExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      cellCount: context.list.size,\n      columnCount: this._getColumnCount(context.list.size),\n      height: 300,\n      horizontalOverscanSize: 0,\n      scrollToCell: undefined,\n      showScrollingPlaceholder: false,\n      verticalOverscanSize: 0,\n    };\n\n    this._columnYMap = [];\n\n    this._cellRenderer = this._cellRenderer.bind(this);\n    this._cellSizeAndPositionGetter = this._cellSizeAndPositionGetter.bind(\n      this,\n    );\n    this._noContentRenderer = this._noContentRenderer.bind(this);\n    this._onCellCountChange = this._onCellCountChange.bind(this);\n    this._onHeightChange = this._onHeightChange.bind(this);\n    this._onHorizontalOverscanSizeChange = this._onHorizontalOverscanSizeChange.bind(\n      this,\n    );\n    this._onScrollToCellChange = this._onScrollToCellChange.bind(this);\n    this._onVerticalOverscanSizeChange = this._onVerticalOverscanSizeChange.bind(\n      this,\n    );\n  }\n\n  render() {\n    const {\n      cellCount,\n      height,\n      horizontalOverscanSize,\n      scrollToCell,\n      showScrollingPlaceholder,\n      verticalOverscanSize,\n    } = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"Collection\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/Collection/Collection.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/Collection.md\"\n        />\n\n        <ContentBoxParagraph>\n          Renders scattered or non-linear data. Unlike <code>Grid</code>, which\n          renders checkerboard data, <code>Collection</code> can render\n          arbitrarily positioned- even overlapping- data.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Show placeholder while scrolling?\"\n              checked={showScrollingPlaceholder}\n              className={styles.checkbox}\n              type=\"checkbox\"\n              onChange={event =>\n                this.setState({\n                  showScrollingPlaceholder: event.target.checked,\n                })\n              }\n            />\n            Show placeholder while scrolling?\n          </label>\n        </ContentBoxParagraph>\n\n        <InputRow>\n          <LabeledInput\n            label=\"Num cells\"\n            name=\"cellCount\"\n            onChange={this._onCellCountChange}\n            value={cellCount}\n          />\n          <LabeledInput\n            label=\"Scroll to cell\"\n            name=\"onScrollToCell\"\n            placeholder=\"Index...\"\n            onChange={this._onScrollToCellChange}\n            value={scrollToCell || ''}\n          />\n          <LabeledInput\n            label=\"Height\"\n            name=\"height\"\n            onChange={this._onHeightChange}\n            value={height}\n          />\n          <LabeledInput\n            label=\"Horizontal Overscan\"\n            name=\"horizontalOverscanSize\"\n            onChange={this._onHorizontalOverscanSizeChange}\n            value={horizontalOverscanSize}\n          />\n          <LabeledInput\n            label=\"Vertical Overscan\"\n            name=\"verticalOverscanSize\"\n            onChange={this._onVerticalOverscanSizeChange}\n            value={verticalOverscanSize}\n          />\n        </InputRow>\n\n        <AutoSizer disableHeight>\n          {({width}) => (\n            <Collection\n              cellCount={cellCount}\n              cellRenderer={this._cellRenderer}\n              cellSizeAndPositionGetter={this._cellSizeAndPositionGetter}\n              className={styles.collection}\n              height={height}\n              horizontalOverscanSize={horizontalOverscanSize}\n              noContentRenderer={this._noContentRenderer}\n              scrollToCell={scrollToCell}\n              verticalOverscanSize={verticalOverscanSize}\n              width={width}\n            />\n          )}\n        </AutoSizer>\n      </ContentBox>\n    );\n  }\n\n  _cellRenderer({index, isScrolling, key, style}) {\n    const {list} = this.context;\n    const {showScrollingPlaceholder} = this.state;\n\n    const datum = list.get(index % list.size);\n\n    // Customize style\n    style.backgroundColor = datum.color;\n\n    return (\n      <div className={styles.cell} key={key} style={style}>\n        {showScrollingPlaceholder && isScrolling ? '...' : index}\n      </div>\n    );\n  }\n\n  _cellSizeAndPositionGetter({index}) {\n    const {list} = this.context;\n    const {columnCount} = this.state;\n\n    const columnPosition = index % (columnCount || 1);\n    const datum = list.get(index % list.size);\n\n    // Poor man's Masonry layout; columns won't all line up equally with the bottom.\n    const height = datum.size;\n    const width = CELL_WIDTH;\n    const x = columnPosition * (GUTTER_SIZE + width);\n    const y = this._columnYMap[columnPosition] || 0;\n\n    this._columnYMap[columnPosition] = y + height + GUTTER_SIZE;\n\n    return {\n      height,\n      width,\n      x,\n      y,\n    };\n  }\n\n  _getColumnCount(cellCount) {\n    return Math.round(Math.sqrt(cellCount));\n  }\n\n  _onHorizontalOverscanSizeChange(event) {\n    const horizontalOverscanSize = parseInt(event.target.value, 10) || 0;\n\n    this.setState({horizontalOverscanSize});\n  }\n\n  _noContentRenderer() {\n    return <div className={styles.noCells}>No cells</div>;\n  }\n\n  _onCellCountChange(event) {\n    const cellCount = parseInt(event.target.value, 10) || 0;\n\n    this._columnYMap = [];\n\n    this.setState({\n      cellCount,\n      columnCount: this._getColumnCount(cellCount),\n    });\n  }\n\n  _onHeightChange(event) {\n    const height = parseInt(event.target.value, 10) || 0;\n\n    this.setState({height});\n  }\n\n  _onScrollToCellChange(event) {\n    const {cellCount} = this.state;\n\n    let scrollToCell = Math.min(\n      cellCount - 1,\n      parseInt(event.target.value, 10),\n    );\n\n    if (isNaN(scrollToCell)) {\n      scrollToCell = undefined;\n    }\n\n    this.setState({scrollToCell});\n  }\n\n  _onVerticalOverscanSizeChange(event) {\n    const verticalOverscanSize = parseInt(event.target.value, 10) || 0;\n\n    this.setState({verticalOverscanSize});\n  }\n}\n"
  },
  {
    "path": "source/Collection/Collection.jest.js",
    "content": "/**\n * Tests Collection and CollectionView.\n * @flow\n */\nimport getScrollbarSize from 'dom-helpers/scrollbarSize';\nimport * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {Simulate} from 'react-dom/test-utils';\nimport {render} from '../TestUtils';\nimport Collection from './Collection';\nimport {CELLS, SECTION_SIZE} from './TestData';\n\ndescribe('Collection', () => {\n  function defaultCellRenderer({index, key, style}) {\n    return (\n      <div className=\"cell\" key={key} style={style}>\n        cell:{index}\n      </div>\n    );\n  }\n\n  function getMarkup(props = {}) {\n    const {cellCount = CELLS.length} = props;\n\n    function defaultCellSizeAndPositionGetter({index}) {\n      index %= cellCount;\n\n      return CELLS[index];\n    }\n\n    return (\n      <Collection\n        cellCount={cellCount}\n        cellRenderer={defaultCellRenderer}\n        cellSizeAndPositionGetter={defaultCellSizeAndPositionGetter}\n        height={SECTION_SIZE}\n        sectionSize={SECTION_SIZE}\n        width={SECTION_SIZE * 2}\n        {...props}\n      />\n    );\n  }\n\n  function simulateScroll({collection, scrollLeft = 0, scrollTop = 0}) {\n    const target = {scrollLeft, scrollTop};\n    collection._collectionView._scrollingContainer = target; // HACK to work around _onScroll target check\n    Simulate.scroll(findDOMNode(collection), {target});\n  }\n\n  function compareArrays(array1, array2) {\n    expect(array1.length).toEqual(array2.length);\n\n    array2.forEach(value => {\n      expect(array1).toContain(value);\n    });\n  }\n\n  describe('number of rendered children', () => {\n    it('should render enough children to fill the available area', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.querySelectorAll('.cell').length).toEqual(4);\n    });\n\n    it('should not render more cells than available if the area is not filled', () => {\n      const rendered = findDOMNode(render(getMarkup({cellCount: 2})));\n      expect(rendered.querySelectorAll('.cell').length).toEqual(2);\n    });\n\n    // Small performance tweak added in 5.5.6\n    it('should not render/parent cells that are null or false', () => {\n      function cellRenderer({index, key, style}) {\n        if (index > 2) {\n          return null;\n        } else {\n          return (\n            <div className=\"cell\" key={key} style={style}>\n              {index}\n            </div>\n          );\n        }\n      }\n      const rendered = findDOMNode(render(getMarkup({cellRenderer})));\n      expect(rendered.querySelectorAll('.cell').length).toEqual(3);\n    });\n  });\n\n  describe('shows and hides scrollbars based on rendered content', () => {\n    let scrollbarSize;\n\n    beforeAll(() => {\n      scrollbarSize = getScrollbarSize();\n    });\n\n    it('should set overflowX:hidden if columns fit within the available width and y-axis has no scrollbar', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 4,\n            width: 6,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).toEqual('hidden');\n    });\n\n    it('should set overflowX:hidden if columns and y-axis scrollbar fit within the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 1,\n            width: 6 + scrollbarSize,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).toEqual('hidden');\n    });\n\n    it('should leave overflowX:auto if columns require more than the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            width: 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).not.toEqual('hidden');\n    });\n\n    it('should leave overflowX:auto if columns and y-axis scrollbar require more than the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 1,\n            width: 6 + scrollbarSize - 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).not.toEqual('hidden');\n    });\n\n    it('should set overflowY:hidden if rows fit within the available width and xaxis has no scrollbar', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 4,\n            width: 6,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).toEqual('hidden');\n    });\n\n    it('should set overflowY:hidden if rows and x-axis scrollbar fit within the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 4 + scrollbarSize,\n            width: 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).toEqual('hidden');\n    });\n\n    it('should leave overflowY:auto if rows require more than the available height', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).not.toEqual('hidden');\n    });\n\n    it('should leave overflowY:auto if rows and y-axis scrollbar require more than the available height', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 4 + scrollbarSize - 1,\n            width: 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).not.toEqual('hidden');\n    });\n\n    it('should accept styles that overwrite calculated ones', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            height: 1,\n            style: {\n              overflowX: 'auto',\n              overflowY: 'auto',\n            },\n            width: 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).toEqual('auto');\n      expect(rendered.style.overflowY).toEqual('auto');\n    });\n  });\n\n  describe('autoHeight', () => {\n    it('should set the container height to auto to adjust to innerScrollContainer height', () => {\n      const props = {\n        autoHeight: true,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(rendered.style.height).toEqual('auto');\n    });\n\n    it('should have container height still affecting number of rows rendered', () => {\n      let indices;\n      const props = {\n        autoHeight: true,\n        height: 500,\n        onSectionRendered: params => {\n          indices = params.indices;\n        },\n      };\n      findDOMNode(render(getMarkup(props)));\n      compareArrays(indices, [0, 1, 2, 3, 4, 5]);\n    });\n\n    it('should have innerScrollContainer height to be equal number of rows * rowHeight', () => {\n      const props = {\n        autoHeight: true,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(\n        rendered.querySelector(\n          '.ReactVirtualized__Collection__innerScrollContainer',\n        ).style.height,\n      ).toEqual('4px');\n    });\n  });\n\n  describe(':scrollToCell', () => {\n    it('should scroll to the top/left', () => {\n      const collection = render(getMarkup({scrollToCell: 0}));\n      expect(collection._collectionView.state.scrollLeft).toEqual(0);\n      expect(collection._collectionView.state.scrollTop).toEqual(0);\n    });\n\n    it('should scroll over to the middle', () => {\n      const collection = render(getMarkup({scrollToCell: 7}));\n      expect(collection._collectionView.state.scrollLeft).toEqual(1);\n      expect(collection._collectionView.state.scrollTop).toEqual(1);\n    });\n\n    it('should scroll to the bottom/right', () => {\n      const collection = render(getMarkup({scrollToCell: 9}));\n      expect(collection._collectionView.state.scrollLeft).toEqual(2);\n      expect(collection._collectionView.state.scrollTop).toEqual(2);\n    });\n\n    it('should honor the specified :scrollToAlignment', () => {\n      let collection = render(\n        getMarkup({\n          scrollToAlignment: 'start',\n          scrollToCell: 2,\n          width: SECTION_SIZE,\n        }),\n      );\n      // Minimum amount of scrolling (\"auto\") would be 0,0\n      expect(collection._collectionView.state.scrollLeft).toEqual(2);\n      expect(collection._collectionView.state.scrollTop).toEqual(1);\n\n      collection = render(\n        getMarkup({\n          scrollToAlignment: 'end',\n          scrollToCell: 2,\n          width: SECTION_SIZE,\n        }),\n      );\n      // This cell would already by visible by \"auto\" rules\n      expect(collection._collectionView.state.scrollLeft).toEqual(1);\n      expect(collection._collectionView.state.scrollTop).toEqual(0);\n\n      collection = render(\n        getMarkup({\n          scrollToAlignment: 'center',\n          scrollToCell: 4,\n          width: SECTION_SIZE,\n        }),\n      );\n      // This cell doesn't fit entirely in the viewport but we center it anyway.\n      expect(collection._collectionView.state.scrollLeft).toEqual(0.5);\n      expect(collection._collectionView.state.scrollTop).toEqual(2);\n    });\n\n    it('should scroll to a cell just added', () => {\n      let collection = render(\n        getMarkup({\n          cellCount: 4,\n        }),\n      );\n      expect(collection._collectionView.state.scrollLeft).toEqual(0);\n      expect(collection._collectionView.state.scrollTop).toEqual(0);\n      collection = render(\n        getMarkup({\n          cellCount: 8,\n          scrollToCell: 7,\n        }),\n      );\n      expect(collection._collectionView.state.scrollLeft).toEqual(1);\n      expect(collection._collectionView.state.scrollTop).toEqual(1);\n    });\n  });\n\n  describe('property updates', () => {\n    it('should update :scrollToCell position when :width changes', () => {\n      let collection = findDOMNode(render(getMarkup({scrollToCell: 3})));\n      expect(collection.textContent).toContain('cell:3');\n      // Making the collection narrower leaves only room for 1 item\n      collection = findDOMNode(render(getMarkup({scrollToCell: 3, width: 1})));\n      expect(collection.textContent).toContain('cell:3');\n    });\n\n    it('should update :scrollToCell position when :height changes', () => {\n      let collection = findDOMNode(render(getMarkup({scrollToCell: 4})));\n      expect(collection.textContent).toContain('cell:4');\n      // Making the collection shorter leaves only room for 1 item\n      collection = findDOMNode(render(getMarkup({scrollToCell: 4, height: 1})));\n      expect(collection.textContent).toContain('cell:4');\n    });\n\n    it('should update scroll position when :scrollToCell changes', () => {\n      let collection = findDOMNode(render(getMarkup()));\n      expect(collection.textContent).not.toContain('cell:9');\n      collection = findDOMNode(render(getMarkup({scrollToCell: 9})));\n      expect(collection.textContent).toContain('cell:9');\n    });\n  });\n\n  describe('noContentRenderer', () => {\n    it('should call :noContentRenderer if :cellCount is 0', () => {\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            noContentRenderer: () => <div>No data</div>,\n            cellCount: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('No data');\n    });\n\n    it('should render an empty body if :cellCount is 0 and there is no :noContentRenderer', () => {\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            cellCount: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('');\n    });\n\n    it('should not show the :noContentRenderer when there are children, even if no children are currently visible (sparse)', () => {\n      const offscreenSizeAndPosition = {\n        x: SECTION_SIZE * 3,\n        y: SECTION_SIZE * 3,\n        width: 1,\n        height: 1,\n      };\n\n      function cellSizeAndPositionGetter() {\n        return offscreenSizeAndPosition;\n      }\n\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            cellCount: 1,\n            cellSizeAndPositionGetter,\n            noContentRenderer: () => <div>No data</div>,\n          }),\n        ),\n      );\n      expect(list.textContent).not.toEqual('No data');\n    });\n  });\n\n  describe('onSectionRendered', () => {\n    it('should call :onSectionRendered if at least one cell is rendered', () => {\n      let indices;\n      render(\n        getMarkup({\n          onSectionRendered: params => {\n            indices = params.indices;\n          },\n        }),\n      );\n      compareArrays(indices, [0, 1, 2, 3]);\n    });\n\n    it('should not call :onSectionRendered unless the rendered indices have changed', () => {\n      let numCalls = 0;\n      let indices;\n      const onSectionRendered = params => {\n        indices = params.indices;\n        numCalls++;\n      };\n      render(getMarkup({onSectionRendered}));\n      expect(numCalls).toEqual(1);\n      compareArrays(indices, [0, 1, 2, 3]);\n      render(getMarkup({onSectionRendered}));\n      expect(numCalls).toEqual(1);\n      compareArrays(indices, [0, 1, 2, 3]);\n    });\n\n    it('should call :onSectionRendered if the rendered indices have changed', () => {\n      let numCalls = 0;\n      let indices;\n      const onSectionRendered = params => {\n        indices = params.indices;\n        numCalls++;\n      };\n      render(getMarkup({onSectionRendered}));\n      expect(numCalls).toEqual(1);\n      compareArrays(indices, [0, 1, 2, 3]);\n      render(\n        getMarkup({\n          height: SECTION_SIZE * 2,\n          onSectionRendered,\n        }),\n      );\n      expect(numCalls).toEqual(2);\n      compareArrays(indices, [0, 1, 2, 3, 4, 5]);\n      render(\n        getMarkup({\n          height: SECTION_SIZE * 2,\n          onSectionRendered,\n          width: SECTION_SIZE,\n        }),\n      );\n      expect(numCalls).toEqual(3);\n      expect(indices).toEqual([0, 4]);\n    });\n\n    it('should not call :onSectionRendered if no cells are rendered', () => {\n      let numCalls = 0;\n      render(\n        getMarkup({\n          height: 0,\n          onSectionRendered: () => numCalls++,\n        }),\n      );\n      expect(numCalls).toEqual(0);\n    });\n  });\n\n  describe(':scrollLeft and :scrollTop properties', () => {\n    it('should render correctly when an initial :scrollLeft and :scrollTop properties are specified', () => {\n      let indices;\n      const collection = render(\n        getMarkup({\n          onSectionRendered: params => {\n            indices = params.indices;\n          },\n          scrollLeft: 2,\n          scrollTop: 2,\n        }),\n      );\n      compareArrays(indices, [3, 4, 5, 7, 8, 9]);\n      expect(\n        collection._collectionView.state.scrollPositionChangeReason,\n      ).toEqual('requested');\n    });\n\n    it('should render correctly when :scrollLeft and :scrollTop properties are updated', () => {\n      let indices;\n      render(\n        getMarkup({\n          onSectionRendered: params => {\n            indices = params.indices;\n          },\n        }),\n      );\n      compareArrays(indices, [0, 1, 2, 3]);\n      const collection = render(\n        getMarkup({\n          onSectionRendered: params => {\n            indices = params.indices;\n          },\n          scrollLeft: 2,\n          scrollTop: 2,\n        }),\n      );\n      compareArrays(indices, [3, 4, 5, 7, 8, 9]);\n      expect(\n        collection._collectionView.state.scrollPositionChangeReason,\n      ).toEqual('requested');\n    });\n  });\n\n  describe('styles, classNames, and ids', () => {\n    it('should use the expected global CSS classNames', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.className).toEqual('ReactVirtualized__Collection');\n    });\n\n    it('should use a custom :className if specified', () => {\n      const rendered = findDOMNode(render(getMarkup({className: 'foo'})));\n      expect(rendered.className).toContain('foo');\n    });\n\n    it('should use a custom :id if specified', () => {\n      const rendered = findDOMNode(render(getMarkup({id: 'bar'})));\n      expect(rendered.getAttribute('id')).toEqual('bar');\n    });\n\n    it('should use a custom :style if specified', () => {\n      const style = {backgroundColor: 'red'};\n      const rendered = findDOMNode(render(getMarkup({style})));\n      expect(rendered.style.backgroundColor).toEqual('red');\n    });\n  });\n\n  describe('onScroll', () => {\n    it('should trigger callback when component is mounted', () => {\n      const onScrollCalls = [];\n      render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n          scrollLeft: 2,\n          scrollTop: 1,\n        }),\n      );\n      expect(onScrollCalls).toEqual([\n        {\n          clientHeight: SECTION_SIZE,\n          clientWidth: SECTION_SIZE * 2,\n          scrollHeight: 4,\n          scrollLeft: 2,\n          scrollTop: 1,\n          scrollWidth: 6,\n        },\n      ]);\n    });\n\n    it('should trigger callback when component scrolls horizontally', () => {\n      const onScrollCalls = [];\n      const collection = render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      simulateScroll({\n        collection,\n        scrollLeft: 1,\n        scrollTop: 0,\n      });\n      expect(onScrollCalls.length).toEqual(2);\n      expect(onScrollCalls[1]).toEqual({\n        clientHeight: SECTION_SIZE,\n        clientWidth: SECTION_SIZE * 2,\n        scrollHeight: 4,\n        scrollLeft: 1,\n        scrollTop: 0,\n        scrollWidth: 6,\n      });\n    });\n\n    it('should trigger callback when component scrolls vertically', () => {\n      const onScrollCalls = [];\n      const collection = render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      simulateScroll({\n        collection,\n        scrollLeft: 0,\n        scrollTop: 2,\n      });\n      expect(onScrollCalls.length).toEqual(2);\n      expect(onScrollCalls[1]).toEqual({\n        clientHeight: SECTION_SIZE,\n        clientWidth: SECTION_SIZE * 2,\n        scrollHeight: 4,\n        scrollLeft: 0,\n        scrollTop: 2,\n        scrollWidth: 6,\n      });\n    });\n\n    it('should not allow negative scroll values', () => {\n      const onScrollCalls = [];\n      const collection = render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      simulateScroll({\n        collection,\n        scrollLeft: -1,\n        scrollTop: -1,\n      });\n      expect(onScrollCalls.length).toEqual(1);\n      expect(onScrollCalls[0].scrollLeft).toEqual(0);\n      expect(onScrollCalls[0].scrollTop).toEqual(0);\n    });\n  });\n\n  describe('cellGroupRenderer', () => {\n    it('should use a custom :cellGroupRenderer if specified', () => {\n      let cellGroupRendererCalled = 0;\n      let cellGroupRendererParams;\n      const cellRenderer = ({index, key, style}) => (\n        <div key={key} style={style}>\n          {index}\n        </div>\n      );\n      findDOMNode(\n        render(\n          getMarkup({\n            cellRenderer,\n            cellGroupRenderer: params => {\n              cellGroupRendererParams = params;\n              cellGroupRendererCalled++;\n\n              return [<div key=\"0\">Fake content</div>];\n            },\n          }),\n        ),\n      );\n      expect(cellGroupRendererCalled).toEqual(1);\n      expect(cellGroupRendererParams.cellRenderer).toEqual(cellRenderer);\n      expect(typeof cellGroupRendererParams.cellSizeAndPositionGetter).toEqual(\n        'function',\n      );\n      compareArrays(cellGroupRendererParams.indices, [0, 1, 2, 3]);\n    });\n  });\n\n  it('should pass the cellRenderer an :isScrolling flag when scrolling is in progress', async done => {\n    const cellRendererCalls = [];\n    function cellRenderer({index, isScrolling, key, style}) {\n      cellRendererCalls.push(isScrolling);\n      return defaultCellRenderer({index, key, style});\n    }\n\n    const collection = render(\n      getMarkup({\n        cellRenderer,\n      }),\n    );\n\n    expect(cellRendererCalls[0]).toEqual(false);\n\n    cellRendererCalls.splice(0);\n\n    simulateScroll({collection, scrollTop: 1});\n\n    // Give React time to process the queued setState()\n    await new Promise(resolve => setTimeout(resolve, 1));\n\n    expect(cellRendererCalls[0]).toEqual(true);\n\n    done();\n  });\n\n  describe('horizontalOverscanSize and verticalOverscanSize', () => {\n    it('should include the horizontal and vertical overscan size when rendering cells', () => {\n      let indices;\n      render(\n        getMarkup({\n          onSectionRendered: params => {\n            indices = params.indices;\n          },\n          height: 1,\n          horizontalOverscanSize: 2,\n          sectionSize: 1,\n          scrollLeft: 2,\n          scrollTop: 2,\n          width: 1,\n          verticalOverscanSize: 1,\n        }),\n      );\n      compareArrays(indices, [0, 2, 3, 4, 5, 6, 7, 9]);\n    });\n\n    it('should not exceed the top/left borders regardless of overscan size', () => {\n      let indices;\n      render(\n        getMarkup({\n          onSectionRendered: params => {\n            indices = params.indices;\n          },\n          height: 2,\n          horizontalOverscanSize: 1,\n          sectionSize: 1,\n          scrollLeft: 0,\n          scrollTop: 0,\n          width: 1,\n          verticalOverscanSize: 2,\n        }),\n      );\n      compareArrays(indices, [0, 4]);\n    });\n\n    it('should not exceed the bottom/right borders regardless of overscan size', () => {\n      let indices;\n      render(\n        getMarkup({\n          onSectionRendered: params => {\n            indices = params.indices;\n          },\n          height: 2,\n          horizontalOverscanSize: 1,\n          sectionSize: 1,\n          scrollLeft: 5,\n          scrollTop: 2,\n          width: 1,\n          verticalOverscanSize: 2,\n        }),\n      );\n      compareArrays(indices, [6, 7, 8, 9]);\n    });\n  });\n\n  describe('cell caching', () => {\n    it('should not cache cells if the Grid is not scrolling', () => {\n      const cellRendererCalls = [];\n      function cellRenderer({isScrolling, index, key, style}) {\n        cellRendererCalls.push({isScrolling, index});\n        return defaultCellRenderer({index, key, style});\n      }\n\n      const props = {\n        cellRenderer,\n        scrollLeft: 0,\n        scrollTop: 0,\n      };\n\n      findDOMNode(render(getMarkup(props)));\n      expect(cellRendererCalls.length).toEqual(4);\n      cellRendererCalls.forEach(call =>\n        expect(call.isScrolling).toEqual(false),\n      );\n\n      cellRendererCalls.splice(0);\n\n      render(\n        getMarkup({\n          ...props,\n          foo: 'bar', // Force re-render\n        }),\n      );\n      expect(cellRendererCalls.length).toEqual(4);\n      cellRendererCalls.forEach(call =>\n        expect(call.isScrolling).toEqual(false),\n      );\n    });\n\n    it.skip('should cache a cell once it has been rendered while scrolling', () => {\n      const cellRendererCalls = [];\n      function cellRenderer({isScrolling, index, key, style}) {\n        cellRendererCalls.push({isScrolling, index});\n        return defaultCellRenderer({index, key, style});\n      }\n\n      const props = {\n        cellRenderer,\n        scrollLeft: 0,\n        scrollTop: 0,\n      };\n\n      const collection = render(getMarkup(props));\n      expect(cellRendererCalls.length).toEqual(4);\n      cellRendererCalls.forEach(call =>\n        expect(call.isScrolling).toEqual(false),\n      );\n\n      // FIXME: simulate scroll is not triggering cells to render in cache\n      // Scroll a little bit; newly-rendered cells will be cached.\n      simulateScroll({collection, scrollTop: 2});\n\n      cellRendererCalls.splice(0);\n\n      // At this point cells 4 and 5 have been rendered,\n      // But cells 7, 8, and 9 have not.\n      render(\n        getMarkup({\n          ...props,\n          scrollLeft: 1,\n          scrollTop: 3,\n        }),\n      );\n      expect(cellRendererCalls.length).toEqual(3);\n      cellRendererCalls.forEach(call => expect(call.isScrolling).toEqual(true));\n    });\n\n    it('should clear cache once :isScrolling is false', async done => {\n      const cellRendererCalls = [];\n      function cellRenderer({isScrolling, index, key, style}) {\n        cellRendererCalls.push({isScrolling, index});\n        return defaultCellRenderer({isScrolling, index, key, style});\n      }\n\n      const props = {\n        cellRenderer,\n        scrollLeft: 0,\n        scrollTop: 0,\n      };\n\n      const collection = render(getMarkup(props));\n      simulateScroll({collection, scrollTop: 1});\n\n      // Allow scrolling timeout to complete so that cell cache is reset\n      await new Promise(resolve => setTimeout(resolve, 500));\n\n      cellRendererCalls.splice(0);\n\n      render(\n        getMarkup({\n          ...props,\n          scrollTop: 1,\n        }),\n      );\n      expect(cellRendererCalls.length).not.toEqual(0);\n\n      done();\n    });\n  });\n\n  // See issue #568 for more\n  it('forceUpdate will also forceUpdate the inner CollectionView', () => {\n    const cellRenderer = jest.fn();\n    cellRenderer.mockImplementation(({key}) => <div key={key} />);\n\n    const rendered = render(getMarkup({cellRenderer}));\n\n    expect(cellRenderer).toHaveBeenCalled();\n\n    cellRenderer.mockReset();\n    rendered.forceUpdate();\n\n    expect(cellRenderer).toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "source/Collection/Collection.js",
    "content": "/** @flow */\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport CollectionView from './CollectionView';\nimport calculateSizeAndPositionData from './utils/calculateSizeAndPositionData';\nimport getUpdatedOffsetForIndex from '../utils/getUpdatedOffsetForIndex';\nimport type {ScrollPosition, SizeInfo} from './types';\n\n/**\n * Renders scattered or non-linear data.\n * Unlike Grid, which renders checkerboard data, Collection can render arbitrarily positioned- even overlapping- data.\n */\nexport default class Collection extends React.PureComponent {\n  static propTypes = {\n    'aria-label': PropTypes.string,\n\n    /**\n     * Number of cells in Collection.\n     */\n    cellCount: PropTypes.number.isRequired,\n\n    /**\n     * Responsible for rendering a group of cells given their indices.\n     * Should implement the following interface: ({\n     *   cellSizeAndPositionGetter:Function,\n     *   indices: Array<number>,\n     *   cellRenderer: Function\n     * }): Array<PropTypes.node>\n     */\n    cellGroupRenderer: PropTypes.func.isRequired,\n\n    /**\n     * Responsible for rendering a cell given an row and column index.\n     * Should implement the following interface: ({ index: number, key: string, style: object }): PropTypes.element\n     */\n    cellRenderer: PropTypes.func.isRequired,\n\n    /**\n     * Callback responsible for returning size and offset/position information for a given cell (index).\n     * ({ index: number }): { height: number, width: number, x: number, y: number }\n     */\n    cellSizeAndPositionGetter: PropTypes.func.isRequired,\n\n    /**\n     * Optionally override the size of the sections a Collection's cells are split into.\n     */\n    sectionSize: PropTypes.number,\n  };\n\n  static defaultProps = {\n    'aria-label': 'grid',\n    cellGroupRenderer: defaultCellGroupRenderer,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._cellMetadata = [];\n    this._lastRenderedCellIndices = [];\n\n    // Cell cache during scroll (for performance)\n    this._cellCache = [];\n\n    this._isScrollingChange = this._isScrollingChange.bind(this);\n    this._setCollectionViewRef = this._setCollectionViewRef.bind(this);\n  }\n\n  forceUpdate() {\n    if (this._collectionView !== undefined) {\n      this._collectionView.forceUpdate();\n    }\n  }\n\n  /** See Collection#recomputeCellSizesAndPositions */\n  recomputeCellSizesAndPositions() {\n    this._cellCache = [];\n    this._collectionView.recomputeCellSizesAndPositions();\n  }\n\n  /** React lifecycle methods */\n\n  render() {\n    const {...props} = this.props;\n\n    return (\n      <CollectionView\n        cellLayoutManager={this}\n        isScrollingChange={this._isScrollingChange}\n        ref={this._setCollectionViewRef}\n        {...props}\n      />\n    );\n  }\n\n  /** CellLayoutManager interface */\n\n  calculateSizeAndPositionData() {\n    const {cellCount, cellSizeAndPositionGetter, sectionSize} = this.props;\n\n    const data = calculateSizeAndPositionData({\n      cellCount,\n      cellSizeAndPositionGetter,\n      sectionSize,\n    });\n\n    this._cellMetadata = data.cellMetadata;\n    this._sectionManager = data.sectionManager;\n    this._height = data.height;\n    this._width = data.width;\n  }\n\n  /**\n   * Returns the most recently rendered set of cell indices.\n   */\n  getLastRenderedIndices() {\n    return this._lastRenderedCellIndices;\n  }\n\n  /**\n   * Calculates the minimum amount of change from the current scroll position to ensure the specified cell is (fully) visible.\n   */\n  getScrollPositionForCell({\n    align,\n    cellIndex,\n    height,\n    scrollLeft,\n    scrollTop,\n    width,\n  }): ScrollPosition {\n    const {cellCount} = this.props;\n\n    if (cellIndex >= 0 && cellIndex < cellCount) {\n      const cellMetadata = this._cellMetadata[cellIndex];\n\n      scrollLeft = getUpdatedOffsetForIndex({\n        align,\n        cellOffset: cellMetadata.x,\n        cellSize: cellMetadata.width,\n        containerSize: width,\n        currentOffset: scrollLeft,\n        targetIndex: cellIndex,\n      });\n\n      scrollTop = getUpdatedOffsetForIndex({\n        align,\n        cellOffset: cellMetadata.y,\n        cellSize: cellMetadata.height,\n        containerSize: height,\n        currentOffset: scrollTop,\n        targetIndex: cellIndex,\n      });\n    }\n\n    return {\n      scrollLeft,\n      scrollTop,\n    };\n  }\n\n  getTotalSize(): SizeInfo {\n    return {\n      height: this._height,\n      width: this._width,\n    };\n  }\n\n  cellRenderers({height, isScrolling, width, x, y}) {\n    const {cellGroupRenderer, cellRenderer} = this.props;\n\n    // Store for later calls to getLastRenderedIndices()\n    this._lastRenderedCellIndices = this._sectionManager.getCellIndices({\n      height,\n      width,\n      x,\n      y,\n    });\n\n    return cellGroupRenderer({\n      cellCache: this._cellCache,\n      cellRenderer,\n      cellSizeAndPositionGetter: ({index}) =>\n        this._sectionManager.getCellMetadata({index}),\n      indices: this._lastRenderedCellIndices,\n      isScrolling,\n    });\n  }\n\n  _isScrollingChange(isScrolling) {\n    if (!isScrolling) {\n      this._cellCache = [];\n    }\n  }\n\n  _setCollectionViewRef(ref) {\n    this._collectionView = ref;\n  }\n}\n\nfunction defaultCellGroupRenderer({\n  cellCache,\n  cellRenderer,\n  cellSizeAndPositionGetter,\n  indices,\n  isScrolling,\n}) {\n  return indices\n    .map(index => {\n      const cellMetadata = cellSizeAndPositionGetter({index});\n\n      let cellRendererProps = {\n        index,\n        isScrolling,\n        key: index,\n        style: {\n          height: cellMetadata.height,\n          left: cellMetadata.x,\n          position: 'absolute',\n          top: cellMetadata.y,\n          width: cellMetadata.width,\n        },\n      };\n\n      // Avoid re-creating cells while scrolling.\n      // This can lead to the same cell being created many times and can cause performance issues for \"heavy\" cells.\n      // If a scroll is in progress- cache and reuse cells.\n      // This cache will be thrown away once scrolling complets.\n      if (isScrolling) {\n        if (!(index in cellCache)) {\n          cellCache[index] = cellRenderer(cellRendererProps);\n        }\n\n        return cellCache[index];\n      } else {\n        return cellRenderer(cellRendererProps);\n      }\n    })\n    .filter(renderedCell => !!renderedCell);\n}\n"
  },
  {
    "path": "source/Collection/CollectionView.js",
    "content": "/** @flow */\nimport clsx from 'clsx';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {polyfill} from 'react-lifecycles-compat';\nimport createCallbackMemoizer from '../utils/createCallbackMemoizer';\nimport getScrollbarSize from 'dom-helpers/scrollbarSize';\n\n// @TODO Merge Collection and CollectionView\n\n/**\n * Specifies the number of milliseconds during which to disable pointer events while a scroll is in progress.\n * This improves performance and makes scrolling smoother.\n */\nconst IS_SCROLLING_TIMEOUT = 150;\n\n/**\n * Controls whether the Grid updates the DOM element's scrollLeft/scrollTop based on the current state or just observes it.\n * This prevents Grid from interrupting mouse-wheel animations (see issue #2).\n */\nconst SCROLL_POSITION_CHANGE_REASONS = {\n  OBSERVED: 'observed',\n  REQUESTED: 'requested',\n};\n\n/**\n * Monitors changes in properties (eg. cellCount) and state (eg. scroll offsets) to determine when rendering needs to occur.\n * This component does not render any visible content itself; it defers to the specified :cellLayoutManager.\n */\nclass CollectionView extends React.PureComponent {\n  static propTypes = {\n    'aria-label': PropTypes.string,\n\n    /**\n     * Removes fixed height from the scrollingContainer so that the total height\n     * of rows can stretch the window. Intended for use with WindowScroller\n     */\n    autoHeight: PropTypes.bool,\n\n    /**\n     * Number of cells in collection.\n     */\n    cellCount: PropTypes.number.isRequired,\n\n    /**\n     * Calculates cell sizes and positions and manages rendering the appropriate cells given a specified window.\n     */\n    cellLayoutManager: PropTypes.object.isRequired,\n\n    /**\n     * Optional custom CSS class name to attach to root Collection element.\n     */\n    className: PropTypes.string,\n\n    /**\n     * Height of Collection; this property determines the number of visible (vs virtualized) rows.\n     */\n    height: PropTypes.number.isRequired,\n\n    /**\n     * Optional custom id to attach to root Collection element.\n     */\n    id: PropTypes.string,\n\n    /**\n     * Enables the `Collection` to horiontally \"overscan\" its content similar to how `Grid` does.\n     * This can reduce flicker around the edges when a user scrolls quickly.\n     */\n    horizontalOverscanSize: PropTypes.number.isRequired,\n\n    isScrollingChange: PropTypes.func,\n\n    /**\n     * Optional renderer to be used in place of rows when either :rowCount or :cellCount is 0.\n     */\n    noContentRenderer: PropTypes.func.isRequired,\n\n    /**\n     * Callback invoked whenever the scroll offset changes within the inner scrollable region.\n     * This callback can be used to sync scrolling between lists, tables, or grids.\n     * ({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void\n     */\n    onScroll: PropTypes.func.isRequired,\n\n    /**\n     * Callback invoked with information about the section of the Collection that was just rendered.\n     * This callback is passed a named :indices parameter which is an Array of the most recently rendered section indices.\n     */\n    onSectionRendered: PropTypes.func.isRequired,\n\n    /**\n     * Horizontal offset.\n     */\n    scrollLeft: PropTypes.number,\n\n    /**\n     * Controls scroll-to-cell behavior of the Grid.\n     * The default (\"auto\") scrolls the least amount possible to ensure that the specified cell is fully visible.\n     * Use \"start\" to align cells to the top/left of the Grid and \"end\" to align bottom/right.\n     */\n    scrollToAlignment: PropTypes.oneOf(['auto', 'end', 'start', 'center'])\n      .isRequired,\n\n    /**\n     * Cell index to ensure visible (by forcefully scrolling if necessary).\n     */\n    scrollToCell: PropTypes.number.isRequired,\n\n    /**\n     * Vertical offset.\n     */\n    scrollTop: PropTypes.number,\n\n    /**\n     * Optional custom inline style to attach to root Collection element.\n     */\n    style: PropTypes.object,\n\n    /**\n     * Enables the `Collection` to vertically \"overscan\" its content similar to how `Grid` does.\n     * This can reduce flicker around the edges when a user scrolls quickly.\n     */\n    verticalOverscanSize: PropTypes.number.isRequired,\n\n    /**\n     * Width of Collection; this property determines the number of visible (vs virtualized) columns.\n     */\n    width: PropTypes.number.isRequired,\n  };\n\n  static defaultProps = {\n    'aria-label': 'grid',\n    horizontalOverscanSize: 0,\n    noContentRenderer: () => null,\n    onScroll: () => null,\n    onSectionRendered: () => null,\n    scrollToAlignment: 'auto',\n    scrollToCell: -1,\n    style: {},\n    verticalOverscanSize: 0,\n  };\n\n  state = {\n    isScrolling: false,\n    scrollLeft: 0,\n    scrollTop: 0,\n  };\n\n  _calculateSizeAndPositionDataOnNextUpdate = false;\n\n  // Invokes callbacks only when their values have changed.\n  _onSectionRenderedMemoizer = createCallbackMemoizer();\n  _onScrollMemoizer = createCallbackMemoizer(false);\n\n  constructor(...args) {\n    super(...args);\n\n    // If this component is being rendered server-side, getScrollbarSize() will return undefined.\n    // We handle this case in componentDidMount()\n    this._scrollbarSize = getScrollbarSize();\n    if (this._scrollbarSize === undefined) {\n      this._scrollbarSizeMeasured = false;\n      this._scrollbarSize = 0;\n    } else {\n      this._scrollbarSizeMeasured = true;\n    }\n  }\n\n  /**\n   * Forced recompute of cell sizes and positions.\n   * This function should be called if cell sizes have changed but nothing else has.\n   * Since cell positions are calculated by callbacks, the collection view has no way of detecting when the underlying data has changed.\n   */\n  recomputeCellSizesAndPositions() {\n    this._calculateSizeAndPositionDataOnNextUpdate = true;\n    this.forceUpdate();\n  }\n\n  /* ---------------------------- Component lifecycle methods ---------------------------- */\n\n  /**\n   * @private\n   * This method updates scrollLeft/scrollTop in state for the following conditions:\n   * 1) Empty content (0 rows or columns)\n   * 2) New scroll props overriding the current state\n   * 3) Cells-count or cells-size has changed, making previous scroll offsets invalid\n   */\n  static getDerivedStateFromProps(nextProps, prevState) {\n    if (\n      nextProps.cellCount === 0 &&\n      (prevState.scrollLeft !== 0 || prevState.scrollTop !== 0)\n    ) {\n      return {\n        scrollLeft: 0,\n        scrollTop: 0,\n        scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED,\n      };\n    } else if (\n      nextProps.scrollLeft !== prevState.scrollLeft ||\n      nextProps.scrollTop !== prevState.scrollTop\n    ) {\n      return {\n        scrollLeft:\n          nextProps.scrollLeft != null\n            ? nextProps.scrollLeft\n            : prevState.scrollLeft,\n        scrollTop:\n          nextProps.scrollTop != null\n            ? nextProps.scrollTop\n            : prevState.scrollTop,\n        scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED,\n      };\n    }\n\n    return null;\n  }\n\n  componentDidMount() {\n    const {cellLayoutManager, scrollLeft, scrollToCell, scrollTop} = this.props;\n\n    // If this component was first rendered server-side, scrollbar size will be undefined.\n    // In that event we need to remeasure.\n    if (!this._scrollbarSizeMeasured) {\n      this._scrollbarSize = getScrollbarSize();\n      this._scrollbarSizeMeasured = true;\n      this.setState({});\n    }\n\n    if (scrollToCell >= 0) {\n      this._updateScrollPositionForScrollToCell();\n    } else if (scrollLeft >= 0 || scrollTop >= 0) {\n      this._setScrollPosition({scrollLeft, scrollTop});\n    }\n\n    // Update onSectionRendered callback.\n    this._invokeOnSectionRenderedHelper();\n\n    const {\n      height: totalHeight,\n      width: totalWidth,\n    } = cellLayoutManager.getTotalSize();\n\n    // Initialize onScroll callback.\n    this._invokeOnScrollMemoizer({\n      scrollLeft: scrollLeft || 0,\n      scrollTop: scrollTop || 0,\n      totalHeight,\n      totalWidth,\n    });\n  }\n\n  componentDidUpdate(prevProps, prevState) {\n    const {height, scrollToAlignment, scrollToCell, width} = this.props;\n    const {scrollLeft, scrollPositionChangeReason, scrollTop} = this.state;\n\n    // Make sure requested changes to :scrollLeft or :scrollTop get applied.\n    // Assigning to scrollLeft/scrollTop tells the browser to interrupt any running scroll animations,\n    // And to discard any pending async changes to the scroll position that may have happened in the meantime (e.g. on a separate scrolling thread).\n    // So we only set these when we require an adjustment of the scroll position.\n    // See issue #2 for more information.\n    if (\n      scrollPositionChangeReason === SCROLL_POSITION_CHANGE_REASONS.REQUESTED\n    ) {\n      if (\n        scrollLeft >= 0 &&\n        scrollLeft !== prevState.scrollLeft &&\n        scrollLeft !== this._scrollingContainer.scrollLeft\n      ) {\n        this._scrollingContainer.scrollLeft = scrollLeft;\n      }\n      if (\n        scrollTop >= 0 &&\n        scrollTop !== prevState.scrollTop &&\n        scrollTop !== this._scrollingContainer.scrollTop\n      ) {\n        this._scrollingContainer.scrollTop = scrollTop;\n      }\n    }\n\n    // Update scroll offsets if the current :scrollToCell values requires it\n    if (\n      height !== prevProps.height ||\n      scrollToAlignment !== prevProps.scrollToAlignment ||\n      scrollToCell !== prevProps.scrollToCell ||\n      width !== prevProps.width\n    ) {\n      this._updateScrollPositionForScrollToCell();\n    }\n\n    // Update onRowsRendered callback if start/stop indices have changed\n    this._invokeOnSectionRenderedHelper();\n  }\n\n  componentWillUnmount() {\n    if (this._disablePointerEventsTimeoutId) {\n      clearTimeout(this._disablePointerEventsTimeoutId);\n    }\n  }\n\n  render() {\n    const {\n      autoHeight,\n      cellCount,\n      cellLayoutManager,\n      className,\n      height,\n      horizontalOverscanSize,\n      id,\n      noContentRenderer,\n      style,\n      verticalOverscanSize,\n      width,\n    } = this.props;\n\n    const {isScrolling, scrollLeft, scrollTop} = this.state;\n\n    // Memoization reset\n    if (\n      this._lastRenderedCellCount !== cellCount ||\n      this._lastRenderedCellLayoutManager !== cellLayoutManager ||\n      this._calculateSizeAndPositionDataOnNextUpdate\n    ) {\n      this._lastRenderedCellCount = cellCount;\n      this._lastRenderedCellLayoutManager = cellLayoutManager;\n      this._calculateSizeAndPositionDataOnNextUpdate = false;\n\n      cellLayoutManager.calculateSizeAndPositionData();\n    }\n\n    const {\n      height: totalHeight,\n      width: totalWidth,\n    } = cellLayoutManager.getTotalSize();\n\n    // Safely expand the rendered area by the specified overscan amount\n    const left = Math.max(0, scrollLeft - horizontalOverscanSize);\n    const top = Math.max(0, scrollTop - verticalOverscanSize);\n    const right = Math.min(\n      totalWidth,\n      scrollLeft + width + horizontalOverscanSize,\n    );\n    const bottom = Math.min(\n      totalHeight,\n      scrollTop + height + verticalOverscanSize,\n    );\n\n    const childrenToDisplay =\n      height > 0 && width > 0\n        ? cellLayoutManager.cellRenderers({\n            height: bottom - top,\n            isScrolling,\n            width: right - left,\n            x: left,\n            y: top,\n          })\n        : [];\n\n    const collectionStyle = {\n      boxSizing: 'border-box',\n      direction: 'ltr',\n      height: autoHeight ? 'auto' : height,\n      position: 'relative',\n      WebkitOverflowScrolling: 'touch',\n      width,\n      willChange: 'transform',\n    };\n\n    // Force browser to hide scrollbars when we know they aren't necessary.\n    // Otherwise once scrollbars appear they may not disappear again.\n    // For more info see issue #116\n    const verticalScrollBarSize =\n      totalHeight > height ? this._scrollbarSize : 0;\n    const horizontalScrollBarSize =\n      totalWidth > width ? this._scrollbarSize : 0;\n\n    // Also explicitly init styles to 'auto' if scrollbars are required.\n    // This works around an obscure edge case where external CSS styles have not yet been loaded,\n    // But an initial scroll index of offset is set as an external prop.\n    // Without this style, Grid would render the correct range of cells but would NOT update its internal offset.\n    // This was originally reported via clauderic/react-infinite-calendar/issues/23\n    collectionStyle.overflowX =\n      totalWidth + verticalScrollBarSize <= width ? 'hidden' : 'auto';\n    collectionStyle.overflowY =\n      totalHeight + horizontalScrollBarSize <= height ? 'hidden' : 'auto';\n\n    return (\n      <div\n        ref={this._setScrollingContainerRef}\n        aria-label={this.props['aria-label']}\n        className={clsx('ReactVirtualized__Collection', className)}\n        id={id}\n        onScroll={this._onScroll}\n        role=\"grid\"\n        style={{\n          ...collectionStyle,\n          ...style,\n        }}\n        tabIndex={0}>\n        {cellCount > 0 && (\n          <div\n            className=\"ReactVirtualized__Collection__innerScrollContainer\"\n            style={{\n              height: totalHeight,\n              maxHeight: totalHeight,\n              maxWidth: totalWidth,\n              overflow: 'hidden',\n              pointerEvents: isScrolling ? 'none' : '',\n              width: totalWidth,\n            }}>\n            {childrenToDisplay}\n          </div>\n        )}\n        {cellCount === 0 && noContentRenderer()}\n      </div>\n    );\n  }\n\n  /* ---------------------------- Helper methods ---------------------------- */\n\n  /**\n   * Sets an :isScrolling flag for a small window of time.\n   * This flag is used to disable pointer events on the scrollable portion of the Collection.\n   * This prevents jerky/stuttery mouse-wheel scrolling.\n   */\n  _enablePointerEventsAfterDelay() {\n    if (this._disablePointerEventsTimeoutId) {\n      clearTimeout(this._disablePointerEventsTimeoutId);\n    }\n\n    this._disablePointerEventsTimeoutId = setTimeout(() => {\n      const {isScrollingChange} = this.props;\n\n      isScrollingChange(false);\n\n      this._disablePointerEventsTimeoutId = null;\n      this.setState({\n        isScrolling: false,\n      });\n    }, IS_SCROLLING_TIMEOUT);\n  }\n\n  _invokeOnSectionRenderedHelper = () => {\n    const {cellLayoutManager, onSectionRendered} = this.props;\n\n    this._onSectionRenderedMemoizer({\n      callback: onSectionRendered,\n      indices: {\n        indices: cellLayoutManager.getLastRenderedIndices(),\n      },\n    });\n  };\n\n  _invokeOnScrollMemoizer({scrollLeft, scrollTop, totalHeight, totalWidth}) {\n    this._onScrollMemoizer({\n      callback: ({scrollLeft, scrollTop}) => {\n        const {height, onScroll, width} = this.props;\n\n        onScroll({\n          clientHeight: height,\n          clientWidth: width,\n          scrollHeight: totalHeight,\n          scrollLeft,\n          scrollTop,\n          scrollWidth: totalWidth,\n        });\n      },\n      indices: {\n        scrollLeft,\n        scrollTop,\n      },\n    });\n  }\n\n  _setScrollingContainerRef = ref => {\n    this._scrollingContainer = ref;\n  };\n\n  _setScrollPosition({scrollLeft, scrollTop}) {\n    const newState = {\n      scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED,\n    };\n\n    if (scrollLeft >= 0) {\n      newState.scrollLeft = scrollLeft;\n    }\n\n    if (scrollTop >= 0) {\n      newState.scrollTop = scrollTop;\n    }\n\n    if (\n      (scrollLeft >= 0 && scrollLeft !== this.state.scrollLeft) ||\n      (scrollTop >= 0 && scrollTop !== this.state.scrollTop)\n    ) {\n      this.setState(newState);\n    }\n  }\n\n  _updateScrollPositionForScrollToCell = () => {\n    const {\n      cellLayoutManager,\n      height,\n      scrollToAlignment,\n      scrollToCell,\n      width,\n    } = this.props;\n    const {scrollLeft, scrollTop} = this.state;\n\n    if (scrollToCell >= 0) {\n      const scrollPosition = cellLayoutManager.getScrollPositionForCell({\n        align: scrollToAlignment,\n        cellIndex: scrollToCell,\n        height,\n        scrollLeft,\n        scrollTop,\n        width,\n      });\n\n      if (\n        scrollPosition.scrollLeft !== scrollLeft ||\n        scrollPosition.scrollTop !== scrollTop\n      ) {\n        this._setScrollPosition(scrollPosition);\n      }\n    }\n  };\n\n  _onScroll = event => {\n    // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollLeft / target.scrollTop.\n    // This invalid event can be detected by comparing event.target to this component's scrollable DOM element.\n    // See issue #404 for more information.\n    if (event.target !== this._scrollingContainer) {\n      return;\n    }\n\n    // Prevent pointer events from interrupting a smooth scroll\n    this._enablePointerEventsAfterDelay();\n\n    // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events,\n    // Gradually converging on a scrollTop that is within the bounds of the new, smaller height.\n    // This causes a series of rapid renders that is slow for long lists.\n    // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height.\n    const {cellLayoutManager, height, isScrollingChange, width} = this.props;\n    const scrollbarSize = this._scrollbarSize;\n    const {\n      height: totalHeight,\n      width: totalWidth,\n    } = cellLayoutManager.getTotalSize();\n    const scrollLeft = Math.max(\n      0,\n      Math.min(totalWidth - width + scrollbarSize, event.target.scrollLeft),\n    );\n    const scrollTop = Math.max(\n      0,\n      Math.min(totalHeight - height + scrollbarSize, event.target.scrollTop),\n    );\n\n    // Certain devices (like Apple touchpad) rapid-fire duplicate events.\n    // Don't force a re-render if this is the case.\n    // The mouse may move faster then the animation frame does.\n    // Use requestAnimationFrame to avoid over-updating.\n    if (\n      this.state.scrollLeft !== scrollLeft ||\n      this.state.scrollTop !== scrollTop\n    ) {\n      // Browsers with cancelable scroll events (eg. Firefox) interrupt scrolling animations if scrollTop/scrollLeft is set.\n      // Other browsers (eg. Safari) don't scroll as well without the help under certain conditions (DOM or style changes during scrolling).\n      // All things considered, this seems to be the best current work around that I'm aware of.\n      // For more information see https://github.com/bvaughn/react-virtualized/pull/124\n      const scrollPositionChangeReason = event.cancelable\n        ? SCROLL_POSITION_CHANGE_REASONS.OBSERVED\n        : SCROLL_POSITION_CHANGE_REASONS.REQUESTED;\n\n      // Synchronously set :isScrolling the first time (since _setNextState will reschedule its animation frame each time it's called)\n      if (!this.state.isScrolling) {\n        isScrollingChange(true);\n      }\n\n      this.setState({\n        isScrolling: true,\n        scrollLeft,\n        scrollPositionChangeReason,\n        scrollTop,\n      });\n    }\n\n    this._invokeOnScrollMemoizer({\n      scrollLeft,\n      scrollTop,\n      totalWidth,\n      totalHeight,\n    });\n  };\n}\n\npolyfill(CollectionView);\n\nexport default CollectionView;\n"
  },
  {
    "path": "source/Collection/Section.jest.js",
    "content": "import Section from './Section';\n\ndescribe('Section', () => {\n  function helper({height = 100, width = 200, x = 0, y = 0} = {}) {\n    return new Section({\n      height,\n      width,\n      x,\n      y,\n    });\n  }\n\n  it('should add a new cell index', () => {\n    const section = helper();\n    expect(section.getCellIndices()).toEqual([]);\n    section.addCellIndex({index: 0});\n    expect(section.getCellIndices()).toEqual([0]);\n    section.addCellIndex({index: 1});\n    expect(section.getCellIndices()).toEqual([0, 1]);\n  });\n\n  it('should not add a duplicate cell index', () => {\n    const section = helper();\n    section.addCellIndex({index: 0});\n    section.addCellIndex({index: 1});\n    section.addCellIndex({index: 0});\n    section.addCellIndex({index: 1});\n    section.addCellIndex({index: 2});\n    expect(section.getCellIndices()).toEqual([0, 1, 2]);\n  });\n\n  it('should define a working toString() method for debugging', () => {\n    const section = helper({\n      height: 100,\n      width: 200,\n      x: 25,\n      y: 50,\n    });\n\n    expect(section.toString()).toEqual('25,50 200x100');\n  });\n});\n"
  },
  {
    "path": "source/Collection/Section.js",
    "content": "/** @flow */\nimport type {Index, SizeAndPositionInfo} from './types';\n\n/**\n * A section of the Window.\n * Window Sections are used to group nearby cells.\n * This enables us to more quickly determine which cells to display in a given region of the Window.\n * Sections have a fixed size and contain 0 to many cells (tracked by their indices).\n */\nexport default class Section {\n  constructor({height, width, x, y}: SizeAndPositionInfo) {\n    this.height = height;\n    this.width = width;\n    this.x = x;\n    this.y = y;\n\n    this._indexMap = {};\n    this._indices = [];\n  }\n\n  /** Add a cell to this section. */\n  addCellIndex({index}: Index) {\n    if (!this._indexMap[index]) {\n      this._indexMap[index] = true;\n      this._indices.push(index);\n    }\n  }\n\n  /** Get all cell indices that have been added to this section. */\n  getCellIndices(): Array<number> {\n    return this._indices;\n  }\n\n  /** Intended for debugger/test purposes only */\n  toString() {\n    return `${this.x},${this.y} ${this.width}x${this.height}`;\n  }\n}\n"
  },
  {
    "path": "source/Collection/SectionManager.jest.js",
    "content": "import SectionManager from './SectionManager';\nimport {CELLS, SECTION_SIZE} from './TestData';\n\nfunction initSectionManager() {\n  const sectionManager = new SectionManager(SECTION_SIZE);\n  CELLS.forEach((cellMetadatum, index) => {\n    sectionManager.registerCell({\n      cellMetadatum,\n      index,\n    });\n  });\n  return sectionManager;\n}\n\nfunction verifySections(\n  sectionManager,\n  sizeAndPosition,\n  expectedSizeAndPositionInfos,\n) {\n  const sections = sectionManager.getSections(sizeAndPosition);\n  expect(sections.length).toEqual(expectedSizeAndPositionInfos.length);\n  expectedSizeAndPositionInfos.forEach(sizeAndPosition => {\n    const match = sections.find(\n      section =>\n        section.x === sizeAndPosition.x && section.y === sizeAndPosition.y,\n    );\n    expect(!!match).toEqual(true);\n  });\n}\n\ndescribe('SectionManager', () => {\n  it('creates the appropriate number of Sections', () => {\n    const sectionManager = initSectionManager();\n    expect(sectionManager.getTotalSectionCount()).toEqual(6);\n  });\n\n  it('returns the proper Sections based on the specified area', () => {\n    const sectionManager = initSectionManager();\n    verifySections(sectionManager, {x: 0, y: 0, width: 1, height: 1}, [\n      {x: 0, y: 0},\n    ]);\n    verifySections(sectionManager, {x: 1, y: 1, width: 1, height: 1}, [\n      {x: 0, y: 0},\n    ]);\n    verifySections(sectionManager, {x: 0, y: 0, width: 4, height: 4}, [\n      {x: 0, y: 0},\n      {x: 2, y: 0},\n      {x: 0, y: 2},\n      {x: 2, y: 2},\n    ]);\n    verifySections(sectionManager, {x: 4, y: 0, width: 2, height: 3}, [\n      {x: 4, y: 0},\n      {x: 4, y: 2},\n    ]);\n  });\n\n  it('assigns cells to the appropriate sections', () => {\n    const sectionManager = initSectionManager();\n    expect(\n      sectionManager.getCellIndices({x: 0, y: 0, width: 2, height: 2}),\n    ).toEqual([0]);\n    expect(\n      sectionManager.getCellIndices({x: 2, y: 0, width: 2, height: 2}),\n    ).toEqual([1, 2, 3]);\n    expect(\n      sectionManager.getCellIndices({x: 4, y: 0, width: 2, height: 2}),\n    ).toEqual([6]);\n    expect(\n      sectionManager.getCellIndices({x: 0, y: 2, width: 2, height: 2}),\n    ).toEqual([4]);\n    expect(\n      sectionManager.getCellIndices({x: 2, y: 2, width: 2, height: 2}),\n    ).toEqual([3, 4, 5]);\n    expect(\n      sectionManager.getCellIndices({x: 4, y: 2, width: 2, height: 2}),\n    ).toEqual([7, 8, 9]);\n  });\n});\n"
  },
  {
    "path": "source/Collection/SectionManager.js",
    "content": "/**\n * Window Sections are used to group nearby cells.\n * This enables us to more quickly determine which cells to display in a given region of the Window.\n * @flow\n */\nimport Section from './Section';\nimport type {Index, SizeAndPositionInfo} from './types';\n\nconst SECTION_SIZE = 100;\n\ntype RegisterCellParams = {\n  cellMetadatum: SizeAndPositionInfo,\n  index: number,\n};\n\n/**\n * Contains 0 to many Sections.\n * Grows (and adds Sections) dynamically as cells are registered.\n * Automatically adds cells to the appropriate Section(s).\n */\nexport default class SectionManager {\n  constructor(sectionSize = SECTION_SIZE) {\n    this._sectionSize = sectionSize;\n\n    this._cellMetadata = [];\n    this._sections = {};\n  }\n\n  /**\n   * Gets all cell indices contained in the specified region.\n   * A region may encompass 1 or more Sections.\n   */\n  getCellIndices({height, width, x, y}: SizeAndPositionInfo): Array<number> {\n    const indices = {};\n\n    this.getSections({height, width, x, y}).forEach(section =>\n      section.getCellIndices().forEach(index => {\n        indices[index] = index;\n      }),\n    );\n\n    // Object keys are strings; this function returns numbers\n    return Object.keys(indices).map(index => indices[index]);\n  }\n\n  /** Get size and position information for the cell specified. */\n  getCellMetadata({index}: Index): SizeAndPositionInfo {\n    return this._cellMetadata[index];\n  }\n\n  /** Get all Sections overlapping the specified region. */\n  getSections({height, width, x, y}: SizeAndPositionInfo): Array<Section> {\n    const sectionXStart = Math.floor(x / this._sectionSize);\n    const sectionXStop = Math.floor((x + width - 1) / this._sectionSize);\n    const sectionYStart = Math.floor(y / this._sectionSize);\n    const sectionYStop = Math.floor((y + height - 1) / this._sectionSize);\n\n    const sections = [];\n\n    for (let sectionX = sectionXStart; sectionX <= sectionXStop; sectionX++) {\n      for (let sectionY = sectionYStart; sectionY <= sectionYStop; sectionY++) {\n        const key = `${sectionX}.${sectionY}`;\n\n        if (!this._sections[key]) {\n          this._sections[key] = new Section({\n            height: this._sectionSize,\n            width: this._sectionSize,\n            x: sectionX * this._sectionSize,\n            y: sectionY * this._sectionSize,\n          });\n        }\n\n        sections.push(this._sections[key]);\n      }\n    }\n\n    return sections;\n  }\n\n  /** Total number of Sections based on the currently registered cells. */\n  getTotalSectionCount() {\n    return Object.keys(this._sections).length;\n  }\n\n  /** Intended for debugger/test purposes only */\n  toString() {\n    return Object.keys(this._sections).map(index =>\n      this._sections[index].toString(),\n    );\n  }\n\n  /** Adds a cell to the appropriate Sections and registers it metadata for later retrievable. */\n  registerCell({cellMetadatum, index}: RegisterCellParams) {\n    this._cellMetadata[index] = cellMetadatum;\n\n    this.getSections(cellMetadatum).forEach(section =>\n      section.addCellIndex({index}),\n    );\n  }\n}\n"
  },
  {
    "path": "source/Collection/TestData.js",
    "content": "/*\n  0 1 2 3 4 5\n ┏━━━┯━━━┯━━━┓\n0┃0 0┊1 3┊6 6┃\n1┃0 0┊2 3┊6 6┃\n ┠┈┈┈┼┈┈┈┼┈┈┈┨\n2┃4 4┊4 3┊7 8┃\n3┃4 4┊4 5┊9 9┃\n ┗━━━┷━━━┷━━━┛\n\nSections to Cells map:\n 0.0 [0]\n 1.0 [1, 2, 3]\n 2.0 [6]\n 0.1 [4]\n 1.1 [3, 4, 5]\n 2.1 [7, 8, 9]\n*/\n\nexport const CELLS = [\n  {x: 0, y: 0, width: 2, height: 2},\n  {x: 2, y: 0, width: 1, height: 1},\n  {x: 2, y: 1, width: 1, height: 1},\n  {x: 3, y: 0, width: 1, height: 3},\n  {x: 0, y: 2, width: 3, height: 2},\n  {x: 3, y: 3, width: 1, height: 1},\n  {x: 4, y: 0, width: 2, height: 2},\n  {x: 4, y: 2, width: 1, height: 1},\n  {x: 5, y: 2, width: 1, height: 1},\n  {x: 4, y: 3, width: 2, height: 1},\n];\n\nexport const SECTION_SIZE = 2;\n"
  },
  {
    "path": "source/Collection/index.js",
    "content": "/** @flow */\nimport Collection from './Collection';\n\nexport default Collection;\nexport {Collection};\n"
  },
  {
    "path": "source/Collection/types.js",
    "content": "/** @flow */\n\nexport type Index = {\n  index: number,\n};\n\nexport type PositionInfo = {\n  x: number,\n  y: number,\n};\n\nexport type ScrollPosition = {\n  scrollLeft: number,\n  scrollTop: number,\n};\n\nexport type SizeAndPositionInfo = {\n  height: number,\n  width: number,\n  x: number,\n  y: number,\n};\n\nexport type SizeInfo = {\n  height: number,\n  width: number,\n};\n"
  },
  {
    "path": "source/Collection/utils/calculateSizeAndPositionData.jest.js",
    "content": "import calculateSizeAndPositionData from './calculateSizeAndPositionData';\n\ndescribe('calculateSizeAndPositionData', () => {\n  it('should query for size and position of each cell', () => {\n    const cellSizeAndPositionGetterCalls = [];\n    function cellSizeAndPositionGetter({index}) {\n      cellSizeAndPositionGetterCalls.push(index);\n      return {\n        x: index * 50,\n        y: 0,\n        width: 50,\n        height: 50,\n      };\n    }\n    const {sectionManager} = calculateSizeAndPositionData({\n      cellCount: 3,\n      cellSizeAndPositionGetter,\n    });\n    expect(cellSizeAndPositionGetterCalls).toEqual([0, 1, 2]);\n    expect(sectionManager.getTotalSectionCount()).toEqual(2);\n  });\n\n  it('should throw an error if invalid metadata is returned for a cell', () => {\n    expect(() =>\n      calculateSizeAndPositionData({\n        cellCount: 3,\n        cellSizeAndPositionGetter: () => {},\n      }),\n    ).toThrow();\n  });\n});\n"
  },
  {
    "path": "source/Collection/utils/calculateSizeAndPositionData.js",
    "content": "import SectionManager from '../SectionManager';\n\nexport default function calculateSizeAndPositionData({\n  cellCount,\n  cellSizeAndPositionGetter,\n  sectionSize,\n}) {\n  const cellMetadata = [];\n  const sectionManager = new SectionManager(sectionSize);\n  let height = 0;\n  let width = 0;\n\n  for (let index = 0; index < cellCount; index++) {\n    const cellMetadatum = cellSizeAndPositionGetter({index});\n\n    if (\n      cellMetadatum.height == null ||\n      isNaN(cellMetadatum.height) ||\n      cellMetadatum.width == null ||\n      isNaN(cellMetadatum.width) ||\n      cellMetadatum.x == null ||\n      isNaN(cellMetadatum.x) ||\n      cellMetadatum.y == null ||\n      isNaN(cellMetadatum.y)\n    ) {\n      throw Error(\n        `Invalid metadata returned for cell ${index}:\n        x:${cellMetadatum.x}, y:${cellMetadatum.y}, width:${cellMetadatum.width}, height:${cellMetadatum.height}`,\n      );\n    }\n\n    height = Math.max(height, cellMetadatum.y + cellMetadatum.height);\n    width = Math.max(width, cellMetadatum.x + cellMetadatum.width);\n\n    cellMetadata[index] = cellMetadatum;\n    sectionManager.registerCell({\n      cellMetadatum,\n      index,\n    });\n  }\n\n  return {\n    cellMetadata,\n    height,\n    sectionManager,\n    width,\n  };\n}\n"
  },
  {
    "path": "source/ColumnSizer/ColumnSizer.example.css",
    "content": ".GridContainer {\n  margin-top: 15px;\n  border: 1px solid #e0e0e0;\n}\n\n.cell,\n.firstCell {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  padding: 0 .5em;\n  border-left: 1px solid #e0e0e0;\n}\n.firstCell {\n  border-left: none;\n}\n\n.noCells {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 1em;\n  color: #bdbdbd;\n}\n"
  },
  {
    "path": "source/ColumnSizer/ColumnSizer.example.js",
    "content": "/**\n * @flow\n */\nimport * as React from 'react';\nimport styles from './ColumnSizer.example.css';\nimport AutoSizer from '../AutoSizer';\nimport ColumnSizer from './ColumnSizer';\nimport Grid from '../Grid';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\n\nexport default class ColumnSizerExample extends React.PureComponent {\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      columnMaxWidth: 100,\n      columnMinWidth: 75,\n      columnCount: 10,\n    };\n\n    this._noColumnMaxWidthChange = this._noColumnMaxWidthChange.bind(this);\n    this._noColumnMinWidthChange = this._noColumnMinWidthChange.bind(this);\n    this._onColumnCountChange = this._onColumnCountChange.bind(this);\n    this._noContentRenderer = this._noContentRenderer.bind(this);\n    this._cellRenderer = this._cellRenderer.bind(this);\n  }\n\n  render() {\n    const {columnMaxWidth, columnMinWidth, columnCount} = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"ColumnSizer\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/ColumnSizer/ColumnSizer.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/ColumnSizer.md\"\n        />\n\n        <ContentBoxParagraph>\n          This component decorates a <code>Grid</code> and calculates the width\n          of its columns based on the current (<code>Grid</code>) width.\n        </ContentBoxParagraph>\n\n        <InputRow>\n          <LabeledInput\n            label=\"Num Columns\"\n            name=\"columnCount\"\n            onChange={this._onColumnCountChange}\n            value={columnCount}\n          />\n          <LabeledInput\n            label=\"Column Min Width\"\n            name=\"columnMinWidth\"\n            onChange={this._noColumnMinWidthChange}\n            value={columnMinWidth}\n          />\n          <LabeledInput\n            label=\"Column Max Width\"\n            name=\"columnMaxWidth\"\n            onChange={this._noColumnMaxWidthChange}\n            value={columnMaxWidth}\n          />\n        </InputRow>\n\n        <div>\n          <AutoSizer disableHeight>\n            {({width}) => (\n              <ColumnSizer\n                columnMaxWidth={columnMaxWidth}\n                columnMinWidth={columnMinWidth}\n                columnCount={columnCount}\n                key=\"GridColumnSizer\"\n                width={width}>\n                {({adjustedWidth, columnWidth, registerChild}) => (\n                  <div\n                    className={styles.GridContainer}\n                    style={{\n                      height: 50,\n                      width: adjustedWidth,\n                    }}>\n                    <Grid\n                      ref={registerChild}\n                      columnWidth={columnWidth}\n                      columnCount={columnCount}\n                      height={50}\n                      noContentRenderer={this._noContentRenderer}\n                      cellRenderer={this._cellRenderer}\n                      rowHeight={50}\n                      rowCount={1}\n                      width={adjustedWidth}\n                    />\n                  </div>\n                )}\n              </ColumnSizer>\n            )}\n          </AutoSizer>\n        </div>\n      </ContentBox>\n    );\n  }\n\n  _noColumnMaxWidthChange(event) {\n    let columnMaxWidth = parseInt(event.target.value, 10);\n\n    if (isNaN(columnMaxWidth)) {\n      columnMaxWidth = undefined;\n    } else {\n      columnMaxWidth = Math.min(1000, columnMaxWidth);\n    }\n\n    this.setState({columnMaxWidth});\n  }\n\n  _noColumnMinWidthChange(event) {\n    let columnMinWidth = parseInt(event.target.value, 10);\n\n    if (isNaN(columnMinWidth)) {\n      columnMinWidth = undefined;\n    } else {\n      columnMinWidth = Math.max(1, columnMinWidth);\n    }\n\n    this.setState({columnMinWidth});\n  }\n\n  _onColumnCountChange(event) {\n    this.setState({columnCount: parseInt(event.target.value, 10) || 0});\n  }\n\n  _noContentRenderer() {\n    return <div className={styles.noCells}>No cells</div>;\n  }\n\n  _cellRenderer({columnIndex, key, rowIndex, style}) {\n    const className = columnIndex === 0 ? styles.firstCell : styles.cell;\n\n    return (\n      <div className={className} key={key} style={style}>\n        {`R:${rowIndex}, C:${columnIndex}`}\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "source/ColumnSizer/ColumnSizer.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport ColumnSizer from './ColumnSizer';\nimport Grid from '../Grid';\n\ndescribe('ColumnSizer', () => {\n  function getMarkup({\n    columnMinWidth = undefined,\n    columnMaxWidth = undefined,\n    columnCount = 10,\n    width = 200,\n  } = {}) {\n    function cellRenderer({columnIndex, key, rowIndex, style}) {\n      return (\n        <div className=\"gridItem\" key={key} style={style}>\n          {`row:${rowIndex}, column:${columnIndex}`}\n        </div>\n      );\n    }\n\n    return (\n      <ColumnSizer\n        columnMinWidth={columnMinWidth}\n        columnMaxWidth={columnMaxWidth}\n        columnCount={columnCount}\n        width={width}>\n        {({adjustedWidth, columnWidth, registerChild}) => (\n          <div>\n            <Grid\n              columnCount={columnCount}\n              columnWidth={columnWidth}\n              height={50}\n              ref={registerChild}\n              cellRenderer={cellRenderer}\n              rowHeight={50}\n              rowCount={1}\n              width={adjustedWidth}\n            />\n            <div className=\"debug\">\n              {`adjustedWidth:${adjustedWidth} columnWidth:${columnWidth}`}\n            </div>\n          </div>\n        )}\n      </ColumnSizer>\n    );\n  }\n\n  it('should distribute column widths evenly if no min/max boundaries have been set', () => {\n    const rendered = findDOMNode(render(getMarkup()));\n    expect(rendered.querySelector('.debug').textContent).toContain(\n      'columnWidth:20',\n    );\n  });\n\n  it('should respect :columnMaxWidth if specified', () => {\n    const rendered = findDOMNode(\n      render(\n        getMarkup({\n          columnMaxWidth: 10,\n        }),\n      ),\n    );\n    expect(rendered.querySelector('.debug').textContent).toContain(\n      'columnWidth:10',\n    );\n  });\n\n  it('should respect :columnMinWidth if specified', () => {\n    const rendered = findDOMNode(\n      render(\n        getMarkup({\n          columnMinWidth: 30,\n        }),\n      ),\n    );\n    expect(rendered.querySelector('.debug').textContent).toContain(\n      'columnWidth:30',\n    );\n  });\n\n  describe('recomputeGridSize', () => {\n    function helper(updatedProps, expectedTextContent) {\n      const renderedA = findDOMNode(render(getMarkup()));\n      expect(renderedA.querySelector('.debug').textContent).toContain(\n        'columnWidth:20',\n      );\n\n      const renderedB = findDOMNode(render(getMarkup(updatedProps)));\n      expect(renderedB.querySelector('.debug').textContent).toContain(\n        expectedTextContent,\n      );\n    }\n\n    it('should recompute metadata sizes if :columnMinWidth changes', () => {\n      helper({columnMinWidth: 30}, 'columnWidth:30');\n    });\n\n    it('should recompute metadata sizes if :columnMaxWidth changes', () => {\n      helper({columnMaxWidth: 15}, 'columnWidth:15');\n    });\n\n    it('should recompute metadata sizes if :width changes', () => {\n      helper({width: 300}, 'columnWidth:30');\n    });\n\n    it('should recompute metadata sizes if :columnCount changes', () => {\n      helper({columnCount: 2}, 'columnWidth:100');\n    });\n  });\n\n  it('should pass the :width as :adjustedWidth if columns require more than the :width to be displayed', () => {\n    const rendered = findDOMNode(\n      render(\n        getMarkup({\n          columnMinWidth: 30,\n        }),\n      ),\n    );\n    expect(rendered.querySelector('.debug').textContent).toContain(\n      'adjustedWidth:200',\n    );\n  });\n\n  it('should pass an :adjustedWidth if columns require less than the :width to be displayed', () => {\n    const rendered = findDOMNode(\n      render(\n        getMarkup({\n          columnMaxWidth: 10,\n        }),\n      ),\n    );\n    expect(rendered.querySelector('.debug').textContent).toContain(\n      'adjustedWidth:100',\n    );\n  });\n\n  it('should error if the registered child is not a Grid or a MultiGrid', () => {\n    spyOn(console, 'error');\n\n    expect(() => {\n      render(\n        <ColumnSizer\n          columnMinWidth={100}\n          columnMaxWidth={100}\n          columnCount={100}\n          width={100}>\n          {({registerChild}) => <div ref={registerChild} />}\n        </ColumnSizer>,\n      );\n    }).toThrow();\n  });\n});\n"
  },
  {
    "path": "source/ColumnSizer/ColumnSizer.js",
    "content": "/** @flow */\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\n\n/**\n * High-order component that auto-calculates column-widths for `Grid` cells.\n */\nexport default class ColumnSizer extends React.PureComponent {\n  static propTypes = {\n    /**\n     * Function responsible for rendering a virtualized Grid.\n     * This function should implement the following signature:\n     * ({ adjustedWidth, getColumnWidth, registerChild }) => PropTypes.element\n     *\n     * The specified :getColumnWidth function should be passed to the Grid's :columnWidth property.\n     * The :registerChild should be passed to the Grid's :ref property.\n     * The :adjustedWidth property is optional; it reflects the lesser of the overall width or the width of all columns.\n     */\n    children: PropTypes.func.isRequired,\n\n    /** Optional maximum allowed column width */\n    columnMaxWidth: PropTypes.number,\n\n    /** Optional minimum allowed column width */\n    columnMinWidth: PropTypes.number,\n\n    /** Number of columns in Grid or Table child */\n    columnCount: PropTypes.number.isRequired,\n\n    /** Width of Grid or Table child */\n    width: PropTypes.number.isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._registerChild = this._registerChild.bind(this);\n  }\n\n  componentDidUpdate(prevProps) {\n    const {columnMaxWidth, columnMinWidth, columnCount, width} = this.props;\n\n    if (\n      columnMaxWidth !== prevProps.columnMaxWidth ||\n      columnMinWidth !== prevProps.columnMinWidth ||\n      columnCount !== prevProps.columnCount ||\n      width !== prevProps.width\n    ) {\n      if (this._registeredChild) {\n        this._registeredChild.recomputeGridSize();\n      }\n    }\n  }\n\n  render() {\n    const {\n      children,\n      columnMaxWidth,\n      columnMinWidth,\n      columnCount,\n      width,\n    } = this.props;\n\n    const safeColumnMinWidth = columnMinWidth || 1;\n\n    const safeColumnMaxWidth = columnMaxWidth\n      ? Math.min(columnMaxWidth, width)\n      : width;\n\n    let columnWidth = width / columnCount;\n    columnWidth = Math.max(safeColumnMinWidth, columnWidth);\n    columnWidth = Math.min(safeColumnMaxWidth, columnWidth);\n    columnWidth = Math.floor(columnWidth);\n\n    let adjustedWidth = Math.min(width, columnWidth * columnCount);\n\n    return children({\n      adjustedWidth,\n      columnWidth,\n      getColumnWidth: () => columnWidth,\n      registerChild: this._registerChild,\n    });\n  }\n\n  _registerChild(child) {\n    if (child && typeof child.recomputeGridSize !== 'function') {\n      throw Error(\n        'Unexpected child type registered; only Grid/MultiGrid children are supported.',\n      );\n    }\n\n    this._registeredChild = child;\n\n    if (this._registeredChild) {\n      this._registeredChild.recomputeGridSize();\n    }\n  }\n}\n"
  },
  {
    "path": "source/ColumnSizer/index.js",
    "content": "/** @flow */\nimport ColumnSizer from './ColumnSizer';\n\nexport default ColumnSizer;\nexport {ColumnSizer};\n"
  },
  {
    "path": "source/Grid/Grid.example.css",
    "content": ".GridRow {\n  margin-top: 15px;\n  display: flex;\n  flex-direction: row;\n}\n.GridColumn {\n  display: flex;\n  flex-direction: column;\n  flex: 1 1 auto;\n}\n.LeftSideGridContainer {\n  flex: 0 0 50px;\n}\n\n.BodyGrid {\n  width: 100%;\n  border: 1px solid #e0e0e0;\n}\n\n.evenRow,\n.oddRow {\n  border-bottom: 1px solid #e0e0e0;\n}\n.oddRow {\n  background-color: #fafafa;\n}\n\n.cell,\n.headerCell {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  padding: 0 .5em;\n}\n.cell {\n  border-right: 1px solid #e0e0e0;\n  border-bottom: 1px solid #e0e0e0;\n}\n.headerCell {\n  font-weight: bold;\n  border-right: 1px solid #e0e0e0;\n}\n.centeredCell {\n  align-items: center;\n  text-align: center;\n}\n\n.letterCell {\n  font-size: 1.5em;\n  color: #fff;\n  text-align: center;\n}\n\n.noCells {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 1em;\n  color: #bdbdbd;\n}\n"
  },
  {
    "path": "source/Grid/Grid.example.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\nimport AutoSizer from '../AutoSizer';\nimport Grid from './Grid';\nimport clsx from 'clsx';\nimport styles from './Grid.example.css';\n\nexport default class GridExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      columnCount: 1000,\n      height: 300,\n      overscanColumnCount: 0,\n      overscanRowCount: 10,\n      rowHeight: 40,\n      rowCount: 1000,\n      scrollToColumn: undefined,\n      scrollToRow: undefined,\n      useDynamicRowHeight: false,\n    };\n\n    this._cellRenderer = this._cellRenderer.bind(this);\n    this._getColumnWidth = this._getColumnWidth.bind(this);\n    this._getRowClassName = this._getRowClassName.bind(this);\n    this._getRowHeight = this._getRowHeight.bind(this);\n    this._noContentRenderer = this._noContentRenderer.bind(this);\n    this._onColumnCountChange = this._onColumnCountChange.bind(this);\n    this._onRowCountChange = this._onRowCountChange.bind(this);\n    this._onScrollToColumnChange = this._onScrollToColumnChange.bind(this);\n    this._onScrollToRowChange = this._onScrollToRowChange.bind(this);\n    this._renderBodyCell = this._renderBodyCell.bind(this);\n    this._renderLeftSideCell = this._renderLeftSideCell.bind(this);\n  }\n\n  render() {\n    const {\n      columnCount,\n      height,\n      overscanColumnCount,\n      overscanRowCount,\n      rowHeight,\n      rowCount,\n      scrollToColumn,\n      scrollToRow,\n      useDynamicRowHeight,\n    } = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"Grid\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/Grid.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/Grid.md\"\n        />\n\n        <ContentBoxParagraph>\n          Renders tabular data with virtualization along the vertical and\n          horizontal axes. Row heights and column widths must be calculated\n          ahead of time and specified as a fixed size or returned by a getter\n          function.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Use dynamic row height?\"\n              className={styles.checkbox}\n              type=\"checkbox\"\n              value={useDynamicRowHeight}\n              onChange={event =>\n                this._updateUseDynamicRowHeights(event.target.checked)\n              }\n            />\n            Use dynamic row height?\n          </label>\n        </ContentBoxParagraph>\n\n        <InputRow>\n          <LabeledInput\n            label=\"Num columns\"\n            name=\"columnCount\"\n            onChange={this._onColumnCountChange}\n            value={columnCount}\n          />\n          <LabeledInput\n            label=\"Num rows\"\n            name=\"rowCount\"\n            onChange={this._onRowCountChange}\n            value={rowCount}\n          />\n          <LabeledInput\n            label=\"Scroll to column\"\n            name=\"onScrollToColumn\"\n            placeholder=\"Index...\"\n            onChange={this._onScrollToColumnChange}\n            value={scrollToColumn || ''}\n          />\n          <LabeledInput\n            label=\"Scroll to row\"\n            name=\"onScrollToRow\"\n            placeholder=\"Index...\"\n            onChange={this._onScrollToRowChange}\n            value={scrollToRow || ''}\n          />\n          <LabeledInput\n            label=\"List height\"\n            name=\"height\"\n            onChange={event =>\n              this.setState({height: parseInt(event.target.value, 10) || 1})\n            }\n            value={height}\n          />\n          <LabeledInput\n            disabled={useDynamicRowHeight}\n            label=\"Row height\"\n            name=\"rowHeight\"\n            onChange={event =>\n              this.setState({\n                rowHeight: parseInt(event.target.value, 10) || 1,\n              })\n            }\n            value={rowHeight}\n          />\n          <LabeledInput\n            label=\"Overscan columns\"\n            name=\"overscanColumnCount\"\n            onChange={event =>\n              this.setState({\n                overscanColumnCount: parseInt(event.target.value, 10) || 0,\n              })\n            }\n            value={overscanColumnCount}\n          />\n          <LabeledInput\n            label=\"Overscan rows\"\n            name=\"overscanRowCount\"\n            onChange={event =>\n              this.setState({\n                overscanRowCount: parseInt(event.target.value, 10) || 0,\n              })\n            }\n            value={overscanRowCount}\n          />\n        </InputRow>\n\n        <AutoSizer disableHeight>\n          {({width}) => (\n            <Grid\n              cellRenderer={this._cellRenderer}\n              className={styles.BodyGrid}\n              columnWidth={this._getColumnWidth}\n              columnCount={columnCount}\n              height={height}\n              noContentRenderer={this._noContentRenderer}\n              overscanColumnCount={overscanColumnCount}\n              overscanRowCount={overscanRowCount}\n              rowHeight={useDynamicRowHeight ? this._getRowHeight : rowHeight}\n              rowCount={rowCount}\n              scrollToColumn={scrollToColumn}\n              scrollToRow={scrollToRow}\n              width={width}\n            />\n          )}\n        </AutoSizer>\n      </ContentBox>\n    );\n  }\n\n  _cellRenderer({columnIndex, key, rowIndex, style}) {\n    if (columnIndex === 0) {\n      return this._renderLeftSideCell({columnIndex, key, rowIndex, style});\n    } else {\n      return this._renderBodyCell({columnIndex, key, rowIndex, style});\n    }\n  }\n\n  _getColumnWidth({index}) {\n    switch (index) {\n      case 0:\n        return 50;\n      case 1:\n        return 100;\n      case 2:\n        return 300;\n      default:\n        return 80;\n    }\n  }\n\n  _getDatum(index) {\n    const {list} = this.context;\n\n    return list.get(index % list.size);\n  }\n\n  _getRowClassName(row) {\n    return row % 2 === 0 ? styles.evenRow : styles.oddRow;\n  }\n\n  _getRowHeight({index}) {\n    return this._getDatum(index).size;\n  }\n\n  _noContentRenderer() {\n    return <div className={styles.noCells}>No cells</div>;\n  }\n\n  _renderBodyCell({columnIndex, key, rowIndex, style}) {\n    const rowClass = this._getRowClassName(rowIndex);\n    const datum = this._getDatum(rowIndex);\n\n    let content;\n\n    switch (columnIndex) {\n      case 1:\n        content = datum.name;\n        break;\n      case 2:\n        content = datum.random;\n        break;\n      default:\n        content = `r:${rowIndex}, c:${columnIndex}`;\n        break;\n    }\n\n    const classNames = clsx(rowClass, styles.cell, {\n      [styles.centeredCell]: columnIndex > 2,\n    });\n\n    return (\n      <div className={classNames} key={key} style={style}>\n        {content}\n      </div>\n    );\n  }\n\n  _renderLeftSideCell({key, rowIndex, style}) {\n    const datum = this._getDatum(rowIndex);\n\n    const classNames = clsx(styles.cell, styles.letterCell);\n\n    // Don't modify styles.\n    // These are frozen by React now (as of 16.0.0).\n    // Since Grid caches and re-uses them, they aren't safe to modify.\n    style = {\n      ...style,\n      backgroundColor: datum.color,\n    };\n\n    return (\n      <div className={classNames} key={key} style={style}>\n        {datum.name.charAt(0)}\n      </div>\n    );\n  }\n\n  _updateUseDynamicRowHeights(value) {\n    this.setState({\n      useDynamicRowHeight: value,\n    });\n  }\n\n  _onColumnCountChange(event) {\n    const columnCount = parseInt(event.target.value, 10) || 0;\n\n    this.setState({columnCount});\n  }\n\n  _onRowCountChange(event) {\n    const rowCount = parseInt(event.target.value, 10) || 0;\n\n    this.setState({rowCount});\n  }\n\n  _onScrollToColumnChange(event) {\n    const {columnCount} = this.state;\n    let scrollToColumn = Math.min(\n      columnCount - 1,\n      parseInt(event.target.value, 10),\n    );\n\n    if (isNaN(scrollToColumn)) {\n      scrollToColumn = undefined;\n    }\n\n    this.setState({scrollToColumn});\n  }\n\n  _onScrollToRowChange(event) {\n    const {rowCount} = this.state;\n    let scrollToRow = Math.min(rowCount - 1, parseInt(event.target.value, 10));\n\n    if (isNaN(scrollToRow)) {\n      scrollToRow = undefined;\n    }\n\n    this.setState({scrollToRow});\n  }\n}\n"
  },
  {
    "path": "source/Grid/Grid.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {Simulate} from 'react-dom/test-utils';\nimport TestRenderer from 'react-test-renderer';\nimport {render} from '../TestUtils';\nimport Grid, {DEFAULT_SCROLLING_RESET_TIME_INTERVAL} from './Grid';\nimport defaultCellRangeRenderer from './defaultCellRangeRenderer';\nimport {CellMeasurer, CellMeasurerCache} from '../CellMeasurer';\nimport {\n  SCROLL_DIRECTION_BACKWARD,\n  SCROLL_DIRECTION_FORWARD,\n} from './defaultOverscanIndicesGetter';\nimport {getMaxElementSize} from './utils/maxElementSize.js';\n\nconst DEFAULT_COLUMN_WIDTH = 50;\nconst DEFAULT_HEIGHT = 100;\nconst DEFAULT_ROW_HEIGHT = 20;\nconst DEFAULT_WIDTH = 200;\nconst NUM_ROWS = 100;\nconst NUM_COLUMNS = 50;\n\nfunction getScrollbarSize0() {\n  return 0;\n}\n\nfunction getScrollbarSize20() {\n  return 20;\n}\n\ndescribe('Grid', () => {\n  function defaultCellRenderer({columnIndex, key, rowIndex, style}) {\n    return (\n      <div className=\"gridItem\" key={key} style={style}>\n        {`row:${rowIndex}, column:${columnIndex}`}\n      </div>\n    );\n  }\n\n  function simulateScroll({grid, scrollLeft = 0, scrollTop = 0}) {\n    const target = {scrollLeft, scrollTop};\n    grid._scrollingContainer = target; // HACK to work around _onScroll target check\n    Simulate.scroll(findDOMNode(grid), {target});\n  }\n\n  function getMarkup(props = {}) {\n    return (\n      <Grid\n        cellRenderer={defaultCellRenderer}\n        columnCount={NUM_COLUMNS}\n        columnWidth={DEFAULT_COLUMN_WIDTH}\n        getScrollbarSize={getScrollbarSize0}\n        height={DEFAULT_HEIGHT}\n        overscanColumnCount={0}\n        overscanRowCount={0}\n        autoHeight={false}\n        rowHeight={DEFAULT_ROW_HEIGHT}\n        rowCount={NUM_ROWS}\n        width={DEFAULT_WIDTH}\n        {...props}\n      />\n    );\n  }\n\n  describe('number of rendered children', () => {\n    it('should render enough children to fill the available area', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.querySelectorAll('.gridItem').length).toEqual(20); // 5 rows x 4 columns\n    });\n\n    it('should not render more rows than available if the area is not filled', () => {\n      const rendered = findDOMNode(render(getMarkup({rowCount: 2})));\n      expect(rendered.querySelectorAll('.gridItem').length).toEqual(8); // 2 rows x 4 columns\n    });\n\n    it('should not render more columns than available if the area is not filled', () => {\n      const rendered = findDOMNode(render(getMarkup({columnCount: 2})));\n      expect(rendered.querySelectorAll('.gridItem').length).toEqual(10); // 5 rows x 2 columns\n    });\n\n    // Small performance tweak added in 5.5.6\n    it('should not render/parent cells that are null or false', () => {\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        if (columnIndex === 0) {\n          return null;\n        } else if (rowIndex === 0) {\n          return false;\n        } else {\n          return (\n            <div className=\"cell\" key={key} style={style}>\n              {`row:${rowIndex}, column:${columnIndex}`}\n            </div>\n          );\n        }\n      }\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 3,\n            overscanColumnCount: 0,\n            overscanRowCount: 0,\n            rowCount: 3,\n            cellRenderer,\n          }),\n        ),\n      );\n      expect(rendered.querySelectorAll('.cell').length).toEqual(4); // [1,1], [1,2], [2,1], and [2,2]\n      expect(rendered.textContent).not.toContain('column:0');\n      expect(rendered.textContent).not.toContain('row:0');\n    });\n\n    it('should scroll to the last existing point when rows are removed', () => {\n      const grid = render(\n        getMarkup({\n          rowCount: 15,\n        }),\n      );\n\n      simulateScroll({\n        grid,\n        scrollTop: 200,\n      });\n\n      const updatedGrid = render(\n        getMarkup({\n          rowCount: 10,\n        }),\n      );\n\n      expect(updatedGrid.state.scrollTop).toEqual(100);\n    });\n\n    it('should scroll to the last existing point when columns are removed', () => {\n      const grid = render(\n        getMarkup({\n          columnCount: 12,\n        }),\n      );\n\n      simulateScroll({\n        grid,\n        scrollLeft: 400,\n      });\n\n      const updatedGrid = render(\n        getMarkup({\n          columnCount: 8,\n        }),\n      );\n\n      expect(updatedGrid.state.scrollLeft).toEqual(200);\n    });\n\n    it('should not scroll unseen rows are removed', () => {\n      render(\n        getMarkup({\n          rowCount: 15,\n        }),\n      );\n      const updatedGrid = render(\n        getMarkup({\n          rowCount: 10,\n        }),\n      );\n\n      expect(updatedGrid.state.scrollTop).toEqual(0);\n    });\n\n    it('should not scroll when unseen columns are removed', () => {\n      render(\n        getMarkup({\n          columnCount: 12,\n        }),\n      );\n      const updatedGrid = render(\n        getMarkup({\n          columnCount: 8,\n        }),\n      );\n\n      expect(updatedGrid.state.scrollLeft).toEqual(0);\n    });\n  });\n\n  describe('shows and hides scrollbars based on rendered content', () => {\n    it('should set overflowX:hidden if columns fit within the available width and y-axis has no scrollbar', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 4,\n            getScrollbarSize: getScrollbarSize20,\n            rowCount: 5,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).toEqual('hidden');\n    });\n\n    it('should set overflowX:hidden if columns and y-axis scrollbar fit within the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 4,\n            getScrollbarSize: getScrollbarSize20,\n            width: 200 + getScrollbarSize20(),\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).toEqual('hidden');\n    });\n\n    it('should leave overflowX:auto if columns require more than the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 4,\n            getScrollbarSize: getScrollbarSize20,\n            width: 200 - 1,\n            rowCount: 5,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).not.toEqual('hidden');\n    });\n\n    it('should leave overflowX:auto if columns and y-axis scrollbar require more than the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 4,\n            getScrollbarSize: getScrollbarSize20,\n            width: 200 + getScrollbarSize20() - 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowX).not.toEqual('hidden');\n    });\n\n    it('should set overflowY:hidden if rows fit within the available width and xaxis has no scrollbar', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            getScrollbarSize: getScrollbarSize20,\n            rowCount: 5,\n            columnCount: 4,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).toEqual('hidden');\n    });\n\n    it('should set overflowY:hidden if rows and x-axis scrollbar fit within the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            getScrollbarSize: getScrollbarSize20,\n            rowCount: 5,\n            height: 100 + getScrollbarSize20(),\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).toEqual('hidden');\n    });\n\n    it('should leave overflowY:auto if rows require more than the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            getScrollbarSize: getScrollbarSize20,\n            rowCount: 5,\n            height: 100 - 1,\n            columnCount: 4,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).not.toEqual('hidden');\n    });\n\n    it('should leave overflowY:auto if rows and x-axis scrollbar require more than the available width', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            getScrollbarSize: getScrollbarSize20,\n            rowCount: 5,\n            height: 100 + getScrollbarSize20() - 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).not.toEqual('hidden');\n    });\n\n    it('should accept styles that overwrite calculated ones', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 1,\n            getScrollbarSize: getScrollbarSize20,\n            height: 1,\n            rowCount: 1,\n            style: {\n              overflowY: 'visible',\n              overflowX: 'visible',\n            },\n            width: 1,\n          }),\n        ),\n      );\n      expect(rendered.style.overflowY).toEqual('visible');\n      expect(rendered.style.overflowX).toEqual('visible');\n    });\n  });\n\n  /** Tests scrolling via initial props */\n  describe(':scrollToColumn and :scrollToRow', () => {\n    it('should scroll to the left', () => {\n      const grid = render(getMarkup({scrollToColumn: 0}));\n      expect(grid.state.scrollLeft).toEqual(0);\n    });\n\n    it('should scroll over to the middle', () => {\n      const grid = render(getMarkup({scrollToColumn: 24}));\n      // 50 columns * 50 item width = 2,500 total item width\n      // 4 columns can be visible at a time and :scrollLeft is initially 0,\n      // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view).\n      expect(grid.state.scrollLeft).toEqual(1050);\n    });\n\n    it('should scroll to the far right', () => {\n      const grid = render(getMarkup({scrollToColumn: 49}));\n      // 50 columns * 50 item width = 2,500 total item width\n      // Target offset for the last item then is 2,500 - 200\n      expect(grid.state.scrollLeft).toEqual(2300);\n    });\n\n    it('should scroll to the top', () => {\n      const grid = render(getMarkup({scrollToRow: 0}));\n      expect(grid.state.scrollTop).toEqual(0);\n    });\n\n    it('should scroll down to the middle', () => {\n      const grid = render(getMarkup({scrollToRow: 49}));\n      // 100 rows * 20 item height = 2,000 total item height\n      // 5 rows can be visible at a time and :scrollTop is initially 0,\n      // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view).\n      expect(grid.state.scrollTop).toEqual(900);\n    });\n\n    it('should scroll to the bottom', () => {\n      const grid = render(getMarkup({scrollToRow: 99}));\n      // 100 rows * 20 item height = 2,000 total item height\n      // Target offset for the last item then is 2,000 - 100\n      expect(grid.state.scrollTop).toEqual(1900);\n    });\n\n    it('should scroll to a row and column just added', () => {\n      let grid = render(getMarkup());\n      expect(grid.state.scrollLeft).toEqual(0);\n      expect(grid.state.scrollTop).toEqual(0);\n      grid = render(\n        getMarkup({\n          columnCount: NUM_COLUMNS + 1,\n          rowCount: NUM_ROWS + 1,\n          scrollToColumn: NUM_COLUMNS,\n          scrollToRow: NUM_ROWS,\n        }),\n      );\n      expect(grid.state.scrollLeft).toEqual(2350);\n      expect(grid.state.scrollTop).toEqual(1920);\n    });\n\n    it('should scroll back to a newly-added cell without a change in prop', () => {\n      let grid = render(\n        getMarkup({\n          columnCount: NUM_COLUMNS,\n          rowCount: NUM_ROWS,\n          scrollToColumn: NUM_COLUMNS,\n          scrollToRow: NUM_ROWS,\n        }),\n      );\n      grid = render(\n        getMarkup({\n          columnCount: NUM_COLUMNS + 1,\n          rowCount: NUM_ROWS + 1,\n          scrollToColumn: NUM_COLUMNS,\n          scrollToRow: NUM_ROWS,\n        }),\n      );\n      expect(grid.state.scrollLeft).toEqual(2350);\n      expect(grid.state.scrollTop).toEqual(1920);\n    });\n\n    it('should scroll to the correct position for :scrollToAlignment \"start\"', () => {\n      const grid = render(\n        getMarkup({\n          scrollToAlignment: 'start',\n          scrollToColumn: 24,\n          scrollToRow: 49,\n        }),\n      );\n      // 50 columns * 50 item width = 2,500 total item width\n      // 100 rows * 20 item height = 2,000 total item height\n      // 4 columns and 5 rows can be visible at a time.\n      // The minimum amount of scrolling leaves the specified cell in the bottom/right corner (just scrolled into view).\n      // Since alignment is set to \"start\" we should scroll past this point until the cell is aligned top/left.\n      expect(grid.state.scrollLeft).toEqual(1200);\n      expect(grid.state.scrollTop).toEqual(980);\n    });\n\n    it('should scroll to the correct position for :scrollToAlignment \"end\"', () => {\n      render(\n        getMarkup({\n          scrollToColumn: 99,\n          scrollToRow: 99,\n        }),\n      );\n      const grid = render(\n        getMarkup({\n          scrollToAlignment: 'end',\n          scrollToColumn: 24,\n          scrollToRow: 49,\n        }),\n      );\n      // 50 columns * 50 item width = 2,500 total item width\n      // 100 rows * 20 item height = 2,000 total item height\n      // We first scroll past the specified cell and then back.\n      // The minimum amount of scrolling then should leave the specified cell in the top/left corner (just scrolled into view).\n      // Since alignment is set to \"end\" we should scroll past this point until the cell is aligned bottom/right.\n      expect(grid.state.scrollLeft).toEqual(1050);\n      expect(grid.state.scrollTop).toEqual(900);\n    });\n\n    it('should scroll to the correct position for :scrollToAlignment \"center\"', () => {\n      render(\n        getMarkup({\n          scrollToColumn: 99,\n          scrollToRow: 99,\n        }),\n      );\n      const grid = render(\n        getMarkup({\n          scrollToAlignment: 'center',\n          scrollToColumn: 24,\n          scrollToRow: 49,\n        }),\n      );\n      // 50 columns * 50 item width = 2,500 total item width\n      // Viewport width is 200\n      // Column 24 starts at 1,200, center point at 1,225, so...\n      expect(grid.state.scrollLeft).toEqual(1125);\n      // 100 rows * 20 item height = 2,000 total item height\n      // Viewport height is 100\n      // Row 49 starts at 980, center point at 990, so...\n      expect(grid.state.scrollTop).toEqual(940);\n    });\n\n    // Tests issue #691\n    it('should set the correct :scrollLeft after height increases from 0', () => {\n      render.unmount();\n      expect(\n        findDOMNode(\n          render(\n            getMarkup({\n              height: 0,\n              scrollToColumn: 24,\n            }),\n          ),\n        ).scrollLeft || 0,\n      ).toEqual(0);\n      expect(\n        findDOMNode(\n          render(\n            getMarkup({\n              height: 100,\n              scrollToColumn: 24,\n            }),\n          ),\n        ).scrollLeft,\n      ).toEqual(1050);\n    });\n\n    // Tests issue #691\n    it('should set the correct :scrollTop after width increases from 0', () => {\n      render.unmount();\n      expect(\n        findDOMNode(\n          render(\n            getMarkup({\n              scrollToRow: 49,\n              width: 0,\n            }),\n          ),\n        ).scrollTop || 0,\n      ).toEqual(0);\n      expect(\n        findDOMNode(\n          render(\n            getMarkup({\n              scrollToRow: 49,\n              width: 100,\n            }),\n          ),\n        ).scrollTop,\n      ).toEqual(900);\n    });\n\n    // Tests issue #218\n    it('should set the correct :scrollTop after row and column counts increase from 0', () => {\n      const expectedScrollTop =\n        100 * DEFAULT_ROW_HEIGHT - DEFAULT_HEIGHT + DEFAULT_ROW_HEIGHT;\n      render(\n        getMarkup({\n          columnCount: 0,\n          rowCount: 150,\n          scrollToRow: 100,\n        }),\n      );\n      expect(\n        findDOMNode(\n          render(\n            getMarkup({\n              columnCount: 150,\n              rowCount: 150,\n              scrollToRow: 100,\n            }),\n          ),\n        ).scrollTop,\n      ).toEqual(expectedScrollTop);\n    });\n\n    it('should support scrollToCell() public method', () => {\n      const grid = render(getMarkup());\n      expect(grid.state.scrollLeft).toEqual(0);\n      expect(grid.state.scrollTop).toEqual(0);\n      grid.scrollToCell({\n        columnIndex: 24,\n        rowIndex: 49,\n      });\n      // 50 columns * 50 item width = 2,500 total item width\n      // 4 columns can be visible at a time and :scrollLeft is initially 0,\n      // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view).\n      expect(grid.state.scrollLeft).toEqual(1050);\n      // 100 rows * 20 item height = 2,000 total item height\n      // 5 rows can be visible at a time and :scrollTop is initially 0,\n      // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view).\n      expect(grid.state.scrollTop).toEqual(900);\n\n      // Change column without affecting row\n      grid.scrollToCell({\n        columnIndex: 49,\n      });\n      expect(grid.state.scrollLeft).toEqual(2300);\n      expect(grid.state.scrollTop).toEqual(900);\n\n      // Change row without affecting column\n      grid.scrollToCell({\n        rowIndex: 99,\n      });\n      expect(grid.state.scrollLeft).toEqual(2300);\n      expect(grid.state.scrollTop).toEqual(1900);\n    });\n\n    it('should support scrollToPosition() public method', () => {\n      const grid = render(getMarkup());\n      expect(grid.state.scrollLeft).toEqual(0);\n      expect(grid.state.scrollTop).toEqual(0);\n\n      grid.scrollToPosition({\n        scrollLeft: 50,\n        scrollTop: 100,\n      });\n      expect(grid.state.scrollLeft).toEqual(50);\n      expect(grid.state.scrollTop).toEqual(100);\n\n      // Change column without affecting row\n      grid.scrollToPosition({\n        scrollLeft: 25,\n      });\n      expect(grid.state.scrollLeft).toEqual(25);\n      expect(grid.state.scrollTop).toEqual(100);\n\n      // Change row without affecting column\n      grid.scrollToPosition({\n        scrollTop: 50,\n      });\n      expect(grid.state.scrollLeft).toEqual(25);\n      expect(grid.state.scrollTop).toEqual(50);\n    });\n\n    it('should support handleScrollEvent() public method', () => {\n      const grid = render(getMarkup());\n      expect(grid.state.scrollLeft).toEqual(0);\n      expect(grid.state.scrollTop).toEqual(0);\n\n      grid.handleScrollEvent({\n        scrollLeft: 50,\n        scrollTop: 100,\n      });\n      expect(grid.state.isScrolling).toEqual(true);\n      expect(grid.state.scrollLeft).toEqual(50);\n      expect(grid.state.scrollTop).toEqual(100);\n    });\n\n    it('should support getOffsetForCell() public method', () => {\n      const grid = render(getMarkup());\n      const {scrollLeft, scrollTop} = grid.getOffsetForCell({\n        columnIndex: 24,\n        rowIndex: 49,\n      });\n      // 50 columns * 50 item width = 2,500 total item width\n      // 4 columns can be visible at a time and :scrollLeft is initially 0,\n      // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view).\n      expect(scrollLeft).toEqual(1050);\n      // 100 rows * 20 item height = 2,000 total item height\n      // 5 rows can be visible at a time and :scrollTop is initially 0,\n      // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view).\n      expect(scrollTop).toEqual(900);\n    });\n\n    it('should support getTotalRowsHeight() public method', () => {\n      const grid = render(getMarkup());\n      grid.recomputeGridSize();\n      const totalHeight = grid.getTotalRowsHeight();\n      // 100 rows * 20 item height = 2,000 total item height\n      expect(totalHeight).toEqual(2000);\n    });\n\n    it('should support getTotalColumnsWidth() public method', () => {\n      const grid = render(getMarkup());\n      grid.recomputeGridSize();\n      const totalWidth = grid.getTotalColumnsWidth();\n      // 50 columns * 50 item width = 2,500 total item width\n      expect(totalWidth).toEqual(2500);\n    });\n\n    // See issue #565\n    it('should update scroll position to account for changed cell sizes within a function prop wrapper', () => {\n      let rowHeight = 20;\n\n      const props = {\n        height: 100,\n        rowCount: 100,\n        rowHeight: ({index}) => (index === 99 ? rowHeight : 20),\n        scrollToRow: 99,\n      };\n\n      const grid = render(getMarkup(props));\n      const node = findDOMNode(grid);\n\n      expect(node.scrollTop).toBe(1900);\n\n      rowHeight = 40;\n      grid.recomputeGridSize({\n        rowIndex: 99,\n      });\n\n      expect(node.scrollTop).toBe(1920);\n    });\n\n    it('should not restore scrollLeft when scrolling left and recomputeGridSize with columnIndex smaller than scrollToColumn', () => {\n      const props = {\n        columnWidth: 50,\n        columnCount: 100,\n        height: 100,\n        rowCount: 100,\n        rowHeight: 20,\n        scrollToColumn: 50,\n        scrollToRow: 50,\n        width: 100,\n      };\n      const grid = render(getMarkup(props));\n\n      expect(grid.state.scrollLeft).toEqual(2450);\n\n      simulateScroll({grid, scrollLeft: 2250});\n      expect(grid.state.scrollLeft).toEqual(2250);\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n\n      grid.recomputeGridSize({columnIndex: 30});\n      expect(grid.state.scrollLeft).toEqual(2250);\n    });\n\n    it('should not restore scrollTop when scrolling up and recomputeGridSize with rowIndex smaller than scrollToRow', () => {\n      const props = {\n        columnWidth: 50,\n        columnCount: 100,\n        height: 100,\n        rowCount: 100,\n        rowHeight: 20,\n        scrollToColumn: 50,\n        scrollToRow: 50,\n        width: 100,\n      };\n      const grid = render(getMarkup(props));\n\n      expect(grid.state.scrollTop).toEqual(920);\n\n      simulateScroll({grid, scrollTop: 720});\n      expect(grid.state.scrollTop).toEqual(720);\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n\n      grid.recomputeGridSize({rowIndex: 20});\n      expect(grid.state.scrollTop).toEqual(720);\n    });\n\n    it('should restore scroll offset for column when row count increases from 0 (and vice versa)', () => {\n      const props = {\n        columnWidth: 50,\n        columnCount: 100,\n        height: 100,\n        rowCount: 100,\n        rowHeight: 20,\n        scrollToColumn: 50,\n        scrollToRow: 50,\n        width: 100,\n      };\n      const grid = render(getMarkup(props));\n      expect(grid.state.scrollLeft).toEqual(2450);\n      expect(grid.state.scrollTop).toEqual(920);\n      render(\n        getMarkup({\n          ...props,\n          columnCount: 0,\n        }),\n      );\n      expect(grid.state.scrollLeft).toEqual(0);\n      expect(grid.state.scrollTop).toEqual(0);\n      render(getMarkup(props));\n      expect(grid.state.scrollLeft).toEqual(2450);\n      expect(grid.state.scrollTop).toEqual(920);\n      render(\n        getMarkup({\n          ...props,\n          rowCount: 0,\n        }),\n      );\n      expect(grid.state.scrollLeft).toEqual(0);\n      expect(grid.state.scrollTop).toEqual(0);\n      render(getMarkup(props));\n      expect(grid.state.scrollLeft).toEqual(2450);\n      expect(grid.state.scrollTop).toEqual(920);\n    });\n\n    it('should take scrollbar size into account when aligning cells', () => {\n      const grid = render(\n        getMarkup({\n          columnWidth: 50,\n          columnCount: 100,\n          getScrollbarSize: getScrollbarSize20,\n          height: 100,\n          rowCount: 100,\n          rowHeight: 20,\n          scrollToColumn: 50,\n          scrollToRow: 50,\n          width: 100,\n        }),\n      );\n\n      expect(grid.state.scrollLeft).toEqual(2450 + getScrollbarSize20());\n      expect(grid.state.scrollTop).toEqual(920 + getScrollbarSize20());\n    });\n  });\n\n  describe('property updates', () => {\n    it('should update :scrollToColumn position when :columnWidth changes', () => {\n      let grid = findDOMNode(render(getMarkup({scrollToColumn: 25})));\n      expect(grid.textContent).toContain('column:25');\n      // Making columns taller pushes name off/beyond the scrolled area\n      grid = findDOMNode(\n        render(getMarkup({scrollToColumn: 25, columnWidth: 20})),\n      );\n      expect(grid.textContent).toContain('column:25');\n    });\n\n    it('should update :scrollToRow position when :rowHeight changes', () => {\n      let grid = findDOMNode(render(getMarkup({scrollToRow: 50})));\n      expect(grid.textContent).toContain('row:50');\n      // Making rows taller pushes name off/beyond the scrolled area\n      grid = findDOMNode(render(getMarkup({scrollToRow: 50, rowHeight: 20})));\n      expect(grid.textContent).toContain('row:50');\n    });\n\n    it('should update :scrollToColumn position when :width changes', () => {\n      let grid = findDOMNode(render(getMarkup({scrollToColumn: 25})));\n      expect(grid.textContent).toContain('column:25');\n      // Making the grid narrower leaves only room for 1 item\n      grid = findDOMNode(render(getMarkup({scrollToColumn: 25, width: 50})));\n      expect(grid.textContent).toContain('column:25');\n    });\n\n    it('should update :scrollToRow position when :height changes', () => {\n      let grid = findDOMNode(render(getMarkup({scrollToRow: 50})));\n      expect(grid.textContent).toContain('row:50');\n      // Making the grid shorter leaves only room for 1 item\n      grid = findDOMNode(render(getMarkup({scrollToRow: 50, height: 20})));\n      expect(grid.textContent).toContain('row:50');\n    });\n\n    it('should update :scrollToColumn position when :scrollToColumn changes', () => {\n      let grid = findDOMNode(render(getMarkup()));\n      expect(grid.textContent).not.toContain('column:25');\n      grid = findDOMNode(render(getMarkup({scrollToColumn: 25})));\n      expect(grid.textContent).toContain('column:25');\n    });\n\n    it('should update :scrollToRow position when :scrollToRow changes', () => {\n      let grid = findDOMNode(render(getMarkup()));\n      expect(grid.textContent).not.toContain('row:50');\n      grid = findDOMNode(render(getMarkup({scrollToRow: 50})));\n      expect(grid.textContent).toContain('row:50');\n    });\n\n    it('should update scroll position if size shrinks smaller than the current scroll', () => {\n      let grid = findDOMNode(render(getMarkup({scrollToColumn: 250})));\n      grid = findDOMNode(render(getMarkup()));\n      grid = findDOMNode(\n        render(getMarkup({scrollToColumn: 250, columnCount: 10})),\n      );\n      expect(grid.textContent).toContain('column:9');\n    });\n\n    it('should update scroll position if size shrinks smaller than the current scroll', () => {\n      let grid = findDOMNode(render(getMarkup({scrollToRow: 500})));\n      grid = findDOMNode(render(getMarkup()));\n      grid = findDOMNode(render(getMarkup({scrollToRow: 500, rowCount: 10})));\n      expect(grid.textContent).toContain('row:9');\n    });\n  });\n\n  describe('noContentRenderer', () => {\n    it('should call :noContentRenderer if :columnCount is 0', () => {\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            noContentRenderer: () => <div>No data</div>,\n            columnCount: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('No data');\n    });\n\n    it('should call :noContentRenderer if :rowCount is 0', () => {\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            noContentRenderer: () => <div>No data</div>,\n            rowCount: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('No data');\n    });\n\n    // Sanity check for bvaughn/react-virtualized/pull/348\n    it('should render an empty body if :rowCount or :columnCount changes to 0', () => {\n      function noContentRenderer() {\n        return <div>No data</div>;\n      }\n\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            noContentRenderer,\n          }),\n        ),\n      );\n      expect(list.textContent).not.toEqual('No data');\n\n      list = findDOMNode(\n        render(\n          getMarkup({\n            noContentRenderer,\n            rowCount: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('No data');\n\n      list = findDOMNode(\n        render(\n          getMarkup({\n            noContentRenderer,\n          }),\n        ),\n      );\n      expect(list.textContent).not.toEqual('No data');\n\n      list = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 0,\n            noContentRenderer,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('No data');\n    });\n\n    it('should render an empty body if :columnCount is 0 and there is no :noContentRenderer', () => {\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            columnCount: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('');\n    });\n\n    it('should render an empty body if :rowCount is 0 and there is no :noContentRenderer', () => {\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            rowCount: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('');\n    });\n\n    it('should render an empty body there is a :noContentRenderer but :height or :width are 0', () => {\n      let list = findDOMNode(\n        render(\n          getMarkup({\n            height: 0,\n            noContentRenderer: () => <div>No data</div>,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('');\n      list = findDOMNode(\n        render(\n          getMarkup({\n            noContentRenderer: () => <div>No data</div>,\n            width: 0,\n          }),\n        ),\n      );\n      expect(list.textContent).toEqual('');\n    });\n  });\n\n  describe('onSectionRendered', () => {\n    it('should call :onSectionRendered if at least one cell is rendered', () => {\n      let columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex;\n      render(\n        getMarkup({\n          onSectionRendered: params =>\n            ({\n              columnStartIndex,\n              columnStopIndex,\n              rowStartIndex,\n              rowStopIndex,\n            } = params),\n        }),\n      );\n      expect(columnStartIndex).toEqual(0);\n      expect(columnStopIndex).toEqual(3);\n      expect(rowStartIndex).toEqual(0);\n      expect(rowStopIndex).toEqual(4);\n    });\n\n    it('should not call :onSectionRendered unless the column or row start or stop indices have changed', () => {\n      let numCalls = 0;\n      let columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex;\n      const onSectionRendered = params => {\n        columnStartIndex = params.columnStartIndex;\n        columnStopIndex = params.columnStopIndex;\n        rowStartIndex = params.rowStartIndex;\n        rowStopIndex = params.rowStopIndex;\n        numCalls++;\n      };\n      render(getMarkup({onSectionRendered}));\n      expect(numCalls).toEqual(1);\n      expect(columnStartIndex).toEqual(0);\n      expect(columnStopIndex).toEqual(3);\n      expect(rowStartIndex).toEqual(0);\n      expect(rowStopIndex).toEqual(4);\n      render(getMarkup({onSectionRendered}));\n      expect(numCalls).toEqual(1);\n      expect(columnStartIndex).toEqual(0);\n      expect(columnStopIndex).toEqual(3);\n      expect(rowStartIndex).toEqual(0);\n      expect(rowStopIndex).toEqual(4);\n    });\n\n    it('should call :onSectionRendered if the row or column start or stop indices have changed', () => {\n      let numCalls = 0;\n      let columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex;\n      const onSectionRendered = params => {\n        columnStartIndex = params.columnStartIndex;\n        columnStopIndex = params.columnStopIndex;\n        rowStartIndex = params.rowStartIndex;\n        rowStopIndex = params.rowStopIndex;\n        numCalls++;\n      };\n      render(getMarkup({onSectionRendered}));\n      expect(columnStartIndex).toEqual(0);\n      expect(columnStopIndex).toEqual(3);\n      expect(rowStartIndex).toEqual(0);\n      expect(rowStopIndex).toEqual(4);\n      render(\n        getMarkup({\n          height: 50,\n          onSectionRendered,\n        }),\n      );\n      expect(numCalls).toEqual(2);\n      expect(columnStartIndex).toEqual(0);\n      expect(columnStopIndex).toEqual(3);\n      expect(rowStartIndex).toEqual(0);\n      expect(rowStopIndex).toEqual(2);\n      render(\n        getMarkup({\n          height: 50,\n          onSectionRendered,\n          width: 100,\n        }),\n      );\n      expect(numCalls).toEqual(3);\n      expect(columnStartIndex).toEqual(0);\n      expect(columnStopIndex).toEqual(1);\n      expect(rowStartIndex).toEqual(0);\n      expect(rowStopIndex).toEqual(2);\n    });\n\n    it('should not call :onSectionRendered if no cells are rendered', () => {\n      let numCalls = 0;\n      render(\n        getMarkup({\n          height: 0,\n          onSectionRendered: () => numCalls++,\n        }),\n      );\n      expect(numCalls).toEqual(0);\n    });\n  });\n\n  describe(':scrollLeft and :scrollTop properties', () => {\n    it('should render correctly when an initial :scrollLeft and :scrollTop properties are specified', () => {\n      let columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex;\n      findDOMNode(\n        render(\n          getMarkup({\n            onSectionRendered: params =>\n              ({\n                columnStartIndex,\n                columnStopIndex,\n                rowStartIndex,\n                rowStopIndex,\n              } = params),\n            scrollLeft: 250,\n            scrollTop: 100,\n          }),\n        ),\n      );\n      expect(rowStartIndex).toEqual(5);\n      expect(rowStopIndex).toEqual(9);\n      expect(columnStartIndex).toEqual(5);\n      expect(columnStopIndex).toEqual(8);\n    });\n\n    it('should render correctly when :scrollLeft and :scrollTop properties are updated', () => {\n      let columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex;\n\n      render(\n        getMarkup({\n          onSectionRendered: params =>\n            ({\n              columnStartIndex,\n              columnStopIndex,\n              rowStartIndex,\n              rowStopIndex,\n            } = params),\n        }),\n      );\n      expect(rowStartIndex).toEqual(0);\n      expect(rowStopIndex).toEqual(4);\n      expect(columnStartIndex).toEqual(0);\n      expect(columnStopIndex).toEqual(3);\n\n      render(\n        getMarkup({\n          onSectionRendered: params =>\n            ({\n              columnStartIndex,\n              columnStopIndex,\n              rowStartIndex,\n              rowStopIndex,\n            } = params),\n          scrollLeft: 250,\n          scrollTop: 100,\n        }),\n      );\n      expect(rowStartIndex).toEqual(5);\n      expect(rowStopIndex).toEqual(9);\n      expect(columnStartIndex).toEqual(5);\n      expect(columnStopIndex).toEqual(8);\n    });\n  });\n\n  describe('styles, classNames, ids, and roles', () => {\n    it('should use the expected global CSS classNames', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.className).toEqual('ReactVirtualized__Grid');\n    });\n\n    it('should use a custom :className if specified', () => {\n      const rendered = findDOMNode(render(getMarkup({className: 'foo'})));\n      expect(rendered.className).toContain('foo');\n    });\n\n    it('should use a custom :id if specified', () => {\n      const rendered = findDOMNode(render(getMarkup({id: 'bar'})));\n      expect(rendered.getAttribute('id')).toEqual('bar');\n    });\n\n    it('should use a custom :style if specified', () => {\n      const style = {backgroundColor: 'red'};\n      const rendered = findDOMNode(render(getMarkup({style})));\n      expect(rendered.style.backgroundColor).toEqual('red');\n    });\n\n    it('should use a custom :containerStyle if specified', () => {\n      const containerStyle = {backgroundColor: 'red'};\n      const rendered = findDOMNode(render(getMarkup({containerStyle})));\n      expect(\n        rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer')\n          .style.backgroundColor,\n      ).toEqual('red');\n    });\n\n    it('should have the gridcell role', () => {\n      const containerStyle = {backgroundColor: 'red'};\n      const rendered = findDOMNode(render(getMarkup({containerStyle})));\n      expect(rendered.querySelectorAll('[role=\"gridcell\"]').length).toEqual(20);\n    });\n  });\n\n  describe('onScroll', () => {\n    it('should trigger callback when component is mounted', () => {\n      const onScrollCalls = [];\n      render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n          scrollLeft: 50,\n          scrollTop: 100,\n        }),\n      );\n      expect(onScrollCalls).toEqual([\n        {\n          clientHeight: 100,\n          clientWidth: 200,\n          scrollHeight: 2000,\n          scrollLeft: 50,\n          scrollTop: 100,\n          scrollWidth: 2500,\n        },\n      ]);\n    });\n\n    it('should trigger callback when component scrolls horizontally', () => {\n      const onScrollCalls = [];\n      const grid = render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      simulateScroll({\n        grid,\n        scrollLeft: 100,\n        scrollTop: 0,\n      });\n      expect(onScrollCalls.length).toEqual(2);\n      expect(onScrollCalls[1]).toEqual({\n        clientHeight: 100,\n        clientWidth: 200,\n        scrollHeight: 2000,\n        scrollLeft: 100,\n        scrollTop: 0,\n        scrollWidth: 2500,\n      });\n    });\n\n    it('should trigger callback when component scrolls vertically', () => {\n      const onScrollCalls = [];\n      const grid = render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      simulateScroll({\n        grid,\n        scrollLeft: 0,\n        scrollTop: 100,\n      });\n      expect(onScrollCalls.length).toEqual(2);\n      expect(onScrollCalls[1]).toEqual({\n        clientHeight: 100,\n        clientWidth: 200,\n        scrollHeight: 2000,\n        scrollLeft: 0,\n        scrollTop: 100,\n        scrollWidth: 2500,\n      });\n    });\n\n    it('should trigger callback with scrollLeft of 0 when total columns width is less than width', () => {\n      const onScrollCalls = [];\n      const grid = render(\n        getMarkup({\n          columnCount: 1,\n          columnWidth: 50,\n          onScroll: params => onScrollCalls.push(params),\n          scrollLeft: 0,\n          scrollTop: 10,\n          width: 200,\n        }),\n      );\n      simulateScroll({\n        grid,\n        scrollLeft: 0,\n        scrollTop: 0,\n      });\n      expect(onScrollCalls.length).toEqual(2);\n      expect(onScrollCalls[1]).toEqual({\n        clientHeight: 100,\n        clientWidth: 200,\n        scrollHeight: 2000,\n        scrollLeft: 0,\n        scrollTop: 0,\n        scrollWidth: 50,\n      });\n    });\n\n    it('should trigger callback with scrollTop of 0 when total rows height is less than height', () => {\n      const onScrollCalls = [];\n      const grid = render(\n        getMarkup({\n          rowCount: 1,\n          rowHeight: 50,\n          onScroll: params => onScrollCalls.push(params),\n          scrollLeft: 0,\n          scrollTop: 10,\n          height: 200,\n        }),\n      );\n      simulateScroll({\n        grid,\n        scrollLeft: 0,\n        scrollTop: 0,\n      });\n      expect(onScrollCalls.length).toEqual(2);\n      expect(onScrollCalls[1]).toEqual({\n        clientHeight: 200,\n        clientWidth: 200,\n        scrollHeight: 50,\n        scrollLeft: 0,\n        scrollTop: 0,\n        scrollWidth: 2500,\n      });\n    });\n\n    // Support use-cases like WindowScroller; enable them to stay in sync with scroll-to-cell changes.\n    it('should trigger when :scrollToColumn or :scrollToRow are changed via props', () => {\n      const onScrollCalls = [];\n      render(getMarkup());\n      render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n          scrollToColumn: 24,\n          scrollToRow: 49,\n        }),\n      );\n      expect(onScrollCalls).toEqual([\n        {\n          clientHeight: 100,\n          clientWidth: 200,\n          scrollHeight: 2000,\n          scrollLeft: 1050,\n          scrollTop: 900,\n          scrollWidth: 2500,\n        },\n      ]);\n    });\n  });\n\n  describe('overscanColumnCount & overscanRowCount', () => {\n    function createHelper() {\n      let columnOverscanStartIndex,\n        columnOverscanStopIndex,\n        columnStartIndex,\n        columnStopIndex,\n        rowOverscanStartIndex,\n        rowOverscanStopIndex,\n        rowStartIndex,\n        rowStopIndex;\n\n      function onSectionRendered(params) {\n        columnOverscanStartIndex = params.columnOverscanStartIndex;\n        columnOverscanStopIndex = params.columnOverscanStopIndex;\n        columnStartIndex = params.columnStartIndex;\n        columnStopIndex = params.columnStopIndex;\n        rowOverscanStartIndex = params.rowOverscanStartIndex;\n        rowOverscanStopIndex = params.rowOverscanStopIndex;\n        rowStartIndex = params.rowStartIndex;\n        rowStopIndex = params.rowStopIndex;\n      }\n\n      return {\n        columnOverscanStartIndex: () => columnOverscanStartIndex,\n        columnOverscanStopIndex: () => columnOverscanStopIndex,\n        columnStartIndex: () => columnStartIndex,\n        columnStopIndex: () => columnStopIndex,\n        onSectionRendered,\n        rowOverscanStartIndex: () => rowOverscanStartIndex,\n        rowOverscanStopIndex: () => rowOverscanStopIndex,\n        rowStartIndex: () => rowStartIndex,\n        rowStopIndex: () => rowStopIndex,\n      };\n    }\n\n    it('should not overscan if disabled', () => {\n      const helper = createHelper();\n      render(\n        getMarkup({\n          onSectionRendered: helper.onSectionRendered,\n        }),\n      );\n      expect(helper.columnOverscanStartIndex()).toEqual(\n        helper.columnStartIndex(),\n      );\n      expect(helper.columnOverscanStopIndex()).toEqual(\n        helper.columnStopIndex(),\n      );\n      expect(helper.rowOverscanStartIndex()).toEqual(helper.rowStartIndex());\n      expect(helper.rowOverscanStopIndex()).toEqual(helper.rowStopIndex());\n    });\n\n    it('should overscan the specified amount', () => {\n      const helper = createHelper();\n      render(\n        getMarkup({\n          onSectionRendered: helper.onSectionRendered,\n          overscanColumnCount: 2,\n          overscanRowCount: 5,\n          scrollToColumn: 25,\n          scrollToRow: 50,\n        }),\n      );\n      expect(helper.columnOverscanStartIndex()).toEqual(22);\n      expect(helper.columnOverscanStopIndex()).toEqual(27);\n      expect(helper.columnStartIndex()).toEqual(22);\n      expect(helper.columnStopIndex()).toEqual(25);\n      expect(helper.rowOverscanStartIndex()).toEqual(46);\n      expect(helper.rowOverscanStopIndex()).toEqual(55);\n      expect(helper.rowStartIndex()).toEqual(46);\n      expect(helper.rowStopIndex()).toEqual(50);\n    });\n\n    it('should not overscan beyond the bounds of the grid', () => {\n      const helper = createHelper();\n      render(\n        getMarkup({\n          onSectionRendered: helper.onSectionRendered,\n          columnCount: 6,\n          overscanColumnCount: 10,\n          overscanRowCount: 10,\n          rowCount: 5,\n        }),\n      );\n      expect(helper.columnOverscanStartIndex()).toEqual(0);\n      expect(helper.columnOverscanStopIndex()).toEqual(5);\n      expect(helper.columnStartIndex()).toEqual(0);\n      expect(helper.columnStopIndex()).toEqual(3);\n      expect(helper.rowOverscanStartIndex()).toEqual(0);\n      expect(helper.rowOverscanStopIndex()).toEqual(4);\n      expect(helper.rowStartIndex()).toEqual(0);\n      expect(helper.rowStopIndex()).toEqual(4);\n    });\n\n    it('should set the correct scroll direction', () => {\n      // Do not pass in the initial state as props, otherwise the internal state is forbidden from updating itself\n      const grid = render(getMarkup());\n\n      // Simulate a scroll to set the initial internal state\n      simulateScroll({\n        grid,\n        scrollLeft: 50,\n        scrollTop: 50,\n      });\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n\n      simulateScroll({\n        grid,\n        scrollLeft: 0,\n        scrollTop: 0,\n      });\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n\n      simulateScroll({\n        grid,\n        scrollLeft: 100,\n        scrollTop: 100,\n      });\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n    });\n\n    it('should set the correct scroll direction when scroll position is updated from props', () => {\n      let grid = render(\n        getMarkup({\n          scrollLeft: 50,\n          scrollTop: 50,\n        }),\n      );\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n\n      grid = render(\n        getMarkup({\n          scrollLeft: 0,\n          scrollTop: 0,\n        }),\n      );\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n\n      grid = render(\n        getMarkup({\n          scrollLeft: 100,\n          scrollTop: 100,\n        }),\n      );\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n    });\n\n    it('should not reset scroll direction for one axis when scrolled in another', () => {\n      // Do not pass in the initial state as props, otherwise the internal state is forbidden from updating itself\n      const grid = render(getMarkup());\n\n      // Simulate a scroll to set the initial internal state\n      simulateScroll({\n        grid,\n        scrollLeft: 0,\n        scrollTop: 5,\n      });\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n\n      simulateScroll({\n        grid,\n        scrollLeft: 5,\n        scrollTop: 5,\n      });\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n\n      simulateScroll({\n        grid,\n        scrollLeft: 5,\n        scrollTop: 0,\n      });\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_FORWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n\n      simulateScroll({\n        grid,\n        scrollLeft: 0,\n        scrollTop: 0,\n      });\n\n      expect(grid.state.scrollDirectionHorizontal).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n      expect(grid.state.scrollDirectionVertical).toEqual(\n        SCROLL_DIRECTION_BACKWARD,\n      );\n    });\n\n    it('should overscan in the direction being scrolled', async done => {\n      const helper = createHelper();\n\n      let onSectionRenderedResolve;\n\n      function onSectionRendered(params) {\n        helper.onSectionRendered(params);\n\n        if (onSectionRenderedResolve) {\n          onSectionRenderedResolve();\n        }\n      }\n\n      const grid = render(\n        getMarkup({\n          onSectionRendered,\n          overscanColumnCount: 2,\n          overscanRowCount: 5,\n        }),\n      );\n\n      // Wait until the onSectionRendered handler / debouncer has processed\n      let onSectionRenderedPromise = new Promise(resolve => {\n        onSectionRenderedResolve = resolve;\n      });\n\n      simulateScroll({\n        grid,\n        scrollLeft: 200,\n        scrollTop: 200,\n      });\n\n      await onSectionRenderedPromise;\n\n      // It should overscan in the direction being scrolled while scroll is in progress\n      expect(helper.columnOverscanStartIndex()).toEqual(4);\n      expect(helper.columnOverscanStopIndex()).toEqual(9);\n      expect(helper.columnStartIndex()).toEqual(4);\n      expect(helper.columnStopIndex()).toEqual(7);\n      expect(helper.rowOverscanStartIndex()).toEqual(10);\n      expect(helper.rowOverscanStopIndex()).toEqual(19);\n      expect(helper.rowStartIndex()).toEqual(10);\n      expect(helper.rowStopIndex()).toEqual(14);\n\n      // Wait until the onSectionRendered handler / debouncer has processed\n      onSectionRenderedPromise = new Promise(resolve => {\n        onSectionRenderedResolve = resolve;\n      });\n\n      simulateScroll({\n        grid,\n        scrollLeft: 100,\n        scrollTop: 100,\n      });\n\n      await onSectionRenderedPromise;\n\n      // It reset overscan once scrolling has finished\n      expect(helper.columnOverscanStartIndex()).toEqual(0);\n      expect(helper.columnOverscanStopIndex()).toEqual(5);\n      expect(helper.columnStartIndex()).toEqual(2);\n      expect(helper.columnStopIndex()).toEqual(5);\n      expect(helper.rowOverscanStartIndex()).toEqual(0);\n      expect(helper.rowOverscanStopIndex()).toEqual(9);\n      expect(helper.rowStartIndex()).toEqual(5);\n      expect(helper.rowStopIndex()).toEqual(9);\n\n      done();\n    });\n  });\n\n  describe('cellRangeRenderer', () => {\n    it('should use a custom :cellRangeRenderer if specified', () => {\n      let cellRangeRendererCalled = 0;\n      let cellRangeRendererParams;\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellRangeRenderer: params => {\n              cellRangeRendererParams = params;\n              cellRangeRendererCalled++;\n\n              return [<div key=\"0\">Fake content</div>];\n            },\n          }),\n        ),\n      );\n      expect(cellRangeRendererCalled).toEqual(1);\n      expect(cellRangeRendererParams.columnStartIndex).toEqual(0);\n      expect(cellRangeRendererParams.columnStopIndex).toEqual(3);\n      expect(cellRangeRendererParams.rowStartIndex).toEqual(0);\n      expect(cellRangeRendererParams.rowStopIndex).toEqual(4);\n      expect(rendered.textContent).toContain('Fake content');\n    });\n  });\n\n  describe('estimated row and column sizes', () => {\n    it('should not estimate sizes if actual sizes are numbers', () => {\n      const grid = render(\n        getMarkup({\n          columnWidth: 100,\n          estimatedColumnSize: 150,\n          estimatedRowSize: 15,\n          rowHeight: 20,\n        }),\n      );\n      expect(Grid._getEstimatedColumnSize(grid.props)).toEqual(100);\n      expect(Grid._getEstimatedRowSize(grid.props)).toEqual(20);\n    });\n\n    it('should estimate row and column sizes if actual sizes are functions', () => {\n      const grid = render(\n        getMarkup({\n          columnWidth: () => 100,\n          estimatedColumnSize: 150,\n          estimatedRowSize: 15,\n          rowHeight: () => 20,\n        }),\n      );\n      expect(Grid._getEstimatedColumnSize(grid.props)).toEqual(150);\n      expect(Grid._getEstimatedRowSize(grid.props)).toEqual(15);\n    });\n  });\n\n  it('should pass the cellRenderer an :isScrolling flag when scrolling is in progress', async done => {\n    const cellRendererCalls = [];\n    function cellRenderer({columnIndex, isScrolling, key, rowIndex, style}) {\n      cellRendererCalls.push(isScrolling);\n      return defaultCellRenderer({columnIndex, key, rowIndex, style});\n    }\n    const grid = render(\n      getMarkup({\n        cellRenderer,\n      }),\n    );\n    expect(cellRendererCalls[0]).toEqual(false);\n    cellRendererCalls.splice(0);\n\n    // Give React time to process the queued setState()\n    await new Promise(resolve => setTimeout(resolve, 1));\n\n    simulateScroll({grid, scrollTop: 100});\n    expect(cellRendererCalls[0]).toEqual(true);\n\n    done();\n  });\n\n  it('should pass the cellRenderer an :isScrolling flag based on props override', () => {\n    const cellRenderer = jest.fn();\n    cellRenderer.mockImplementation(({key, style}) => (\n      <div key={key} style={style} />\n    ));\n    render(\n      getMarkup({\n        cellRenderer,\n        isScrolling: true,\n      }),\n    );\n    expect(cellRenderer).toHaveBeenCalled();\n    expect(cellRenderer.mock.calls[0][0].isScrolling).toBe(true);\n    cellRenderer.mockReset();\n    render(\n      getMarkup({\n        cellRenderer,\n        isScrolling: false,\n        width: DEFAULT_WIDTH + 1,\n      }),\n    );\n    expect(cellRenderer).toHaveBeenCalled();\n    expect(cellRenderer.mock.calls[0][0].isScrolling).toBe(false);\n  });\n\n  it('should pass the cellRenderer an :isVisible flag', () => {\n    const cellRendererCalls = [];\n    function cellRenderer(props) {\n      cellRendererCalls.push(props);\n      return defaultCellRenderer(props);\n    }\n    render(\n      getMarkup({\n        cellRenderer,\n        height: DEFAULT_ROW_HEIGHT,\n        overscanColumnCount: 1,\n        overscanRowCount: 1,\n        width: DEFAULT_COLUMN_WIDTH,\n      }),\n    );\n    cellRendererCalls.forEach(props => {\n      expect(props.isVisible).toEqual(\n        props.columnIndex === 0 && props.rowIndex === 0,\n      ); // Only the first cell is visible\n    });\n  });\n\n  describe('cell caching', () => {\n    it('should not cache cells if the Grid is not scrolling', () => {\n      const cellRendererCalls = [];\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        cellRendererCalls.push({columnIndex, rowIndex});\n        return defaultCellRenderer({columnIndex, key, rowIndex, style});\n      }\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 40,\n        rowHeight: 20,\n        scrollToRow: 0,\n        width: 100,\n      };\n\n      render(\n        getMarkup({\n          ...props,\n          scrollToRow: 0,\n        }),\n      );\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n\n      cellRendererCalls.splice(0);\n\n      render(\n        getMarkup({\n          ...props,\n          scrollToRow: 1,\n        }),\n      );\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n    });\n\n    it('should not cache cells if the offsets are not adjusted', () => {\n      const cellRendererCalls = [];\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        cellRendererCalls.push({columnIndex, rowIndex});\n        return defaultCellRenderer({columnIndex, key, rowIndex, style});\n      }\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 40,\n        rowHeight: 20,\n        rowCount: 100000,\n        scrollToRow: 0,\n        width: 100,\n      };\n\n      render(\n        getMarkup({\n          ...props,\n          scrollToRow: 0,\n        }),\n      );\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n\n      cellRendererCalls.splice(0);\n\n      render(\n        getMarkup({\n          ...props,\n          scrollToRow: 1,\n        }),\n      );\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n    });\n\n    it('should cache a cell once it has been rendered while scrolling', () => {\n      const cellRendererCalls = [];\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        cellRendererCalls.push({columnIndex, rowIndex});\n        return defaultCellRenderer({columnIndex, key, rowIndex, style});\n      }\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 40,\n        rowHeight: 20,\n        width: 100,\n      };\n\n      const grid = render(\n        getMarkup({\n          ...props,\n          scrollToRow: 0,\n        }),\n      );\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n\n      simulateScroll({grid, scrollTop: 1});\n\n      cellRendererCalls.splice(0);\n\n      // Rows 0-2 have already rendered but row 3 is not yet visible\n      // This means that only row 3 should be newly-created\n      // The others should come from the cache\n      render(\n        getMarkup({\n          ...props,\n          scrollToRow: 3,\n        }),\n      );\n      expect(cellRendererCalls).toEqual([{columnIndex: 0, rowIndex: 3}]);\n    });\n\n    it('should clear cache once :isScrolling is false', async done => {\n      const cellRendererCalls = [];\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        cellRendererCalls.push({columnIndex, rowIndex});\n        return defaultCellRenderer({columnIndex, key, rowIndex, style});\n      }\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 40,\n        rowHeight: 20,\n        scrollToRow: 0,\n        width: 100,\n      };\n\n      const grid = render(getMarkup(props));\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n\n      simulateScroll({grid, scrollTop: 1});\n\n      // Allow scrolling timeout to complete so that cell cache is reset\n      await new Promise(resolve =>\n        setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2),\n      );\n\n      cellRendererCalls.splice(0);\n\n      render(\n        getMarkup({\n          ...props,\n          scrollToRow: 1,\n        }),\n      );\n      expect(cellRendererCalls.length).not.toEqual(0);\n\n      done();\n    });\n\n    it('should clear cache once :isScrolling via props is false', async () => {\n      const cellRenderer = jest.fn();\n      cellRenderer.mockImplementation(params => (\n        <div key={params.key} style={params.style} />\n      ));\n\n      const props = {\n        autoHeight: true,\n        cellRenderer,\n        columnCount: 1,\n        isScrolling: true,\n        rowCount: 1,\n      };\n\n      render(getMarkup(props));\n      render(getMarkup(props));\n      expect(cellRenderer).toHaveBeenCalledTimes(1); // Due to cell cache\n\n      const scrollingStyle = cellRenderer.mock.calls[0][0].style;\n\n      cellRenderer.mockReset();\n\n      render(\n        getMarkup({\n          ...props,\n          isScrolling: false,\n        }),\n      );\n\n      expect(cellRenderer.mock.calls[0][0].style).toBe(scrollingStyle);\n      expect(cellRenderer).toHaveBeenCalledTimes(1); // Reset cache\n\n      cellRenderer.mockReset();\n\n      render(\n        getMarkup({\n          ...props,\n          isScrolling: true,\n        }),\n      );\n\n      expect(cellRenderer.mock.calls[0][0].style).not.toBe(scrollingStyle);\n      expect(cellRenderer).toHaveBeenCalledTimes(1); // Only cached when scrolling\n    });\n\n    it('should clear cache if :recomputeGridSize is called', () => {\n      const cellRendererCalls = [];\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        cellRendererCalls.push({columnIndex, rowIndex});\n        return defaultCellRenderer({columnIndex, key, rowIndex, style});\n      }\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 40,\n        rowHeight: 20,\n        scrollTop: 0,\n        width: 100,\n      };\n\n      const grid = render(getMarkup(props));\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n\n      simulateScroll({grid, scrollTop: 1});\n\n      cellRendererCalls.splice(0);\n\n      grid.recomputeGridSize();\n\n      expect(cellRendererCalls.length).not.toEqual(0);\n    });\n\n    it('should not clear cache if :isScrollingOptOut is true', () => {\n      const cellRendererCalls = [];\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        cellRendererCalls.push({columnIndex, rowIndex});\n        return defaultCellRenderer({columnIndex, key, rowIndex, style});\n      }\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 40,\n        rowHeight: 20,\n        scrollTop: 0,\n        width: 100,\n        isScrollingOptOut: true,\n      };\n\n      render(getMarkup(props));\n      render(getMarkup(props));\n      expect(cellRendererCalls).toEqual([\n        {columnIndex: 0, rowIndex: 0},\n        {columnIndex: 0, rowIndex: 1},\n      ]);\n\n      cellRendererCalls.splice(0);\n\n      render(\n        getMarkup({\n          ...props,\n          isScrolling: false,\n        }),\n      );\n\n      // Visible cells are cached\n      expect(cellRendererCalls.length).toEqual(0);\n\n      render(\n        getMarkup({\n          ...props,\n          isScrolling: true,\n        }),\n      );\n\n      // Only cleared non-visible cells\n      expect(cellRendererCalls.length).toEqual(0);\n    });\n\n    it('should not trigger render by _debounceScrollEndedCallback if process slow table', async () => {\n      const scrollingResetTimeInterval = 50;\n      let cellRangeRendererCalls = 0;\n      function cellRangeRenderer(props) {\n        const startTime = Date.now();\n        while (Date.now() - startTime <= scrollingResetTimeInterval); // imitate very slow render\n        cellRangeRendererCalls++;\n        return defaultCellRangeRenderer(props);\n      }\n      const props = {\n        scrollingResetTimeInterval,\n        cellRangeRenderer,\n      };\n\n      const grid = render(getMarkup(props));\n      render(getMarkup(props));\n      expect(cellRangeRendererCalls).toEqual(1);\n\n      for (let i = 1; i <= 5; i++) {\n        cellRangeRendererCalls = 0;\n        simulateScroll({grid, scrollTop: i});\n        // small wait for maybe early _debounceScrollEndedCallback\n        await new Promise(resolve =>\n          setTimeout(resolve, scrollingResetTimeInterval / 2),\n        );\n        expect(cellRangeRendererCalls).toEqual(1);\n      }\n\n      cellRangeRendererCalls = 0;\n      // wait for real _debounceScrollEndedCallback\n      await new Promise(resolve =>\n        setTimeout(resolve, scrollingResetTimeInterval * 1.5),\n      );\n      expect(cellRangeRendererCalls).toEqual(1);\n    });\n\n    it('should support a custom :scrollingResetTimeInterval prop', async done => {\n      const cellRendererCalls = [];\n      const scrollingResetTimeInterval =\n        DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2;\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        cellRendererCalls.push({columnIndex, rowIndex});\n        return defaultCellRenderer({columnIndex, key, rowIndex, style});\n      }\n      const props = {\n        cellRenderer,\n        scrollingResetTimeInterval,\n      };\n\n      const grid = render(getMarkup(props));\n      expect(cellRendererCalls.length > 0).toEqual(true);\n\n      simulateScroll({grid, scrollTop: 1});\n\n      await new Promise(resolve =>\n        setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL),\n      );\n\n      cellRendererCalls.splice(0);\n      render(\n        getMarkup({\n          ...props,\n          className: 'foo',\n        }),\n      );\n      expect(cellRendererCalls.length).toEqual(0);\n\n      await new Promise(resolve =>\n        setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2),\n      );\n\n      cellRendererCalls.splice(0);\n      render(\n        getMarkup({\n          ...props,\n          className: 'bar',\n        }),\n      );\n      expect(cellRendererCalls.length).not.toEqual(0);\n\n      done();\n    });\n  });\n\n  describe('measureAllCells', () => {\n    it('should measure any unmeasured columns and rows', () => {\n      const grid = render(\n        getMarkup({\n          columnCount: 10,\n          columnWidth: () => 100,\n          estimatedColumnSize: 150,\n          estimatedRowSize: 15,\n          height: 0,\n          rowCount: 10,\n          rowHeight: () => 20,\n          width: 0,\n        }),\n      );\n      expect(\n        grid.state.instanceProps.columnSizeAndPositionManager.getTotalSize(),\n      ).toEqual(1500);\n      expect(\n        grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize(),\n      ).toEqual(150);\n      grid.measureAllCells();\n      expect(\n        grid.state.instanceProps.columnSizeAndPositionManager.getTotalSize(),\n      ).toEqual(1000);\n      expect(\n        grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize(),\n      ).toEqual(200);\n    });\n  });\n\n  describe('recomputeGridSize', () => {\n    it('should recompute cell sizes and other values when called', () => {\n      const columnIndices = [];\n      const rowIndices = [];\n      function columnWidth({index}) {\n        columnIndices.push(index);\n        return 10;\n      }\n      function rowHeight({index}) {\n        rowIndices.push(index);\n        return 10;\n      }\n      const props = {\n        columnCount: 50,\n        columnWidth,\n        height: 50,\n        rowHeight,\n        rowCount: 50,\n        width: 100,\n      };\n      const component = render(getMarkup(props));\n\n      columnIndices.splice(0);\n      rowIndices.splice(0);\n\n      component.recomputeGridSize();\n\n      // Only the rows required to fill the current viewport will be rendered\n      expect(columnIndices[0]).toEqual(0);\n      expect(columnIndices[columnIndices.length - 1]).toEqual(9);\n      expect(rowIndices[0]).toEqual(0);\n      expect(rowIndices[rowIndices.length - 1]).toEqual(4);\n\n      columnIndices.splice(0);\n      rowIndices.splice(0);\n\n      component.recomputeGridSize({\n        columnIndex: 4,\n        rowIndex: 2,\n      });\n\n      // Only the rows required to fill the current viewport will be rendered\n      expect(columnIndices[0]).toEqual(4);\n      expect(columnIndices[columnIndices.length - 1]).toEqual(9);\n      expect(rowIndices[0]).toEqual(2);\n      expect(rowIndices[rowIndices.length - 1]).toEqual(4);\n    });\n  });\n\n  describe('autoContainerWidth', () => {\n    it('should set the innerScrollContainer width to auto to better support single-column HOCs', () => {\n      const props = {\n        autoContainerWidth: true,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(\n        rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer')\n          .style.width,\n      ).toEqual('auto');\n    });\n\n    it('should set the innerScrollContainer width to :totalColumnsWidth unless :autoContainerWidth', () => {\n      const props = {\n        autoContainerWidth: false,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(\n        rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer')\n          .style.width,\n      ).toEqual('2500px'); // 50 columns x 50px\n    });\n  });\n\n  describe('autoHeight', () => {\n    it('should set the container height to auto to adjust to innerScrollContainer height', () => {\n      const props = {\n        autoHeight: true,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(rendered.style.height).toEqual('auto');\n    });\n\n    it('should have container height still affecting number of rows rendered', () => {\n      const props = {\n        height: 500,\n        autoHeight: true,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(rendered.querySelectorAll('.gridItem').length).toEqual(100); // 25 rows x 4 columns\n    });\n\n    it('should have innerScrollContainer height to be equal number of rows * rowHeight', () => {\n      const props = {\n        autoHeight: true,\n      };\n      const grid = render(getMarkup(props));\n      const rendered = findDOMNode(grid);\n      expect(\n        rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer')\n          .style.height,\n      ).toEqual('2000px'); // 100 rows * 20px rowHeight\n      expect(\n        grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize(),\n      ).toEqual(2000);\n    });\n  });\n\n  describe('autoWidth', () => {\n    it('should set the container width to auto to adjust to innerScrollContainer width', () => {\n      const props = {\n        autoWidth: true,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(rendered.style.width).toEqual('auto');\n    });\n\n    it('should have container width still affecting number of columns rendered', () => {\n      const props = {\n        width: 500,\n        autoWidth: true,\n      };\n      const rendered = findDOMNode(render(getMarkup(props)));\n      expect(rendered.querySelectorAll('.gridItem').length).toEqual(50); // 5 rows x 10 columns\n    });\n\n    it('should have innerScrollContainer width to be equal number of columns * columnWidth', () => {\n      const props = {\n        autoWidth: true,\n      };\n      const grid = render(getMarkup(props));\n      const rendered = findDOMNode(grid);\n      expect(\n        rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer')\n          .style.width,\n      ).toEqual('2500px'); // 50 columns * 50px columnWidth\n      expect(\n        grid.state.instanceProps.columnSizeAndPositionManager.getTotalSize(),\n      ).toEqual(2500);\n    });\n  });\n\n  describe('tabIndex', () => {\n    it('should be focusable by default', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.tabIndex).toEqual(0);\n    });\n\n    it('should allow tabIndex to be overridden', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            tabIndex: -1,\n          }),\n        ),\n      );\n      expect(rendered.tabIndex).toEqual(-1);\n    });\n  });\n\n  describe('role', () => {\n    it('should have grid role by default', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.getAttribute('role')).toEqual('grid');\n    });\n\n    it('should allow role to be overridden', () => {\n      const role = null;\n      const rendered = findDOMNode(render(getMarkup({role})));\n      expect(rendered.getAttribute('role')).toEqual(role);\n    });\n  });\n\n  describe('pure', () => {\n    it('should not re-render unless props have changed', () => {\n      let cellRendererCalled = false;\n      function cellRenderer({key, style}) {\n        cellRendererCalled = true;\n        return <div key={key} style={style} />;\n      }\n      const markup = getMarkup({cellRenderer});\n      render(markup);\n      expect(cellRendererCalled).toEqual(true);\n      cellRendererCalled = false;\n      render(markup);\n      expect(cellRendererCalled).toEqual(false);\n    });\n\n    it('should not re-render grid components if they extend PureComponent', () => {\n      let componentUpdates = 0;\n\n      class GridComponent extends React.PureComponent {\n        componentDidUpdate() {\n          componentUpdates++;\n        }\n\n        render() {\n          const {columnIndex, rowIndex, style} = this.props;\n          return (\n            <div className=\"gridItem\" style={style}>\n              {`row:${rowIndex}, column:${columnIndex}`}\n            </div>\n          );\n        }\n      }\n\n      function cellRenderer({columnIndex, key, rowIndex, style}) {\n        return (\n          <GridComponent\n            key={key}\n            columnIndex={columnIndex}\n            rowIndex={rowIndex}\n            style={style}\n          />\n        );\n      }\n\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 40,\n        rowHeight: 20,\n        scrollTop: 0,\n        width: 100,\n      };\n\n      const grid = render(getMarkup(props));\n      simulateScroll({grid, scrollToIndex: 1});\n\n      expect(componentUpdates).toEqual(0);\n    });\n\n    it('should clear all but the visible rows from the style cache once :isScrolling is false', async done => {\n      const props = {\n        columnWidth: 50,\n        height: 100,\n        overscanColumnCount: 0,\n        overscanRowCount: 0,\n        rowHeight: 50,\n        width: 100,\n      };\n\n      const grid = render(getMarkup(props));\n\n      expect(Object.keys(grid._styleCache).length).toBe(4);\n\n      simulateScroll({grid, scrollTop: 50});\n\n      expect(Object.keys(grid._styleCache).length).toBe(6);\n\n      // Allow scrolling timeout to complete so that cell cache is reset\n      await new Promise(resolve =>\n        setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2),\n      );\n\n      expect(Object.keys(grid._styleCache).length).toBe(4);\n\n      done();\n    });\n\n    it('should clear style cache if :recomputeGridSize is called', () => {\n      const props = {\n        columnWidth: 50,\n        height: 100,\n        overscanColumnCount: 0,\n        overscanRowCount: 0,\n        rowHeight: 50,\n        width: 100,\n      };\n\n      const grid = render(getMarkup(props));\n\n      expect(Object.keys(grid._styleCache).length).toBe(4);\n\n      render(\n        getMarkup({\n          ...props,\n          scrollTop: 50,\n        }),\n      );\n\n      expect(Object.keys(grid._styleCache).length).toBe(6);\n\n      grid.recomputeGridSize();\n\n      expect(Object.keys(grid._styleCache).length).toBe(4);\n    });\n\n    it('should clear style cache if cell sizes change', () => {\n      const cellRendererCalls = [];\n      function cellRenderer(params) {\n        cellRendererCalls.push(params);\n        return <div key={params.key} style={params.style} />;\n      }\n\n      const props = {\n        cellRenderer,\n        columnWidth: 100,\n        height: 100,\n        overscanColumnCount: 0,\n        overscanRowCount: 0,\n        rowHeight: 100,\n        width: 100,\n      };\n\n      render(getMarkup(props));\n\n      expect(cellRendererCalls.length).toEqual(1);\n      expect(cellRendererCalls[0].style.width).toEqual(100);\n\n      render(\n        getMarkup({\n          ...props,\n          columnWidth: 50,\n          width: 50,\n        }),\n      );\n\n      expect(cellRendererCalls.length).toEqual(2);\n      expect(cellRendererCalls[1].style.width).toEqual(50);\n    });\n  });\n\n  it('should not pull from the style cache while scrolling if there is an offset adjustment', () => {\n    let cellRendererCalls = [];\n    function cellRenderer(params) {\n      cellRendererCalls.push(params);\n      return <div key={params.key} style={params.style} />;\n    }\n\n    const grid = render(\n      getMarkup({\n        cellRenderer,\n        width: 100,\n        height: 100,\n        rowHeight: 100,\n        columnWidth: 100,\n        rowCount: (getMaxElementSize() * 2) / 100, // lots of offset\n        scrollTop: 2000,\n      }),\n    );\n\n    simulateScroll({\n      grid,\n      scrollTop: 2100,\n    });\n\n    // cellRendererCalls[0] is the element at rowIndex 0\n    // only two calls. Since the scrollTop is updated in getDerivedStateFromProps\n    const firstProps = cellRendererCalls[0];\n    const secondProps = cellRendererCalls[1];\n\n    expect(cellRendererCalls.length).toEqual(2);\n    expect(firstProps.style).not.toBe(secondProps.style);\n  });\n\n  it('should only cache styles when a :deferredMeasurementCache is provided if the cell has already been measured', () => {\n    const cache = new CellMeasurerCache({\n      fixedWidth: true,\n    });\n    cache.set(0, 0, 100, 100);\n    cache.set(1, 1, 100, 100);\n\n    const grid = render(\n      getMarkup({\n        columnCount: 2,\n        deferredMeasurementCache: cache,\n        rowCount: 2,\n      }),\n    );\n\n    const keys = Object.keys(grid._styleCache);\n\n    expect(keys).toEqual(['0-0', '1-1']);\n  });\n\n  describe('DEV warnings', () => {\n    it('should warn about cells that forget to include the :style property', () => {\n      spyOn(console, 'warn');\n\n      function cellRenderer(params) {\n        return <div key={params.key} />;\n      }\n\n      render(\n        getMarkup({\n          cellRenderer,\n        }),\n      );\n\n      expect(console.warn).toHaveBeenCalledWith(\n        'Rendered cell should include style property for positioning.',\n      );\n      expect(console.warn).toHaveBeenCalledTimes(1);\n    });\n\n    it('should warn about CellMeasurer measured cells that forget to include the :style property', () => {\n      spyOn(console, 'warn');\n\n      const cache = new CellMeasurerCache({\n        fixedWidth: true,\n      });\n\n      const cellRenderer = jest.fn();\n      cellRenderer.mockImplementation(params => (\n        <CellMeasurer\n          cache={cache}\n          columnIndex={params.columnIndex}\n          key={params.key}\n          parent={params.parent}\n          rowIndex={params.rowIndex}\n          style={params.style}>\n          <div />\n        </CellMeasurer>\n      ));\n\n      render(\n        getMarkup({\n          cellRenderer,\n          columnCount: 1,\n          deferredMeasurementCache: cache,\n          rowCount: 1,\n        }),\n      );\n\n      expect(console.warn).toHaveBeenCalledWith(\n        'Rendered cell should include style property for positioning.',\n      );\n      expect(console.warn).toHaveBeenCalledTimes(1);\n    });\n  });\n\n  describe('deferredMeasurementCache', () => {\n    it('invalidateCellSizeAfterRender should invalidate cache and refresh displayed cells after mount', () => {\n      const cache = new CellMeasurerCache({\n        fixedWidth: true,\n      });\n\n      let invalidateCellSizeAfterRender = true;\n\n      const cellRenderer = jest.fn();\n      cellRenderer.mockImplementation(params => {\n        // Don't get stuck in a loop\n        if (invalidateCellSizeAfterRender) {\n          invalidateCellSizeAfterRender = false;\n\n          params.parent.invalidateCellSizeAfterRender({\n            columnIndex: 1,\n            rowIndex: 0,\n          });\n        }\n        return <div key={params.key} style={params.style} />;\n      });\n\n      const props = {\n        cellRenderer,\n        columnCount: 2,\n        deferredMeasurementCache: cache,\n        rowCount: 2,\n      };\n\n      render(getMarkup(props));\n\n      // 4 times for initial render + 4 once cellCache was cleared\n      expect(cellRenderer).toHaveBeenCalledTimes(8);\n    });\n\n    it('should invalidate cache and refresh displayed cells after update', () => {\n      const cache = new CellMeasurerCache({\n        fixedWidth: true,\n      });\n\n      const cellRenderer = jest.fn();\n      cellRenderer.mockImplementation(params => (\n        <div key={params.key} style={params.style} />\n      ));\n\n      const props = {\n        cellRenderer,\n        columnCount: 2,\n        deferredMeasurementCache: cache,\n        rowCount: 2,\n      };\n\n      const grid = render(getMarkup(props));\n\n      expect(cellRenderer).toHaveBeenCalledTimes(4);\n\n      let invalidateCellSizeAfterRender = false;\n\n      cellRenderer.mockReset();\n      cellRenderer.mockImplementation(params => {\n        // Don't get stuck in a loop\n        if (invalidateCellSizeAfterRender) {\n          invalidateCellSizeAfterRender = false;\n          params.parent.invalidateCellSizeAfterRender({\n            columnIndex: 1,\n            rowIndex: 0,\n          });\n        }\n        return <div key={params.key} style={params.style} />;\n      });\n\n      invalidateCellSizeAfterRender = true;\n      grid.recomputeGridSize();\n\n      // 4 times for initial render + 4 once cellCache was cleared\n      expect(cellRenderer).toHaveBeenCalledTimes(8);\n    });\n\n    it('should not cache cells until they have been measured by CellMeasurer', () => {\n      const cache = new CellMeasurerCache({\n        fixedWidth: true,\n      });\n\n      // Fake measure cell 0,0 but not cell 0,1\n      cache.set(0, 0, 100, 30);\n\n      const cellRenderer = jest.fn();\n      cellRenderer.mockImplementation(params => (\n        <div key={params.key} style={params.style} />\n      ));\n\n      const props = {\n        cellRenderer,\n        columnCount: 2,\n        deferredMeasurementCache: cache,\n        rowCount: 1,\n      };\n\n      // Trigger 2 renders\n      // The second render should re-use the style for cell 0,0\n      // But should not re-use the style for cell 0,1 since it was not measured\n      const grid = render(getMarkup(props));\n      grid.forceUpdate();\n\n      // 0,0 - 0,1 - 0,0 - 0,1\n      expect(cellRenderer).toHaveBeenCalledTimes(4);\n      const style00A = cellRenderer.mock.calls[0][0].style;\n      const style01A = cellRenderer.mock.calls[1][0].style;\n      const style00B = cellRenderer.mock.calls[2][0].style;\n      const style01B = cellRenderer.mock.calls[3][0].style;\n      expect(style00A).toBe(style00B);\n      expect(style01A).not.toBe(style01B);\n    });\n  });\n\n  describe('onScrollbarPresenceChange', () => {\n    it('should not trigger on-mount if scrollbars are hidden', () => {\n      const onScrollbarPresenceChange = jest.fn();\n\n      render(\n        getMarkup({\n          columnCount: 1,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 1,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n    });\n\n    it('should trigger on-mount if scrollbars are visible', () => {\n      const onScrollbarPresenceChange = jest.fn();\n\n      render(\n        getMarkup({\n          columnCount: 100,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 100,\n        }),\n      );\n      expect(onScrollbarPresenceChange).toHaveBeenCalled();\n\n      const args = onScrollbarPresenceChange.mock.calls[0][0];\n      expect(args.horizontal).toBe(true);\n      expect(args.size).toBe(getScrollbarSize20());\n      expect(args.vertical).toBe(true);\n    });\n\n    it('should trigger on-update if scrollbar visibility has changed', () => {\n      const onScrollbarPresenceChange = jest.fn();\n      render(\n        getMarkup({\n          columnCount: 1,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 1,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n\n      render(\n        getMarkup({\n          columnCount: 100,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 100,\n        }),\n      );\n      expect(onScrollbarPresenceChange).toHaveBeenCalled();\n\n      const args = onScrollbarPresenceChange.mock.calls[0][0];\n      expect(args.horizontal).toBe(true);\n      expect(args.size).toBe(getScrollbarSize20());\n      expect(args.vertical).toBe(true);\n    });\n\n    it('should not trigger on-update if scrollbar visibility does not change', () => {\n      const onScrollbarPresenceChange = jest.fn();\n      render(\n        getMarkup({\n          columnCount: 1,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 1,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n      render(\n        getMarkup({\n          columnCount: 2,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 2,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n    });\n  });\n\n  it('should not complain when using react-test-renderer', () => {\n    const instance = TestRenderer.create(getMarkup()).getInstance();\n    expect(instance).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "source/Grid/Grid.js",
    "content": "/** @flow */\n\nimport type {\n  CellRenderer,\n  CellRangeRenderer,\n  CellPosition,\n  CellSize,\n  CellSizeGetter,\n  NoContentRenderer,\n  Scroll,\n  ScrollbarPresenceChange,\n  RenderedSection,\n  OverscanIndicesGetter,\n  Alignment,\n  CellCache,\n  StyleCache,\n} from './types';\nimport type {AnimationTimeoutId} from '../utils/requestAnimationTimeout';\n\nimport * as React from 'react';\nimport clsx from 'clsx';\nimport calculateSizeAndPositionDataAndUpdateScrollOffset from './utils/calculateSizeAndPositionDataAndUpdateScrollOffset';\nimport ScalingCellSizeAndPositionManager from './utils/ScalingCellSizeAndPositionManager';\nimport createCallbackMemoizer from '../utils/createCallbackMemoizer';\nimport defaultOverscanIndicesGetter, {\n  SCROLL_DIRECTION_BACKWARD,\n  SCROLL_DIRECTION_FORWARD,\n} from './defaultOverscanIndicesGetter';\nimport updateScrollIndexHelper from './utils/updateScrollIndexHelper';\nimport defaultCellRangeRenderer from './defaultCellRangeRenderer';\nimport scrollbarSize from 'dom-helpers/scrollbarSize';\nimport {polyfill} from 'react-lifecycles-compat';\nimport {\n  requestAnimationTimeout,\n  cancelAnimationTimeout,\n} from '../utils/requestAnimationTimeout';\n\n/**\n * Specifies the number of milliseconds during which to disable pointer events while a scroll is in progress.\n * This improves performance and makes scrolling smoother.\n */\nexport const DEFAULT_SCROLLING_RESET_TIME_INTERVAL = 150;\n\n/**\n * Controls whether the Grid updates the DOM element's scrollLeft/scrollTop based on the current state or just observes it.\n * This prevents Grid from interrupting mouse-wheel animations (see issue #2).\n */\nconst SCROLL_POSITION_CHANGE_REASONS = {\n  OBSERVED: 'observed',\n  REQUESTED: 'requested',\n};\n\nconst renderNull: NoContentRenderer = () => null;\n\ntype ScrollPosition = {\n  scrollTop?: number,\n  scrollLeft?: number,\n};\n\ntype Props = {\n  'aria-label': string,\n  'aria-readonly'?: boolean,\n\n  /**\n   * Set the width of the inner scrollable container to 'auto'.\n   * This is useful for single-column Grids to ensure that the column doesn't extend below a vertical scrollbar.\n   */\n  autoContainerWidth: boolean,\n\n  /**\n   * Removes fixed height from the scrollingContainer so that the total height of rows can stretch the window.\n   * Intended for use with WindowScroller\n   */\n  autoHeight: boolean,\n\n  /**\n   * Removes fixed width from the scrollingContainer so that the total width of rows can stretch the window.\n   * Intended for use with WindowScroller\n   */\n  autoWidth: boolean,\n\n  /** Responsible for rendering a cell given an row and column index.  */\n  cellRenderer: CellRenderer,\n\n  /** Responsible for rendering a group of cells given their index ranges.  */\n  cellRangeRenderer: CellRangeRenderer,\n\n  /** Optional custom CSS class name to attach to root Grid element.  */\n  className?: string,\n\n  /** Number of columns in grid.  */\n  columnCount: number,\n\n  /** Either a fixed column width (number) or a function that returns the width of a column given its index.  */\n  columnWidth: CellSize,\n\n  /** Unfiltered props for the Grid container. */\n  containerProps?: Object,\n\n  /** ARIA role for the cell-container.  */\n  containerRole: string,\n\n  /** Optional inline style applied to inner cell-container */\n  containerStyle: Object,\n\n  /**\n   * If CellMeasurer is used to measure this Grid's children, this should be a pointer to its CellMeasurerCache.\n   * A shared CellMeasurerCache reference enables Grid and CellMeasurer to share measurement data.\n   */\n  deferredMeasurementCache?: Object,\n\n  /**\n   * Used to estimate the total width of a Grid before all of its columns have actually been measured.\n   * The estimated total width is adjusted as columns are rendered.\n   */\n  estimatedColumnSize: number,\n\n  /**\n   * Used to estimate the total height of a Grid before all of its rows have actually been measured.\n   * The estimated total height is adjusted as rows are rendered.\n   */\n  estimatedRowSize: number,\n\n  /** Exposed for testing purposes only.  */\n  getScrollbarSize: () => number,\n\n  /** Height of Grid; this property determines the number of visible (vs virtualized) rows.  */\n  height: number,\n\n  /** Optional custom id to attach to root Grid element.  */\n  id?: string,\n\n  /**\n   * Override internal is-scrolling state tracking.\n   * This property is primarily intended for use with the WindowScroller component.\n   */\n  isScrolling?: boolean,\n\n  /**\n   * Opt-out of isScrolling param passed to cellRangeRenderer.\n   * To avoid the extra render when scroll stops.\n   */\n  isScrollingOptOut: boolean,\n\n  /** Optional renderer to be used in place of rows when either :rowCount or :columnCount is 0.  */\n  noContentRenderer: NoContentRenderer,\n\n  /**\n   * Callback invoked whenever the scroll offset changes within the inner scrollable region.\n   * This callback can be used to sync scrolling between lists, tables, or grids.\n   */\n  onScroll: (params: Scroll) => void,\n\n  /**\n   * Called whenever a horizontal or vertical scrollbar is added or removed.\n   * This prop is not intended for end-user use;\n   * It is used by MultiGrid to support fixed-row/fixed-column scroll syncing.\n   */\n  onScrollbarPresenceChange: (params: ScrollbarPresenceChange) => void,\n\n  /** Callback invoked with information about the section of the Grid that was just rendered.  */\n  onSectionRendered: (params: RenderedSection) => void,\n\n  /**\n   * Number of columns to render before/after the visible section of the grid.\n   * These columns can help for smoother scrolling on touch devices or browsers that send scroll events infrequently.\n   */\n  overscanColumnCount: number,\n\n  /**\n   * Calculates the number of cells to overscan before and after a specified range.\n   * This function ensures that overscanning doesn't exceed the available cells.\n   */\n  overscanIndicesGetter: OverscanIndicesGetter,\n\n  /**\n   * Number of rows to render above/below the visible section of the grid.\n   * These rows can help for smoother scrolling on touch devices or browsers that send scroll events infrequently.\n   */\n  overscanRowCount: number,\n\n  /** ARIA role for the grid element.  */\n  role: string,\n\n  /**\n   * Either a fixed row height (number) or a function that returns the height of a row given its index.\n   * Should implement the following interface: ({ index: number }): number\n   */\n  rowHeight: CellSize,\n\n  /** Number of rows in grid.  */\n  rowCount: number,\n\n  /** Wait this amount of time after the last scroll event before resetting Grid `pointer-events`. */\n  scrollingResetTimeInterval: number,\n\n  /** Horizontal offset. */\n  scrollLeft?: number,\n\n  /**\n   * Controls scroll-to-cell behavior of the Grid.\n   * The default (\"auto\") scrolls the least amount possible to ensure that the specified cell is fully visible.\n   * Use \"start\" to align cells to the top/left of the Grid and \"end\" to align bottom/right.\n   */\n  scrollToAlignment: Alignment,\n\n  /** Column index to ensure visible (by forcefully scrolling if necessary) */\n  scrollToColumn: number,\n\n  /** Vertical offset. */\n  scrollTop?: number,\n\n  /** Row index to ensure visible (by forcefully scrolling if necessary) */\n  scrollToRow: number,\n\n  /** Optional inline style */\n  style: Object,\n\n  /** Tab index for focus */\n  tabIndex: ?number,\n\n  /** Width of Grid; this property determines the number of visible (vs virtualized) columns.  */\n  width: number,\n\n  /** Reference to DOM node */\n  elementRef?: React.Ref<React.ElementType>,\n};\n\ntype InstanceProps = {\n  prevColumnWidth: CellSize,\n  prevRowHeight: CellSize,\n\n  prevColumnCount: number,\n  prevRowCount: number,\n  prevIsScrolling: boolean,\n  prevScrollToColumn: number,\n  prevScrollToRow: number,\n\n  columnSizeAndPositionManager: ScalingCellSizeAndPositionManager,\n  rowSizeAndPositionManager: ScalingCellSizeAndPositionManager,\n\n  scrollbarSize: number,\n  scrollbarSizeMeasured: boolean,\n};\n\ntype State = {\n  instanceProps: InstanceProps,\n  isScrolling: boolean,\n  scrollDirectionHorizontal: -1 | 1,\n  scrollDirectionVertical: -1 | 1,\n  scrollLeft: number,\n  scrollTop: number,\n  scrollPositionChangeReason: 'observed' | 'requested' | null,\n  needToResetStyleCache: boolean,\n};\n\n/**\n * Renders tabular data with virtualization along the vertical and horizontal axes.\n * Row heights and column widths must be known ahead of time and specified as properties.\n */\nclass Grid extends React.PureComponent<Props, State> {\n  static defaultProps = {\n    'aria-label': 'grid',\n    'aria-readonly': true,\n    autoContainerWidth: false,\n    autoHeight: false,\n    autoWidth: false,\n    cellRangeRenderer: defaultCellRangeRenderer,\n    containerRole: 'row',\n    containerStyle: {},\n    estimatedColumnSize: 100,\n    estimatedRowSize: 30,\n    getScrollbarSize: scrollbarSize,\n    noContentRenderer: renderNull,\n    onScroll: () => {},\n    onScrollbarPresenceChange: () => {},\n    onSectionRendered: () => {},\n    overscanColumnCount: 0,\n    overscanIndicesGetter: defaultOverscanIndicesGetter,\n    overscanRowCount: 10,\n    role: 'grid',\n    scrollingResetTimeInterval: DEFAULT_SCROLLING_RESET_TIME_INTERVAL,\n    scrollToAlignment: 'auto',\n    scrollToColumn: -1,\n    scrollToRow: -1,\n    style: {},\n    tabIndex: 0,\n    isScrollingOptOut: false,\n  };\n\n  // Invokes onSectionRendered callback only when start/stop row or column indices change\n  _onGridRenderedMemoizer = createCallbackMemoizer();\n  _onScrollMemoizer = createCallbackMemoizer(false);\n\n  _deferredInvalidateColumnIndex = null;\n  _deferredInvalidateRowIndex = null;\n  _recomputeScrollLeftFlag = false;\n  _recomputeScrollTopFlag = false;\n\n  _horizontalScrollBarSize = 0;\n  _verticalScrollBarSize = 0;\n  _scrollbarPresenceChanged = false;\n  _scrollingContainer: Element;\n\n  _childrenToDisplay: React.Element<*>[];\n\n  _columnStartIndex: number;\n  _columnStopIndex: number;\n  _rowStartIndex: number;\n  _rowStopIndex: number;\n  _renderedColumnStartIndex = 0;\n  _renderedColumnStopIndex = 0;\n  _renderedRowStartIndex = 0;\n  _renderedRowStopIndex = 0;\n\n  _initialScrollTop: number;\n  _initialScrollLeft: number;\n\n  _disablePointerEventsTimeoutId: ?AnimationTimeoutId;\n\n  _styleCache: StyleCache = {};\n  _cellCache: CellCache = {};\n\n  constructor(props: Props) {\n    super(props);\n    const columnSizeAndPositionManager = new ScalingCellSizeAndPositionManager({\n      cellCount: props.columnCount,\n      cellSizeGetter: params => Grid._wrapSizeGetter(props.columnWidth)(params),\n      estimatedCellSize: Grid._getEstimatedColumnSize(props),\n    });\n    const rowSizeAndPositionManager = new ScalingCellSizeAndPositionManager({\n      cellCount: props.rowCount,\n      cellSizeGetter: params => Grid._wrapSizeGetter(props.rowHeight)(params),\n      estimatedCellSize: Grid._getEstimatedRowSize(props),\n    });\n\n    this.state = {\n      instanceProps: {\n        columnSizeAndPositionManager,\n        rowSizeAndPositionManager,\n\n        prevColumnWidth: props.columnWidth,\n        prevRowHeight: props.rowHeight,\n        prevColumnCount: props.columnCount,\n        prevRowCount: props.rowCount,\n        prevIsScrolling: props.isScrolling === true,\n        prevScrollToColumn: props.scrollToColumn,\n        prevScrollToRow: props.scrollToRow,\n\n        scrollbarSize: 0,\n        scrollbarSizeMeasured: false,\n      },\n      isScrolling: false,\n      scrollDirectionHorizontal: SCROLL_DIRECTION_FORWARD,\n      scrollDirectionVertical: SCROLL_DIRECTION_FORWARD,\n      scrollLeft: 0,\n      scrollTop: 0,\n      scrollPositionChangeReason: null,\n\n      needToResetStyleCache: false,\n    };\n\n    if (props.scrollToRow > 0) {\n      this._initialScrollTop = this._getCalculatedScrollTop(props, this.state);\n    }\n    if (props.scrollToColumn > 0) {\n      this._initialScrollLeft = this._getCalculatedScrollLeft(\n        props,\n        this.state,\n      );\n    }\n  }\n\n  /**\n   * Gets offsets for a given cell and alignment.\n   */\n  getOffsetForCell({\n    alignment = this.props.scrollToAlignment,\n    columnIndex = this.props.scrollToColumn,\n    rowIndex = this.props.scrollToRow,\n  }: {\n    alignment?: Alignment,\n    columnIndex?: number,\n    rowIndex?: number,\n  } = {}) {\n    const offsetProps = {\n      ...this.props,\n      scrollToAlignment: alignment,\n      scrollToColumn: columnIndex,\n      scrollToRow: rowIndex,\n    };\n\n    return {\n      scrollLeft: this._getCalculatedScrollLeft(offsetProps),\n      scrollTop: this._getCalculatedScrollTop(offsetProps),\n    };\n  }\n\n  /**\n   * Gets estimated total rows' height.\n   */\n  getTotalRowsHeight() {\n    return this.state.instanceProps.rowSizeAndPositionManager.getTotalSize();\n  }\n\n  /**\n   * Gets estimated total columns' width.\n   */\n  getTotalColumnsWidth() {\n    return this.state.instanceProps.columnSizeAndPositionManager.getTotalSize();\n  }\n\n  /**\n   * This method handles a scroll event originating from an external scroll control.\n   * It's an advanced method and should probably not be used unless you're implementing a custom scroll-bar solution.\n   */\n  handleScrollEvent({\n    scrollLeft: scrollLeftParam = 0,\n    scrollTop: scrollTopParam = 0,\n  }: ScrollPosition) {\n    // On iOS, we can arrive at negative offsets by swiping past the start.\n    // To prevent flicker here, we make playing in the negative offset zone cause nothing to happen.\n    if (scrollTopParam < 0) {\n      return;\n    }\n\n    // Prevent pointer events from interrupting a smooth scroll\n    this._debounceScrollEnded();\n\n    const {autoHeight, autoWidth, height, width} = this.props;\n    const {instanceProps} = this.state;\n\n    // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events,\n    // Gradually converging on a scrollTop that is within the bounds of the new, smaller height.\n    // This causes a series of rapid renders that is slow for long lists.\n    // We can avoid that by doing some simple bounds checking to ensure that scroll offsets never exceed their bounds.\n    const scrollbarSize = instanceProps.scrollbarSize;\n    const totalRowsHeight = instanceProps.rowSizeAndPositionManager.getTotalSize();\n    const totalColumnsWidth = instanceProps.columnSizeAndPositionManager.getTotalSize();\n    const scrollLeft = Math.min(\n      Math.max(0, totalColumnsWidth - width + scrollbarSize),\n      scrollLeftParam,\n    );\n    const scrollTop = Math.min(\n      Math.max(0, totalRowsHeight - height + scrollbarSize),\n      scrollTopParam,\n    );\n\n    // Certain devices (like Apple touchpad) rapid-fire duplicate events.\n    // Don't force a re-render if this is the case.\n    // The mouse may move faster then the animation frame does.\n    // Use requestAnimationFrame to avoid over-updating.\n    if (\n      this.state.scrollLeft !== scrollLeft ||\n      this.state.scrollTop !== scrollTop\n    ) {\n      // Track scrolling direction so we can more efficiently overscan rows to reduce empty space around the edges while scrolling.\n      // Don't change direction for an axis unless scroll offset has changed.\n      const scrollDirectionHorizontal =\n        scrollLeft !== this.state.scrollLeft\n          ? scrollLeft > this.state.scrollLeft\n            ? SCROLL_DIRECTION_FORWARD\n            : SCROLL_DIRECTION_BACKWARD\n          : this.state.scrollDirectionHorizontal;\n      const scrollDirectionVertical =\n        scrollTop !== this.state.scrollTop\n          ? scrollTop > this.state.scrollTop\n            ? SCROLL_DIRECTION_FORWARD\n            : SCROLL_DIRECTION_BACKWARD\n          : this.state.scrollDirectionVertical;\n\n      const newState: $Shape<State> = {\n        isScrolling: true,\n        scrollDirectionHorizontal,\n        scrollDirectionVertical,\n        scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.OBSERVED,\n      };\n\n      if (!autoHeight) {\n        newState.scrollTop = scrollTop;\n      }\n\n      if (!autoWidth) {\n        newState.scrollLeft = scrollLeft;\n      }\n\n      newState.needToResetStyleCache = false;\n      this.setState(newState);\n    }\n\n    this._invokeOnScrollMemoizer({\n      scrollLeft,\n      scrollTop,\n      totalColumnsWidth,\n      totalRowsHeight,\n    });\n  }\n\n  /**\n   * Invalidate Grid size and recompute visible cells.\n   * This is a deferred wrapper for recomputeGridSize().\n   * It sets a flag to be evaluated on cDM/cDU to avoid unnecessary renders.\n   * This method is intended for advanced use-cases like CellMeasurer.\n   */\n  // @TODO (bvaughn) Add automated test coverage for this.\n  invalidateCellSizeAfterRender({columnIndex, rowIndex}: CellPosition) {\n    this._deferredInvalidateColumnIndex =\n      typeof this._deferredInvalidateColumnIndex === 'number'\n        ? Math.min(this._deferredInvalidateColumnIndex, columnIndex)\n        : columnIndex;\n    this._deferredInvalidateRowIndex =\n      typeof this._deferredInvalidateRowIndex === 'number'\n        ? Math.min(this._deferredInvalidateRowIndex, rowIndex)\n        : rowIndex;\n  }\n\n  /**\n   * Pre-measure all columns and rows in a Grid.\n   * Typically cells are only measured as needed and estimated sizes are used for cells that have not yet been measured.\n   * This method ensures that the next call to getTotalSize() returns an exact size (as opposed to just an estimated one).\n   */\n  measureAllCells() {\n    const {columnCount, rowCount} = this.props;\n    const {instanceProps} = this.state;\n    instanceProps.columnSizeAndPositionManager.getSizeAndPositionOfCell(\n      columnCount - 1,\n    );\n    instanceProps.rowSizeAndPositionManager.getSizeAndPositionOfCell(\n      rowCount - 1,\n    );\n  }\n\n  /**\n   * Forced recompute of row heights and column widths.\n   * This function should be called if dynamic column or row sizes have changed but nothing else has.\n   * Since Grid only receives :columnCount and :rowCount it has no way of detecting when the underlying data changes.\n   */\n  recomputeGridSize({columnIndex = 0, rowIndex = 0}: CellPosition = {}) {\n    const {scrollToColumn, scrollToRow} = this.props;\n    const {instanceProps} = this.state;\n\n    instanceProps.columnSizeAndPositionManager.resetCell(columnIndex);\n    instanceProps.rowSizeAndPositionManager.resetCell(rowIndex);\n\n    // Cell sizes may be determined by a function property.\n    // In this case the cDU handler can't know if they changed.\n    // Store this flag to let the next cDU pass know it needs to recompute the scroll offset.\n    this._recomputeScrollLeftFlag =\n      scrollToColumn >= 0 &&\n      (this.state.scrollDirectionHorizontal === SCROLL_DIRECTION_FORWARD\n        ? columnIndex <= scrollToColumn\n        : columnIndex >= scrollToColumn);\n    this._recomputeScrollTopFlag =\n      scrollToRow >= 0 &&\n      (this.state.scrollDirectionVertical === SCROLL_DIRECTION_FORWARD\n        ? rowIndex <= scrollToRow\n        : rowIndex >= scrollToRow);\n\n    // Clear cell cache in case we are scrolling;\n    // Invalid row heights likely mean invalid cached content as well.\n    this._styleCache = {};\n    this._cellCache = {};\n\n    this.forceUpdate();\n  }\n\n  /**\n   * Ensure column and row are visible.\n   */\n  scrollToCell({columnIndex, rowIndex}: CellPosition) {\n    const {columnCount} = this.props;\n\n    const props = this.props;\n\n    // Don't adjust scroll offset for single-column grids (eg List, Table).\n    // This can cause a funky scroll offset because of the vertical scrollbar width.\n    if (columnCount > 1 && columnIndex !== undefined) {\n      this._updateScrollLeftForScrollToColumn({\n        ...props,\n        scrollToColumn: columnIndex,\n      });\n    }\n\n    if (rowIndex !== undefined) {\n      this._updateScrollTopForScrollToRow({\n        ...props,\n        scrollToRow: rowIndex,\n      });\n    }\n  }\n\n  componentDidMount() {\n    const {\n      getScrollbarSize,\n      height,\n      scrollLeft,\n      scrollToColumn,\n      scrollTop,\n      scrollToRow,\n      width,\n    } = this.props;\n\n    const {instanceProps} = this.state;\n\n    // Reset initial offsets to be ignored in browser\n    this._initialScrollTop = 0;\n    this._initialScrollLeft = 0;\n\n    // If cell sizes have been invalidated (eg we are using CellMeasurer) then reset cached positions.\n    // We must do this at the start of the method as we may calculate and update scroll position below.\n    this._handleInvalidatedGridSize();\n\n    // If this component was first rendered server-side, scrollbar size will be undefined.\n    // In that event we need to remeasure.\n    if (!instanceProps.scrollbarSizeMeasured) {\n      this.setState(prevState => {\n        const stateUpdate = {...prevState, needToResetStyleCache: false};\n        stateUpdate.instanceProps.scrollbarSize = getScrollbarSize();\n        stateUpdate.instanceProps.scrollbarSizeMeasured = true;\n        return stateUpdate;\n      });\n    }\n\n    if (\n      (typeof scrollLeft === 'number' && scrollLeft >= 0) ||\n      (typeof scrollTop === 'number' && scrollTop >= 0)\n    ) {\n      const stateUpdate = Grid._getScrollToPositionStateUpdate({\n        prevState: this.state,\n        scrollLeft,\n        scrollTop,\n      });\n      if (stateUpdate) {\n        stateUpdate.needToResetStyleCache = false;\n        this.setState(stateUpdate);\n      }\n    }\n\n    // refs don't work in `react-test-renderer`\n    if (this._scrollingContainer) {\n      // setting the ref's scrollLeft and scrollTop.\n      // Somehow in MultiGrid the main grid doesn't trigger a update on mount.\n      if (this._scrollingContainer.scrollLeft !== this.state.scrollLeft) {\n        this._scrollingContainer.scrollLeft = this.state.scrollLeft;\n      }\n      if (this._scrollingContainer.scrollTop !== this.state.scrollTop) {\n        this._scrollingContainer.scrollTop = this.state.scrollTop;\n      }\n    }\n\n    // Don't update scroll offset if the size is 0; we don't render any cells in this case.\n    // Setting a state may cause us to later thing we've updated the offce when we haven't.\n    const sizeIsBiggerThanZero = height > 0 && width > 0;\n    if (scrollToColumn >= 0 && sizeIsBiggerThanZero) {\n      this._updateScrollLeftForScrollToColumn();\n    }\n    if (scrollToRow >= 0 && sizeIsBiggerThanZero) {\n      this._updateScrollTopForScrollToRow();\n    }\n\n    // Update onRowsRendered callback\n    this._invokeOnGridRenderedHelper();\n\n    // Initialize onScroll callback\n    this._invokeOnScrollMemoizer({\n      scrollLeft: scrollLeft || 0,\n      scrollTop: scrollTop || 0,\n      totalColumnsWidth: instanceProps.columnSizeAndPositionManager.getTotalSize(),\n      totalRowsHeight: instanceProps.rowSizeAndPositionManager.getTotalSize(),\n    });\n\n    this._maybeCallOnScrollbarPresenceChange();\n  }\n\n  /**\n   * @private\n   * This method updates scrollLeft/scrollTop in state for the following conditions:\n   * 1) New scroll-to-cell props have been set\n   */\n  componentDidUpdate(prevProps: Props, prevState: State) {\n    const {\n      autoHeight,\n      autoWidth,\n      columnCount,\n      height,\n      rowCount,\n      scrollToAlignment,\n      scrollToColumn,\n      scrollToRow,\n      width,\n    } = this.props;\n    const {\n      scrollLeft,\n      scrollPositionChangeReason,\n      scrollTop,\n      instanceProps,\n    } = this.state;\n    // If cell sizes have been invalidated (eg we are using CellMeasurer) then reset cached positions.\n    // We must do this at the start of the method as we may calculate and update scroll position below.\n    this._handleInvalidatedGridSize();\n\n    // Handle edge case where column or row count has only just increased over 0.\n    // In this case we may have to restore a previously-specified scroll offset.\n    // For more info see bvaughn/react-virtualized/issues/218\n    const columnOrRowCountJustIncreasedFromZero =\n      (columnCount > 0 && prevProps.columnCount === 0) ||\n      (rowCount > 0 && prevProps.rowCount === 0);\n\n    // Make sure requested changes to :scrollLeft or :scrollTop get applied.\n    // Assigning to scrollLeft/scrollTop tells the browser to interrupt any running scroll animations,\n    // And to discard any pending async changes to the scroll position that may have happened in the meantime (e.g. on a separate scrolling thread).\n    // So we only set these when we require an adjustment of the scroll position.\n    // See issue #2 for more information.\n    if (\n      scrollPositionChangeReason === SCROLL_POSITION_CHANGE_REASONS.REQUESTED\n    ) {\n      // @TRICKY :autoHeight and :autoWidth properties instructs Grid to leave :scrollTop and :scrollLeft management to an external HOC (eg WindowScroller).\n      // In this case we should avoid checking scrollingContainer.scrollTop and scrollingContainer.scrollLeft since it forces layout/flow.\n      if (\n        !autoWidth &&\n        scrollLeft >= 0 &&\n        (scrollLeft !== this._scrollingContainer.scrollLeft ||\n          columnOrRowCountJustIncreasedFromZero)\n      ) {\n        this._scrollingContainer.scrollLeft = scrollLeft;\n      }\n      if (\n        !autoHeight &&\n        scrollTop >= 0 &&\n        (scrollTop !== this._scrollingContainer.scrollTop ||\n          columnOrRowCountJustIncreasedFromZero)\n      ) {\n        this._scrollingContainer.scrollTop = scrollTop;\n      }\n    }\n\n    // Special case where the previous size was 0:\n    // In this case we don't show any windowed cells at all.\n    // So we should always recalculate offset afterwards.\n    const sizeJustIncreasedFromZero =\n      (prevProps.width === 0 || prevProps.height === 0) &&\n      height > 0 &&\n      width > 0;\n\n    // Update scroll offsets if the current :scrollToColumn or :scrollToRow values requires it\n    // @TODO Do we also need this check or can the one in componentWillUpdate() suffice?\n    if (this._recomputeScrollLeftFlag) {\n      this._recomputeScrollLeftFlag = false;\n      this._updateScrollLeftForScrollToColumn(this.props);\n    } else {\n      updateScrollIndexHelper({\n        cellSizeAndPositionManager: instanceProps.columnSizeAndPositionManager,\n        previousCellsCount: prevProps.columnCount,\n        previousCellSize: prevProps.columnWidth,\n        previousScrollToAlignment: prevProps.scrollToAlignment,\n        previousScrollToIndex: prevProps.scrollToColumn,\n        previousSize: prevProps.width,\n        scrollOffset: scrollLeft,\n        scrollToAlignment,\n        scrollToIndex: scrollToColumn,\n        size: width,\n        sizeJustIncreasedFromZero,\n        updateScrollIndexCallback: () =>\n          this._updateScrollLeftForScrollToColumn(this.props),\n      });\n    }\n\n    if (this._recomputeScrollTopFlag) {\n      this._recomputeScrollTopFlag = false;\n      this._updateScrollTopForScrollToRow(this.props);\n    } else {\n      updateScrollIndexHelper({\n        cellSizeAndPositionManager: instanceProps.rowSizeAndPositionManager,\n        previousCellsCount: prevProps.rowCount,\n        previousCellSize: prevProps.rowHeight,\n        previousScrollToAlignment: prevProps.scrollToAlignment,\n        previousScrollToIndex: prevProps.scrollToRow,\n        previousSize: prevProps.height,\n        scrollOffset: scrollTop,\n        scrollToAlignment,\n        scrollToIndex: scrollToRow,\n        size: height,\n        sizeJustIncreasedFromZero,\n        updateScrollIndexCallback: () =>\n          this._updateScrollTopForScrollToRow(this.props),\n      });\n    }\n\n    // Update onRowsRendered callback if start/stop indices have changed\n    this._invokeOnGridRenderedHelper();\n\n    // Changes to :scrollLeft or :scrollTop should also notify :onScroll listeners\n    if (\n      scrollLeft !== prevState.scrollLeft ||\n      scrollTop !== prevState.scrollTop\n    ) {\n      const totalRowsHeight = instanceProps.rowSizeAndPositionManager.getTotalSize();\n      const totalColumnsWidth = instanceProps.columnSizeAndPositionManager.getTotalSize();\n\n      this._invokeOnScrollMemoizer({\n        scrollLeft,\n        scrollTop,\n        totalColumnsWidth,\n        totalRowsHeight,\n      });\n    }\n\n    this._maybeCallOnScrollbarPresenceChange();\n  }\n\n  componentWillUnmount() {\n    if (this._disablePointerEventsTimeoutId) {\n      cancelAnimationTimeout(this._disablePointerEventsTimeoutId);\n    }\n  }\n\n  /**\n   * This method updates scrollLeft/scrollTop in state for the following conditions:\n   * 1) Empty content (0 rows or columns)\n   * 2) New scroll props overriding the current state\n   * 3) Cells-count or cells-size has changed, making previous scroll offsets invalid\n   */\n  static getDerivedStateFromProps(\n    nextProps: Props,\n    prevState: State,\n  ): $Shape<State> {\n    const newState = {};\n\n    if (\n      (nextProps.columnCount === 0 && prevState.scrollLeft !== 0) ||\n      (nextProps.rowCount === 0 && prevState.scrollTop !== 0)\n    ) {\n      newState.scrollLeft = 0;\n      newState.scrollTop = 0;\n\n      // only use scroll{Left,Top} from props if scrollTo{Column,Row} isn't specified\n      // scrollTo{Column,Row} should override scroll{Left,Top}\n    } else if (\n      (nextProps.scrollLeft !== prevState.scrollLeft &&\n        nextProps.scrollToColumn < 0) ||\n      (nextProps.scrollTop !== prevState.scrollTop && nextProps.scrollToRow < 0)\n    ) {\n      Object.assign(\n        newState,\n        Grid._getScrollToPositionStateUpdate({\n          prevState,\n          scrollLeft: nextProps.scrollLeft,\n          scrollTop: nextProps.scrollTop,\n        }),\n      );\n    }\n\n    let {instanceProps} = prevState;\n\n    // Initially we should not clearStyleCache\n    newState.needToResetStyleCache = false;\n    if (\n      nextProps.columnWidth !== instanceProps.prevColumnWidth ||\n      nextProps.rowHeight !== instanceProps.prevRowHeight\n    ) {\n      // Reset cache. set it to {} in render\n      newState.needToResetStyleCache = true;\n    }\n\n    instanceProps.columnSizeAndPositionManager.configure({\n      cellCount: nextProps.columnCount,\n      estimatedCellSize: Grid._getEstimatedColumnSize(nextProps),\n      cellSizeGetter: Grid._wrapSizeGetter(nextProps.columnWidth),\n    });\n\n    instanceProps.rowSizeAndPositionManager.configure({\n      cellCount: nextProps.rowCount,\n      estimatedCellSize: Grid._getEstimatedRowSize(nextProps),\n      cellSizeGetter: Grid._wrapSizeGetter(nextProps.rowHeight),\n    });\n\n    if (\n      instanceProps.prevColumnCount === 0 ||\n      instanceProps.prevRowCount === 0\n    ) {\n      instanceProps.prevColumnCount = 0;\n      instanceProps.prevRowCount = 0;\n    }\n\n    // If scrolling is controlled outside this component, clear cache when scrolling stops\n    if (\n      nextProps.autoHeight &&\n      nextProps.isScrolling === false &&\n      instanceProps.prevIsScrolling === true\n    ) {\n      Object.assign(newState, {\n        isScrolling: false,\n      });\n    }\n\n    let maybeStateA;\n    let maybeStateB;\n\n    calculateSizeAndPositionDataAndUpdateScrollOffset({\n      cellCount: instanceProps.prevColumnCount,\n      cellSize:\n        typeof instanceProps.prevColumnWidth === 'number'\n          ? instanceProps.prevColumnWidth\n          : null,\n      computeMetadataCallback: () =>\n        instanceProps.columnSizeAndPositionManager.resetCell(0),\n      computeMetadataCallbackProps: nextProps,\n      nextCellsCount: nextProps.columnCount,\n      nextCellSize:\n        typeof nextProps.columnWidth === 'number'\n          ? nextProps.columnWidth\n          : null,\n      nextScrollToIndex: nextProps.scrollToColumn,\n      scrollToIndex: instanceProps.prevScrollToColumn,\n      updateScrollOffsetForScrollToIndex: () => {\n        maybeStateA = Grid._getScrollLeftForScrollToColumnStateUpdate(\n          nextProps,\n          prevState,\n        );\n      },\n    });\n    calculateSizeAndPositionDataAndUpdateScrollOffset({\n      cellCount: instanceProps.prevRowCount,\n      cellSize:\n        typeof instanceProps.prevRowHeight === 'number'\n          ? instanceProps.prevRowHeight\n          : null,\n      computeMetadataCallback: () =>\n        instanceProps.rowSizeAndPositionManager.resetCell(0),\n      computeMetadataCallbackProps: nextProps,\n      nextCellsCount: nextProps.rowCount,\n      nextCellSize:\n        typeof nextProps.rowHeight === 'number' ? nextProps.rowHeight : null,\n      nextScrollToIndex: nextProps.scrollToRow,\n      scrollToIndex: instanceProps.prevScrollToRow,\n      updateScrollOffsetForScrollToIndex: () => {\n        maybeStateB = Grid._getScrollTopForScrollToRowStateUpdate(\n          nextProps,\n          prevState,\n        );\n      },\n    });\n\n    instanceProps.prevColumnCount = nextProps.columnCount;\n    instanceProps.prevColumnWidth = nextProps.columnWidth;\n    instanceProps.prevIsScrolling = nextProps.isScrolling === true;\n    instanceProps.prevRowCount = nextProps.rowCount;\n    instanceProps.prevRowHeight = nextProps.rowHeight;\n    instanceProps.prevScrollToColumn = nextProps.scrollToColumn;\n    instanceProps.prevScrollToRow = nextProps.scrollToRow;\n\n    // getting scrollBarSize (moved from componentWillMount)\n    instanceProps.scrollbarSize = nextProps.getScrollbarSize();\n    if (instanceProps.scrollbarSize === undefined) {\n      instanceProps.scrollbarSizeMeasured = false;\n      instanceProps.scrollbarSize = 0;\n    } else {\n      instanceProps.scrollbarSizeMeasured = true;\n    }\n\n    newState.instanceProps = instanceProps;\n\n    return {...newState, ...maybeStateA, ...maybeStateB};\n  }\n\n  render() {\n    const {\n      autoContainerWidth,\n      autoHeight,\n      autoWidth,\n      className,\n      containerProps,\n      containerRole,\n      containerStyle,\n      height,\n      id,\n      noContentRenderer,\n      role,\n      style,\n      tabIndex,\n      width,\n    } = this.props;\n    const {instanceProps, needToResetStyleCache} = this.state;\n\n    const isScrolling = this._isScrolling();\n\n    const gridStyle: Object = {\n      boxSizing: 'border-box',\n      direction: 'ltr',\n      height: autoHeight ? 'auto' : height,\n      position: 'relative',\n      width: autoWidth ? 'auto' : width,\n      WebkitOverflowScrolling: 'touch',\n      willChange: 'transform',\n    };\n\n    if (needToResetStyleCache) {\n      this._styleCache = {};\n    }\n\n    // calculate _styleCache here\n    // if state.isScrolling (not from _isScrolling) then reset\n    if (!this.state.isScrolling) {\n      this._resetStyleCache();\n    }\n\n    // calculate children to render here\n    this._calculateChildrenToRender(this.props, this.state);\n\n    const totalColumnsWidth = instanceProps.columnSizeAndPositionManager.getTotalSize();\n    const totalRowsHeight = instanceProps.rowSizeAndPositionManager.getTotalSize();\n\n    // Force browser to hide scrollbars when we know they aren't necessary.\n    // Otherwise once scrollbars appear they may not disappear again.\n    // For more info see issue #116\n    const verticalScrollBarSize =\n      totalRowsHeight > height ? instanceProps.scrollbarSize : 0;\n    const horizontalScrollBarSize =\n      totalColumnsWidth > width ? instanceProps.scrollbarSize : 0;\n\n    if (\n      horizontalScrollBarSize !== this._horizontalScrollBarSize ||\n      verticalScrollBarSize !== this._verticalScrollBarSize\n    ) {\n      this._horizontalScrollBarSize = horizontalScrollBarSize;\n      this._verticalScrollBarSize = verticalScrollBarSize;\n      this._scrollbarPresenceChanged = true;\n    }\n\n    // Also explicitly init styles to 'auto' if scrollbars are required.\n    // This works around an obscure edge case where external CSS styles have not yet been loaded,\n    // But an initial scroll index of offset is set as an external prop.\n    // Without this style, Grid would render the correct range of cells but would NOT update its internal offset.\n    // This was originally reported via clauderic/react-infinite-calendar/issues/23\n    gridStyle.overflowX =\n      totalColumnsWidth + verticalScrollBarSize <= width ? 'hidden' : 'auto';\n    gridStyle.overflowY =\n      totalRowsHeight + horizontalScrollBarSize <= height ? 'hidden' : 'auto';\n\n    const childrenToDisplay = this._childrenToDisplay;\n\n    const showNoContentRenderer =\n      childrenToDisplay.length === 0 && height > 0 && width > 0;\n\n    return (\n      <div\n        ref={this._setScrollingContainerRef}\n        {...containerProps}\n        aria-label={this.props['aria-label']}\n        aria-readonly={this.props['aria-readonly']}\n        className={clsx('ReactVirtualized__Grid', className)}\n        id={id}\n        onScroll={this._onScroll}\n        role={role}\n        style={{\n          ...gridStyle,\n          ...style,\n        }}\n        tabIndex={tabIndex}>\n        {childrenToDisplay.length > 0 && (\n          <div\n            className=\"ReactVirtualized__Grid__innerScrollContainer\"\n            role={containerRole}\n            style={{\n              width: autoContainerWidth ? 'auto' : totalColumnsWidth,\n              height: totalRowsHeight,\n              maxWidth: totalColumnsWidth,\n              maxHeight: totalRowsHeight,\n              overflow: 'hidden',\n              pointerEvents: isScrolling ? 'none' : '',\n              position: 'relative',\n              ...containerStyle,\n            }}>\n            {childrenToDisplay}\n          </div>\n        )}\n        {showNoContentRenderer && noContentRenderer()}\n      </div>\n    );\n  }\n\n  /* ---------------------------- Helper methods ---------------------------- */\n\n  _calculateChildrenToRender(\n    props: Props = this.props,\n    state: State = this.state,\n  ) {\n    const {\n      cellRenderer,\n      cellRangeRenderer,\n      columnCount,\n      deferredMeasurementCache,\n      height,\n      overscanColumnCount,\n      overscanIndicesGetter,\n      overscanRowCount,\n      rowCount,\n      width,\n      isScrollingOptOut,\n    } = props;\n\n    const {\n      scrollDirectionHorizontal,\n      scrollDirectionVertical,\n      instanceProps,\n    } = state;\n\n    const scrollTop =\n      this._initialScrollTop > 0 ? this._initialScrollTop : state.scrollTop;\n    const scrollLeft =\n      this._initialScrollLeft > 0 ? this._initialScrollLeft : state.scrollLeft;\n\n    const isScrolling = this._isScrolling(props, state);\n\n    this._childrenToDisplay = [];\n\n    // Render only enough columns and rows to cover the visible area of the grid.\n    if (height > 0 && width > 0) {\n      const visibleColumnIndices = instanceProps.columnSizeAndPositionManager.getVisibleCellRange(\n        {\n          containerSize: width,\n          offset: scrollLeft,\n        },\n      );\n      const visibleRowIndices = instanceProps.rowSizeAndPositionManager.getVisibleCellRange(\n        {\n          containerSize: height,\n          offset: scrollTop,\n        },\n      );\n\n      const horizontalOffsetAdjustment = instanceProps.columnSizeAndPositionManager.getOffsetAdjustment(\n        {\n          containerSize: width,\n          offset: scrollLeft,\n        },\n      );\n      const verticalOffsetAdjustment = instanceProps.rowSizeAndPositionManager.getOffsetAdjustment(\n        {\n          containerSize: height,\n          offset: scrollTop,\n        },\n      );\n\n      // Store for _invokeOnGridRenderedHelper()\n      this._renderedColumnStartIndex = visibleColumnIndices.start;\n      this._renderedColumnStopIndex = visibleColumnIndices.stop;\n      this._renderedRowStartIndex = visibleRowIndices.start;\n      this._renderedRowStopIndex = visibleRowIndices.stop;\n\n      const overscanColumnIndices = overscanIndicesGetter({\n        direction: 'horizontal',\n        cellCount: columnCount,\n        overscanCellsCount: overscanColumnCount,\n        scrollDirection: scrollDirectionHorizontal,\n        startIndex:\n          typeof visibleColumnIndices.start === 'number'\n            ? visibleColumnIndices.start\n            : 0,\n        stopIndex:\n          typeof visibleColumnIndices.stop === 'number'\n            ? visibleColumnIndices.stop\n            : -1,\n      });\n\n      const overscanRowIndices = overscanIndicesGetter({\n        direction: 'vertical',\n        cellCount: rowCount,\n        overscanCellsCount: overscanRowCount,\n        scrollDirection: scrollDirectionVertical,\n        startIndex:\n          typeof visibleRowIndices.start === 'number'\n            ? visibleRowIndices.start\n            : 0,\n        stopIndex:\n          typeof visibleRowIndices.stop === 'number'\n            ? visibleRowIndices.stop\n            : -1,\n      });\n\n      // Store for _invokeOnGridRenderedHelper()\n      let columnStartIndex = overscanColumnIndices.overscanStartIndex;\n      let columnStopIndex = overscanColumnIndices.overscanStopIndex;\n      let rowStartIndex = overscanRowIndices.overscanStartIndex;\n      let rowStopIndex = overscanRowIndices.overscanStopIndex;\n\n      // Advanced use-cases (eg CellMeasurer) require batched measurements to determine accurate sizes.\n      if (deferredMeasurementCache) {\n        // If rows have a dynamic height, scan the rows we are about to render.\n        // If any have not yet been measured, then we need to render all columns initially,\n        // Because the height of the row is equal to the tallest cell within that row,\n        // (And so we can't know the height without measuring all column-cells first).\n        if (!deferredMeasurementCache.hasFixedHeight()) {\n          for (\n            let rowIndex = rowStartIndex;\n            rowIndex <= rowStopIndex;\n            rowIndex++\n          ) {\n            if (!deferredMeasurementCache.has(rowIndex, 0)) {\n              columnStartIndex = 0;\n              columnStopIndex = columnCount - 1;\n              break;\n            }\n          }\n        }\n\n        // If columns have a dynamic width, scan the columns we are about to render.\n        // If any have not yet been measured, then we need to render all rows initially,\n        // Because the width of the column is equal to the widest cell within that column,\n        // (And so we can't know the width without measuring all row-cells first).\n        if (!deferredMeasurementCache.hasFixedWidth()) {\n          for (\n            let columnIndex = columnStartIndex;\n            columnIndex <= columnStopIndex;\n            columnIndex++\n          ) {\n            if (!deferredMeasurementCache.has(0, columnIndex)) {\n              rowStartIndex = 0;\n              rowStopIndex = rowCount - 1;\n              break;\n            }\n          }\n        }\n      }\n\n      this._childrenToDisplay = cellRangeRenderer({\n        cellCache: this._cellCache,\n        cellRenderer,\n        columnSizeAndPositionManager:\n          instanceProps.columnSizeAndPositionManager,\n        columnStartIndex,\n        columnStopIndex,\n        deferredMeasurementCache,\n        horizontalOffsetAdjustment,\n        isScrolling,\n        isScrollingOptOut,\n        parent: this,\n        rowSizeAndPositionManager: instanceProps.rowSizeAndPositionManager,\n        rowStartIndex,\n        rowStopIndex,\n        scrollLeft,\n        scrollTop,\n        styleCache: this._styleCache,\n        verticalOffsetAdjustment,\n        visibleColumnIndices,\n        visibleRowIndices,\n      });\n\n      // update the indices\n      this._columnStartIndex = columnStartIndex;\n      this._columnStopIndex = columnStopIndex;\n      this._rowStartIndex = rowStartIndex;\n      this._rowStopIndex = rowStopIndex;\n    }\n  }\n\n  /**\n   * Sets an :isScrolling flag for a small window of time.\n   * This flag is used to disable pointer events on the scrollable portion of the Grid.\n   * This prevents jerky/stuttery mouse-wheel scrolling.\n   */\n  _debounceScrollEnded() {\n    const {scrollingResetTimeInterval} = this.props;\n\n    if (this._disablePointerEventsTimeoutId) {\n      cancelAnimationTimeout(this._disablePointerEventsTimeoutId);\n    }\n\n    this._disablePointerEventsTimeoutId = requestAnimationTimeout(\n      this._debounceScrollEndedCallback,\n      scrollingResetTimeInterval,\n    );\n  }\n\n  _debounceScrollEndedCallback = () => {\n    this._disablePointerEventsTimeoutId = null;\n    // isScrolling is used to determine if we reset styleCache\n    this.setState({\n      isScrolling: false,\n      needToResetStyleCache: false,\n    });\n  };\n\n  static _getEstimatedColumnSize(props: Props) {\n    return typeof props.columnWidth === 'number'\n      ? props.columnWidth\n      : props.estimatedColumnSize;\n  }\n\n  static _getEstimatedRowSize(props: Props) {\n    return typeof props.rowHeight === 'number'\n      ? props.rowHeight\n      : props.estimatedRowSize;\n  }\n\n  /**\n   * Check for batched CellMeasurer size invalidations.\n   * This will occur the first time one or more previously unmeasured cells are rendered.\n   */\n  _handleInvalidatedGridSize() {\n    if (\n      typeof this._deferredInvalidateColumnIndex === 'number' &&\n      typeof this._deferredInvalidateRowIndex === 'number'\n    ) {\n      const columnIndex = this._deferredInvalidateColumnIndex;\n      const rowIndex = this._deferredInvalidateRowIndex;\n\n      this._deferredInvalidateColumnIndex = null;\n      this._deferredInvalidateRowIndex = null;\n\n      this.recomputeGridSize({columnIndex, rowIndex});\n    }\n  }\n\n  _invokeOnGridRenderedHelper = () => {\n    const {onSectionRendered} = this.props;\n\n    this._onGridRenderedMemoizer({\n      callback: onSectionRendered,\n      indices: {\n        columnOverscanStartIndex: this._columnStartIndex,\n        columnOverscanStopIndex: this._columnStopIndex,\n        columnStartIndex: this._renderedColumnStartIndex,\n        columnStopIndex: this._renderedColumnStopIndex,\n        rowOverscanStartIndex: this._rowStartIndex,\n        rowOverscanStopIndex: this._rowStopIndex,\n        rowStartIndex: this._renderedRowStartIndex,\n        rowStopIndex: this._renderedRowStopIndex,\n      },\n    });\n  };\n\n  _invokeOnScrollMemoizer({\n    scrollLeft,\n    scrollTop,\n    totalColumnsWidth,\n    totalRowsHeight,\n  }: {\n    scrollLeft: number,\n    scrollTop: number,\n    totalColumnsWidth: number,\n    totalRowsHeight: number,\n  }) {\n    this._onScrollMemoizer({\n      callback: ({scrollLeft, scrollTop}) => {\n        const {height, onScroll, width} = this.props;\n\n        onScroll({\n          clientHeight: height,\n          clientWidth: width,\n          scrollHeight: totalRowsHeight,\n          scrollLeft,\n          scrollTop,\n          scrollWidth: totalColumnsWidth,\n        });\n      },\n      indices: {\n        scrollLeft,\n        scrollTop,\n      },\n    });\n  }\n\n  _isScrolling(props: Props = this.props, state: State = this.state): boolean {\n    // If isScrolling is defined in props, use it to override the value in state\n    // This is a performance optimization for WindowScroller + Grid\n    return Object.hasOwnProperty.call(props, 'isScrolling')\n      ? Boolean(props.isScrolling)\n      : Boolean(state.isScrolling);\n  }\n\n  _maybeCallOnScrollbarPresenceChange() {\n    if (this._scrollbarPresenceChanged) {\n      const {onScrollbarPresenceChange} = this.props;\n\n      this._scrollbarPresenceChanged = false;\n\n      onScrollbarPresenceChange({\n        horizontal: this._horizontalScrollBarSize > 0,\n        size: this.state.instanceProps.scrollbarSize,\n        vertical: this._verticalScrollBarSize > 0,\n      });\n    }\n  }\n\n  _setScrollingContainerRef = (ref: Element) => {\n    this._scrollingContainer = ref;\n\n    if (typeof this.props.elementRef === 'function') {\n      this.props.elementRef(ref);\n    } else if (typeof this.props.elementRef === 'object') {\n      this.props.elementRef.current = ref;\n    }\n  };\n\n  /**\n   * Get the updated state after scrolling to\n   * scrollLeft and scrollTop\n   */\n  static _getScrollToPositionStateUpdate({\n    prevState,\n    scrollLeft,\n    scrollTop,\n  }: {\n    prevState: State,\n    scrollLeft?: number,\n    scrollTop?: number,\n  }): $Shape<State> {\n    const newState: Object = {\n      scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED,\n    };\n\n    if (typeof scrollLeft === 'number' && scrollLeft >= 0) {\n      newState.scrollDirectionHorizontal =\n        scrollLeft > prevState.scrollLeft\n          ? SCROLL_DIRECTION_FORWARD\n          : SCROLL_DIRECTION_BACKWARD;\n      newState.scrollLeft = scrollLeft;\n    }\n\n    if (typeof scrollTop === 'number' && scrollTop >= 0) {\n      newState.scrollDirectionVertical =\n        scrollTop > prevState.scrollTop\n          ? SCROLL_DIRECTION_FORWARD\n          : SCROLL_DIRECTION_BACKWARD;\n      newState.scrollTop = scrollTop;\n    }\n\n    if (\n      (typeof scrollLeft === 'number' &&\n        scrollLeft >= 0 &&\n        scrollLeft !== prevState.scrollLeft) ||\n      (typeof scrollTop === 'number' &&\n        scrollTop >= 0 &&\n        scrollTop !== prevState.scrollTop)\n    ) {\n      return newState;\n    }\n    return {};\n  }\n\n  /**\n   * Scroll to the specified offset(s).\n   * Useful for animating position changes.\n   */\n  scrollToPosition({scrollLeft, scrollTop}: ScrollPosition) {\n    const stateUpdate = Grid._getScrollToPositionStateUpdate({\n      prevState: this.state,\n      scrollLeft,\n      scrollTop,\n    });\n\n    if (stateUpdate) {\n      stateUpdate.needToResetStyleCache = false;\n      this.setState(stateUpdate);\n    }\n  }\n\n  static _wrapSizeGetter(value: CellSize): CellSizeGetter {\n    return typeof value === 'function' ? value : () => (value: any);\n  }\n\n  static _getCalculatedScrollLeft(nextProps: Props, prevState: State) {\n    const {\n      columnCount,\n      height,\n      scrollToAlignment,\n      scrollToColumn,\n      width,\n    } = nextProps;\n    const {scrollLeft, instanceProps} = prevState;\n\n    if (columnCount > 0) {\n      const finalColumn = columnCount - 1;\n      const targetIndex =\n        scrollToColumn < 0\n          ? finalColumn\n          : Math.min(finalColumn, scrollToColumn);\n      const totalRowsHeight = instanceProps.rowSizeAndPositionManager.getTotalSize();\n      const scrollBarSize =\n        instanceProps.scrollbarSizeMeasured && totalRowsHeight > height\n          ? instanceProps.scrollbarSize\n          : 0;\n\n      return instanceProps.columnSizeAndPositionManager.getUpdatedOffsetForIndex(\n        {\n          align: scrollToAlignment,\n          containerSize: width - scrollBarSize,\n          currentOffset: scrollLeft,\n          targetIndex,\n        },\n      );\n    }\n    return 0;\n  }\n\n  _getCalculatedScrollLeft(\n    props: Props = this.props,\n    state: State = this.state,\n  ) {\n    return Grid._getCalculatedScrollLeft(props, state);\n  }\n\n  static _getScrollLeftForScrollToColumnStateUpdate(\n    nextProps: Props,\n    prevState: State,\n  ): $Shape<State> {\n    const {scrollLeft} = prevState;\n    const calculatedScrollLeft = Grid._getCalculatedScrollLeft(\n      nextProps,\n      prevState,\n    );\n\n    if (\n      typeof calculatedScrollLeft === 'number' &&\n      calculatedScrollLeft >= 0 &&\n      scrollLeft !== calculatedScrollLeft\n    ) {\n      return Grid._getScrollToPositionStateUpdate({\n        prevState,\n        scrollLeft: calculatedScrollLeft,\n        scrollTop: -1,\n      });\n    }\n    return {};\n  }\n\n  _updateScrollLeftForScrollToColumn(\n    props: Props = this.props,\n    state: State = this.state,\n  ) {\n    const stateUpdate = Grid._getScrollLeftForScrollToColumnStateUpdate(\n      props,\n      state,\n    );\n    if (stateUpdate) {\n      stateUpdate.needToResetStyleCache = false;\n      this.setState(stateUpdate);\n    }\n  }\n\n  static _getCalculatedScrollTop(nextProps: Props, prevState: State) {\n    const {height, rowCount, scrollToAlignment, scrollToRow, width} = nextProps;\n    const {scrollTop, instanceProps} = prevState;\n\n    if (rowCount > 0) {\n      const finalRow = rowCount - 1;\n      const targetIndex =\n        scrollToRow < 0 ? finalRow : Math.min(finalRow, scrollToRow);\n      const totalColumnsWidth = instanceProps.columnSizeAndPositionManager.getTotalSize();\n      const scrollBarSize =\n        instanceProps.scrollbarSizeMeasured && totalColumnsWidth > width\n          ? instanceProps.scrollbarSize\n          : 0;\n\n      return instanceProps.rowSizeAndPositionManager.getUpdatedOffsetForIndex({\n        align: scrollToAlignment,\n        containerSize: height - scrollBarSize,\n        currentOffset: scrollTop,\n        targetIndex,\n      });\n    }\n    return 0;\n  }\n\n  _getCalculatedScrollTop(\n    props: Props = this.props,\n    state: State = this.state,\n  ) {\n    return Grid._getCalculatedScrollTop(props, state);\n  }\n\n  _resetStyleCache() {\n    const styleCache = this._styleCache;\n    const cellCache = this._cellCache;\n    const {isScrollingOptOut} = this.props;\n\n    // Reset cell and style caches once scrolling stops.\n    // This makes Grid simpler to use (since cells commonly change).\n    // And it keeps the caches from growing too large.\n    // Performance is most sensitive when a user is scrolling.\n    // Don't clear visible cells from cellCache if isScrollingOptOut is specified.\n    // This keeps the cellCache to a resonable size.\n    this._cellCache = {};\n    this._styleCache = {};\n\n    // Copy over the visible cell styles so avoid unnecessary re-render.\n    for (\n      let rowIndex = this._rowStartIndex;\n      rowIndex <= this._rowStopIndex;\n      rowIndex++\n    ) {\n      for (\n        let columnIndex = this._columnStartIndex;\n        columnIndex <= this._columnStopIndex;\n        columnIndex++\n      ) {\n        let key = `${rowIndex}-${columnIndex}`;\n        this._styleCache[key] = styleCache[key];\n\n        if (isScrollingOptOut) {\n          this._cellCache[key] = cellCache[key];\n        }\n      }\n    }\n  }\n\n  static _getScrollTopForScrollToRowStateUpdate(\n    nextProps: Props,\n    prevState: State,\n  ): $Shape<State> {\n    const {scrollTop} = prevState;\n    const calculatedScrollTop = Grid._getCalculatedScrollTop(\n      nextProps,\n      prevState,\n    );\n\n    if (\n      typeof calculatedScrollTop === 'number' &&\n      calculatedScrollTop >= 0 &&\n      scrollTop !== calculatedScrollTop\n    ) {\n      return Grid._getScrollToPositionStateUpdate({\n        prevState,\n        scrollLeft: -1,\n        scrollTop: calculatedScrollTop,\n      });\n    }\n    return {};\n  }\n\n  _updateScrollTopForScrollToRow(\n    props: Props = this.props,\n    state: State = this.state,\n  ) {\n    const stateUpdate = Grid._getScrollTopForScrollToRowStateUpdate(\n      props,\n      state,\n    );\n    if (stateUpdate) {\n      stateUpdate.needToResetStyleCache = false;\n      this.setState(stateUpdate);\n    }\n  }\n\n  _onScroll = (event: Event) => {\n    // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollLeft / target.scrollTop.\n    // This invalid event can be detected by comparing event.target to this component's scrollable DOM element.\n    // See issue #404 for more information.\n    if (event.target === this._scrollingContainer) {\n      this.handleScrollEvent((event.target: any));\n    }\n  };\n}\n\npolyfill(Grid);\nexport default Grid;\n"
  },
  {
    "path": "source/Grid/Grid.ssr.js",
    "content": "/**\n * @flow\n * @jest-environment node\n */\n\nimport * as React from 'react';\nimport * as ReactDOMServer from 'react-dom/server';\nimport Grid from './Grid';\n\ndeclare var test: any;\ndeclare var expect: any;\n\ntest('should render Grid with dom server', () => {\n  const rendered = ReactDOMServer.renderToString(\n    <Grid\n      cellRenderer={({style, key, rowIndex, columnIndex}) => (\n        <div style={style} key={key}>\n          {rowIndex + ':' + columnIndex}\n        </div>\n      )}\n      columnCount={1000}\n      columnWidth={20}\n      height={500}\n      rowCount={1000}\n      rowHeight={20}\n      width={500}\n    />,\n  );\n\n  expect(rendered).toContain('0:0');\n  expect(rendered).toContain('24:24');\n  expect(rendered).not.toContain('25:25');\n});\n\ntest('should support :scrollToColumn and :scrollToRow in server render', () => {\n  const rendered = ReactDOMServer.renderToString(\n    <Grid\n      cellRenderer={({style, key, rowIndex, columnIndex}) => (\n        <div style={style} key={key}>\n          {rowIndex + ':' + columnIndex}\n        </div>\n      )}\n      columnCount={1000}\n      columnWidth={20}\n      scrollToColumn={250}\n      height={500}\n      rowCount={1000}\n      rowHeight={20}\n      scrollToRow={250}\n      width={500}\n    />,\n  );\n\n  expect(rendered).toContain('250:250');\n  expect(rendered).not.toContain('0:0');\n});\n"
  },
  {
    "path": "source/Grid/accessibilityOverscanIndicesGetter.jest.js",
    "content": "import overscanIndicesGetter, {\n  SCROLL_DIRECTION_BACKWARD,\n  SCROLL_DIRECTION_FORWARD,\n} from './accessibilityOverscanIndicesGetter';\n\ndescribe('overscanIndicesGetter', () => {\n  function testHelper({\n    cellCount,\n    startIndex,\n    stopIndex,\n    overscanCellsCount,\n    scrollDirection,\n  }) {\n    return overscanIndicesGetter({\n      cellCount,\n      overscanCellsCount,\n      scrollDirection,\n      startIndex,\n      stopIndex,\n    });\n  }\n\n  it('should still overscan by 1 (for keyboard accessibility) if :overscanCellsCount is 0', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 10,\n        stopIndex: 20,\n        overscanCellsCount: 0,\n        scrollDirection: SCROLL_DIRECTION_BACKWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 9,\n      overscanStopIndex: 21,\n    });\n\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 10,\n        stopIndex: 20,\n        overscanCellsCount: 0,\n        scrollDirection: SCROLL_DIRECTION_FORWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 9,\n      overscanStopIndex: 21,\n    });\n  });\n\n  it('should overscan forward', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 20,\n        stopIndex: 30,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_FORWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 19,\n      overscanStopIndex: 40,\n    });\n  });\n\n  it('should overscan backward', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 20,\n        stopIndex: 30,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_BACKWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 10,\n      overscanStopIndex: 31,\n    });\n  });\n\n  it('should not overscan beyond the start of the list', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 5,\n        stopIndex: 15,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_BACKWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 0,\n      overscanStopIndex: 16,\n    });\n  });\n\n  it('should not overscan beyond the end of the list', () => {\n    expect(\n      testHelper({\n        cellCount: 25,\n        startIndex: 10,\n        stopIndex: 20,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_FORWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 9,\n      overscanStopIndex: 24,\n    });\n  });\n});\n"
  },
  {
    "path": "source/Grid/accessibilityOverscanIndicesGetter.js",
    "content": "// @flow\n\nimport type {OverscanIndicesGetterParams, OverscanIndices} from './types';\n\nexport const SCROLL_DIRECTION_BACKWARD = -1;\nexport const SCROLL_DIRECTION_FORWARD = 1;\n\nexport const SCROLL_DIRECTION_HORIZONTAL = 'horizontal';\nexport const SCROLL_DIRECTION_VERTICAL = 'vertical';\n\n/**\n * Calculates the number of cells to overscan before and after a specified range.\n * This function ensures that overscanning doesn't exceed the available cells.\n */\n\nexport default function defaultOverscanIndicesGetter({\n  cellCount,\n  overscanCellsCount,\n  scrollDirection,\n  startIndex,\n  stopIndex,\n}: OverscanIndicesGetterParams): OverscanIndices {\n  // Make sure we render at least 1 cell extra before and after (except near boundaries)\n  // This is necessary in order to support keyboard navigation (TAB/SHIFT+TAB) in some cases\n  // For more info see issues #625\n  overscanCellsCount = Math.max(1, overscanCellsCount);\n\n  if (scrollDirection === SCROLL_DIRECTION_FORWARD) {\n    return {\n      overscanStartIndex: Math.max(0, startIndex - 1),\n      overscanStopIndex: Math.min(\n        cellCount - 1,\n        stopIndex + overscanCellsCount,\n      ),\n    };\n  } else {\n    return {\n      overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),\n      overscanStopIndex: Math.min(cellCount - 1, stopIndex + 1),\n    };\n  }\n}\n"
  },
  {
    "path": "source/Grid/defaultCellRangeRenderer.js",
    "content": "/** @flow */\n\nimport type {CellRangeRendererParams} from './types';\nimport React from 'react';\n\n/**\n * Default implementation of cellRangeRenderer used by Grid.\n * This renderer supports cell-caching while the user is scrolling.\n */\n\nexport default function defaultCellRangeRenderer({\n  cellCache,\n  cellRenderer,\n  columnSizeAndPositionManager,\n  columnStartIndex,\n  columnStopIndex,\n  deferredMeasurementCache,\n  horizontalOffsetAdjustment,\n  isScrolling,\n  isScrollingOptOut,\n  parent, // Grid (or List or Table)\n  rowSizeAndPositionManager,\n  rowStartIndex,\n  rowStopIndex,\n  styleCache,\n  verticalOffsetAdjustment,\n  visibleColumnIndices,\n  visibleRowIndices,\n}: CellRangeRendererParams) {\n  const renderedCells = [];\n\n  // Browsers have native size limits for elements (eg Chrome 33M pixels, IE 1.5M pixes).\n  // User cannot scroll beyond these size limitations.\n  // In order to work around this, ScalingCellSizeAndPositionManager compresses offsets.\n  // We should never cache styles for compressed offsets though as this can lead to bugs.\n  // See issue #576 for more.\n  const areOffsetsAdjusted =\n    columnSizeAndPositionManager.areOffsetsAdjusted() ||\n    rowSizeAndPositionManager.areOffsetsAdjusted();\n\n  const canCacheStyle = !isScrolling && !areOffsetsAdjusted;\n\n  for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {\n    let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex);\n\n    for (\n      let columnIndex = columnStartIndex;\n      columnIndex <= columnStopIndex;\n      columnIndex++\n    ) {\n      let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(\n        columnIndex,\n      );\n      let isVisible =\n        columnIndex >= visibleColumnIndices.start &&\n        columnIndex <= visibleColumnIndices.stop &&\n        rowIndex >= visibleRowIndices.start &&\n        rowIndex <= visibleRowIndices.stop;\n      let key = `${rowIndex}-${columnIndex}`;\n      let style;\n\n      // Cache style objects so shallow-compare doesn't re-render unnecessarily.\n      if (canCacheStyle && styleCache[key]) {\n        style = styleCache[key];\n      } else {\n        // In deferred mode, cells will be initially rendered before we know their size.\n        // Don't interfere with CellMeasurer's measurements by setting an invalid size.\n        if (\n          deferredMeasurementCache &&\n          !deferredMeasurementCache.has(rowIndex, columnIndex)\n        ) {\n          // Position not-yet-measured cells at top/left 0,0,\n          // And give them width/height of 'auto' so they can grow larger than the parent Grid if necessary.\n          // Positioning them further to the right/bottom influences their measured size.\n          style = {\n            height: 'auto',\n            left: 0,\n            position: 'absolute',\n            top: 0,\n            width: 'auto',\n          };\n        } else {\n          style = {\n            height: rowDatum.size,\n            left: columnDatum.offset + horizontalOffsetAdjustment,\n            position: 'absolute',\n            top: rowDatum.offset + verticalOffsetAdjustment,\n            width: columnDatum.size,\n          };\n\n          styleCache[key] = style;\n        }\n      }\n\n      let cellRendererParams = {\n        columnIndex,\n        isScrolling,\n        isVisible,\n        key,\n        parent,\n        rowIndex,\n        style,\n      };\n\n      let renderedCell;\n\n      // Avoid re-creating cells while scrolling.\n      // This can lead to the same cell being created many times and can cause performance issues for \"heavy\" cells.\n      // If a scroll is in progress- cache and reuse cells.\n      // This cache will be thrown away once scrolling completes.\n      // However if we are scaling scroll positions and sizes, we should also avoid caching.\n      // This is because the offset changes slightly as scroll position changes and caching leads to stale values.\n      // For more info refer to issue #395\n      //\n      // If isScrollingOptOut is specified, we always cache cells.\n      // For more info refer to issue #1028\n      if (\n        (isScrollingOptOut || isScrolling) &&\n        !horizontalOffsetAdjustment &&\n        !verticalOffsetAdjustment\n      ) {\n        if (!cellCache[key]) {\n          cellCache[key] = cellRenderer(cellRendererParams);\n        }\n\n        renderedCell = cellCache[key];\n\n        // If the user is no longer scrolling, don't cache cells.\n        // This makes dynamic cell content difficult for users and would also lead to a heavier memory footprint.\n      } else {\n        renderedCell = cellRenderer(cellRendererParams);\n      }\n\n      if (renderedCell == null || renderedCell === false) {\n        continue;\n      }\n\n      if (process.env.NODE_ENV !== 'production') {\n        warnAboutMissingStyle(parent, renderedCell);\n      }\n\n      if (!renderedCell.props.role) {\n        renderedCell = React.cloneElement(renderedCell, {role: 'gridcell'});\n      }\n\n      renderedCells.push(renderedCell);\n    }\n  }\n\n  return renderedCells;\n}\n\nfunction warnAboutMissingStyle(parent, renderedCell) {\n  if (process.env.NODE_ENV !== 'production') {\n    if (renderedCell) {\n      // If the direct child is a CellMeasurer, then we should check its child\n      // See issue #611\n      if (renderedCell.type && renderedCell.type.__internalCellMeasurerFlag) {\n        renderedCell = renderedCell.props.children;\n      }\n\n      if (\n        renderedCell &&\n        renderedCell.props &&\n        renderedCell.props.style === undefined &&\n        parent.__warnedAboutMissingStyle !== true\n      ) {\n        parent.__warnedAboutMissingStyle = true;\n\n        console.warn(\n          'Rendered cell should include style property for positioning.',\n        );\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "source/Grid/defaultOverscanIndicesGetter.jest.js",
    "content": "import overscanIndicesGetter, {\n  SCROLL_DIRECTION_BACKWARD,\n  SCROLL_DIRECTION_FORWARD,\n} from './defaultOverscanIndicesGetter';\n\ndescribe('overscanIndicesGetter', () => {\n  function testHelper({\n    cellCount,\n    startIndex,\n    stopIndex,\n    overscanCellsCount,\n    scrollDirection,\n  }) {\n    return overscanIndicesGetter({\n      cellCount,\n      overscanCellsCount,\n      scrollDirection,\n      startIndex,\n      stopIndex,\n    });\n  }\n\n  it('should not overscan if :overscanCellsCount is 0', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 10,\n        stopIndex: 20,\n        overscanCellsCount: 0,\n        scrollDirection: SCROLL_DIRECTION_BACKWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 10,\n      overscanStopIndex: 20,\n    });\n\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 10,\n        stopIndex: 20,\n        overscanCellsCount: 0,\n        scrollDirection: SCROLL_DIRECTION_FORWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 10,\n      overscanStopIndex: 20,\n    });\n  });\n\n  it('should overscan forward', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 20,\n        stopIndex: 30,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_FORWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 20,\n      overscanStopIndex: 40,\n    });\n  });\n\n  it('should overscan backward', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 20,\n        stopIndex: 30,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_BACKWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 10,\n      overscanStopIndex: 30,\n    });\n  });\n\n  it('should not overscan beyond the start of the list', () => {\n    expect(\n      testHelper({\n        cellCount: 100,\n        startIndex: 5,\n        stopIndex: 15,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_BACKWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 0,\n      overscanStopIndex: 15,\n    });\n  });\n\n  it('should not overscan beyond the end of the list', () => {\n    expect(\n      testHelper({\n        cellCount: 25,\n        startIndex: 10,\n        stopIndex: 20,\n        overscanCellsCount: 10,\n        scrollDirection: SCROLL_DIRECTION_FORWARD,\n      }),\n    ).toEqual({\n      overscanStartIndex: 10,\n      overscanStopIndex: 24,\n    });\n  });\n});\n"
  },
  {
    "path": "source/Grid/defaultOverscanIndicesGetter.js",
    "content": "// @flow\n\nimport type {OverscanIndicesGetterParams, OverscanIndices} from './types';\n\nexport const SCROLL_DIRECTION_BACKWARD = -1;\nexport const SCROLL_DIRECTION_FORWARD = 1;\n\nexport const SCROLL_DIRECTION_HORIZONTAL = 'horizontal';\nexport const SCROLL_DIRECTION_VERTICAL = 'vertical';\n\n/**\n * Calculates the number of cells to overscan before and after a specified range.\n * This function ensures that overscanning doesn't exceed the available cells.\n */\n\nexport default function defaultOverscanIndicesGetter({\n  cellCount,\n  overscanCellsCount,\n  scrollDirection,\n  startIndex,\n  stopIndex,\n}: OverscanIndicesGetterParams): OverscanIndices {\n  if (scrollDirection === SCROLL_DIRECTION_FORWARD) {\n    return {\n      overscanStartIndex: Math.max(0, startIndex),\n      overscanStopIndex: Math.min(\n        cellCount - 1,\n        stopIndex + overscanCellsCount,\n      ),\n    };\n  } else {\n    return {\n      overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),\n      overscanStopIndex: Math.min(cellCount - 1, stopIndex),\n    };\n  }\n}\n"
  },
  {
    "path": "source/Grid/index.js",
    "content": "/** @flow */\n\nexport type {\n  NoContentRenderer,\n  Alignment,\n  CellPosition,\n  CellSize,\n  OverscanIndicesGetter,\n  RenderedSection,\n  CellRendererParams,\n  Scroll,\n} from './types';\n\nexport {default} from './Grid';\nexport {default as Grid} from './Grid';\nexport {default as accessibilityOverscanIndicesGetter} from './accessibilityOverscanIndicesGetter';\nexport {default as defaultCellRangeRenderer} from './defaultCellRangeRenderer';\nexport {default as defaultOverscanIndicesGetter} from './defaultOverscanIndicesGetter';\n"
  },
  {
    "path": "source/Grid/types.js",
    "content": "// @flow\n\nimport * as React from 'react';\nimport ScalingCellSizeAndPositionManager from './utils/ScalingCellSizeAndPositionManager';\n\nexport type CellPosition = {columnIndex: number, rowIndex: number};\n\nexport type CellRendererParams = {\n  columnIndex: number,\n  isScrolling: boolean,\n  isVisible: boolean,\n  key: string,\n  parent: Object,\n  rowIndex: number,\n  style: Object,\n};\n\nexport type CellRenderer = (props: CellRendererParams) => React.Element<*>;\n\nexport type CellCache = {[key: string]: React.Element<*>};\nexport type StyleCache = {[key: string]: Object};\n\nexport type CellRangeRendererParams = {\n  cellCache: CellCache,\n  cellRenderer: CellRenderer,\n  columnSizeAndPositionManager: ScalingCellSizeAndPositionManager,\n  columnStartIndex: number,\n  columnStopIndex: number,\n  deferredMeasurementCache?: Object,\n  horizontalOffsetAdjustment: number,\n  isScrolling: boolean,\n  isScrollingOptOut: boolean,\n  parent: Object,\n  rowSizeAndPositionManager: ScalingCellSizeAndPositionManager,\n  rowStartIndex: number,\n  rowStopIndex: number,\n  scrollLeft: number,\n  scrollTop: number,\n  styleCache: StyleCache,\n  verticalOffsetAdjustment: number,\n  visibleColumnIndices: Object,\n  visibleRowIndices: Object,\n};\n\nexport type CellRangeRenderer = (\n  params: CellRangeRendererParams,\n) => React.Element<*>[];\n\nexport type CellSizeGetter = (params: {index: number}) => number;\n\nexport type CellSize = CellSizeGetter | number;\n\nexport type NoContentRenderer = () => React.Element<*> | null;\n\nexport type Scroll = {\n  clientHeight: number,\n  clientWidth: number,\n  scrollHeight: number,\n  scrollLeft: number,\n  scrollTop: number,\n  scrollWidth: number,\n};\n\nexport type ScrollbarPresenceChange = {\n  horizontal: boolean,\n  vertical: boolean,\n  size: number,\n};\n\nexport type RenderedSection = {\n  columnOverscanStartIndex: number,\n  columnOverscanStopIndex: number,\n  columnStartIndex: number,\n  columnStopIndex: number,\n  rowOverscanStartIndex: number,\n  rowOverscanStopIndex: number,\n  rowStartIndex: number,\n  rowStopIndex: number,\n};\n\nexport type OverscanIndicesGetterParams = {\n  // One of SCROLL_DIRECTION_HORIZONTAL or SCROLL_DIRECTION_VERTICAL\n  direction: 'horizontal' | 'vertical',\n\n  // One of SCROLL_DIRECTION_BACKWARD or SCROLL_DIRECTION_FORWARD\n  scrollDirection: -1 | 1,\n\n  // Number of rows or columns in the current axis\n  cellCount: number,\n\n  // Maximum number of cells to over-render in either direction\n  overscanCellsCount: number,\n\n  // Begin of range of visible cells\n  startIndex: number,\n\n  // End of range of visible cells\n  stopIndex: number,\n};\n\nexport type OverscanIndices = {\n  overscanStartIndex: number,\n  overscanStopIndex: number,\n};\n\nexport type OverscanIndicesGetter = (\n  params: OverscanIndicesGetterParams,\n) => OverscanIndices;\n\nexport type Alignment = 'auto' | 'end' | 'start' | 'center';\n\nexport type VisibleCellRange = {\n  start?: number,\n  stop?: number,\n};\n"
  },
  {
    "path": "source/Grid/utils/CellSizeAndPositionManager.jest.js",
    "content": "import CellSizeAndPositionManager from './CellSizeAndPositionManager';\n\ndescribe('CellSizeAndPositionManager', () => {\n  function getCellSizeAndPositionManager({\n    cellCount = 100,\n    estimatedCellSize = 15,\n  } = {}) {\n    const cellSizeGetterCalls = [];\n    const cellSizeAndPositionManager = new CellSizeAndPositionManager({\n      cellCount,\n      cellSizeGetter: ({index}) => {\n        cellSizeGetterCalls.push(index);\n        return 10;\n      },\n      estimatedCellSize,\n    });\n\n    return {\n      cellSizeAndPositionManager,\n      cellSizeGetterCalls,\n    };\n  }\n\n  describe('configure', () => {\n    it('should update inner :cellCount and :estimatedCellSize', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(cellSizeAndPositionManager.getCellCount()).toEqual(100);\n      expect(cellSizeAndPositionManager.getEstimatedCellSize()).toEqual(15);\n\n      cellSizeAndPositionManager.configure({\n        cellCount: 20,\n        estimatedCellSize: 30,\n      });\n      expect(cellSizeAndPositionManager.getCellCount()).toEqual(20);\n      expect(cellSizeAndPositionManager.getEstimatedCellSize()).toEqual(30);\n    });\n  });\n\n  describe('findNearestCell', () => {\n    it('should error if given NaN', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(() => cellSizeAndPositionManager._findNearestCell(NaN)).toThrow();\n    });\n\n    it('should gracefully handle offets outisde of bounds (to account for elastic scrolling)', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(cellSizeAndPositionManager._findNearestCell(-100)).toEqual(0);\n      expect(cellSizeAndPositionManager._findNearestCell(1234567890)).toEqual(\n        99,\n      );\n    });\n\n    it('should find the first cell', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(cellSizeAndPositionManager._findNearestCell(0)).toEqual(0);\n      expect(cellSizeAndPositionManager._findNearestCell(9)).toEqual(0);\n    });\n\n    it('should find the last cell', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(cellSizeAndPositionManager._findNearestCell(990)).toEqual(99);\n      expect(cellSizeAndPositionManager._findNearestCell(991)).toEqual(99);\n    });\n\n    it('should find the a cell that exactly matches a specified offset in the middle', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(cellSizeAndPositionManager._findNearestCell(100)).toEqual(10);\n    });\n\n    it('should find the cell closest to (but before) the specified offset in the middle', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(cellSizeAndPositionManager._findNearestCell(101)).toEqual(10);\n    });\n  });\n\n  describe('getSizeAndPositionOfCell', () => {\n    it('should error if an invalid index is specified', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(() =>\n        cellSizeAndPositionManager.getSizeAndPositionOfCell(-1),\n      ).toThrow();\n      expect(() =>\n        cellSizeAndPositionManager.getSizeAndPositionOfCell(100),\n      ).toThrow();\n    });\n\n    it('should return the correct size and position information for the requested cell', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(\n        cellSizeAndPositionManager.getSizeAndPositionOfCell(0).offset,\n      ).toEqual(0);\n      expect(\n        cellSizeAndPositionManager.getSizeAndPositionOfCell(0).size,\n      ).toEqual(10);\n      expect(\n        cellSizeAndPositionManager.getSizeAndPositionOfCell(1).offset,\n      ).toEqual(10);\n      expect(\n        cellSizeAndPositionManager.getSizeAndPositionOfCell(2).offset,\n      ).toEqual(20);\n    });\n\n    it('should only measure the necessary cells to return the information requested', () => {\n      const {\n        cellSizeAndPositionManager,\n        cellSizeGetterCalls,\n      } = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(0);\n      expect(cellSizeGetterCalls).toEqual([0]);\n    });\n\n    it('should just-in-time measure all cells up to the requested cell if no cells have yet been measured', () => {\n      const {\n        cellSizeAndPositionManager,\n        cellSizeGetterCalls,\n      } = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      expect(cellSizeGetterCalls).toEqual([0, 1, 2, 3, 4, 5]);\n    });\n\n    it('should just-in-time measure cells up to the requested cell if some but not all cells have been measured', () => {\n      const {\n        cellSizeAndPositionManager,\n        cellSizeGetterCalls,\n      } = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      cellSizeGetterCalls.splice(0);\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(10);\n      expect(cellSizeGetterCalls).toEqual([6, 7, 8, 9, 10]);\n    });\n\n    it('should return cached size and position data if cell has already been measured', () => {\n      const {\n        cellSizeAndPositionManager,\n        cellSizeGetterCalls,\n      } = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      cellSizeGetterCalls.splice(0);\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      expect(cellSizeGetterCalls).toEqual([]);\n    });\n  });\n\n  describe('getSizeAndPositionOfLastMeasuredCell', () => {\n    it('should return an empty object if no cached cells are present', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(\n        cellSizeAndPositionManager.getSizeAndPositionOfLastMeasuredCell(),\n      ).toEqual({\n        offset: 0,\n        size: 0,\n      });\n    });\n\n    it('should return size and position data for the highest/last measured cell', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      expect(\n        cellSizeAndPositionManager.getSizeAndPositionOfLastMeasuredCell(),\n      ).toEqual({\n        offset: 50,\n        size: 10,\n      });\n    });\n  });\n\n  describe('getTotalSize', () => {\n    it('should calculate total size based purely on :estimatedCellSize if no measurements have been done', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      expect(cellSizeAndPositionManager.getTotalSize()).toEqual(1500);\n    });\n\n    it('should calculate total size based on a mixture of actual cell sizes and :estimatedCellSize if some cells have been measured', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(49);\n      expect(cellSizeAndPositionManager.getTotalSize()).toEqual(1250);\n    });\n\n    it('should calculate total size based on the actual measured sizes if all cells have been measured', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(99);\n      expect(cellSizeAndPositionManager.getTotalSize()).toEqual(1000);\n    });\n  });\n\n  describe('getUpdatedOffsetForIndex', () => {\n    function getUpdatedOffsetForIndexHelper({\n      align = 'auto',\n      cellCount = 10,\n      cellSize = 10,\n      containerSize = 50,\n      currentOffset = 0,\n      estimatedCellSize = 15,\n      targetIndex = 0,\n    }) {\n      const cellSizeAndPositionManager = new CellSizeAndPositionManager({\n        cellCount,\n        cellSizeGetter: () => cellSize,\n        estimatedCellSize,\n      });\n\n      return cellSizeAndPositionManager.getUpdatedOffsetForIndex({\n        align,\n        containerSize,\n        currentOffset,\n        targetIndex,\n      });\n    }\n\n    it('should scroll to the beginning', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          currentOffset: 100,\n          targetIndex: 0,\n        }),\n      ).toEqual(0);\n    });\n\n    it('should scroll to the end', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          currentOffset: 0,\n          targetIndex: 9,\n        }),\n      ).toEqual(50);\n    });\n\n    it('should scroll forward to the middle', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          currentOffset: 0,\n          targetIndex: 6,\n        }),\n      ).toEqual(20);\n    });\n\n    it('should scroll backward to the middle', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          currentOffset: 50,\n          targetIndex: 2,\n        }),\n      ).toEqual(20);\n    });\n\n    it('should not scroll if an item is already visible', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          currentOffset: 20,\n          targetIndex: 3,\n        }),\n      ).toEqual(20);\n    });\n\n    it('should honor specified :align values', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'auto',\n          currentOffset: 0,\n          targetIndex: 5,\n        }),\n      ).toEqual(10);\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'start',\n          currentOffset: 0,\n          targetIndex: 5,\n        }),\n      ).toEqual(50);\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'auto',\n          currentOffset: 50,\n          targetIndex: 4,\n        }),\n      ).toEqual(40);\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'end',\n          currentOffset: 50,\n          targetIndex: 5,\n        }),\n      ).toEqual(10);\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'center',\n          currentOffset: 50,\n          targetIndex: 5,\n        }),\n      ).toEqual(30);\n    });\n\n    it('should not scroll past the safe bounds even if the specified :align requests it', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'end',\n          currentOffset: 50,\n          targetIndex: 0,\n        }),\n      ).toEqual(0);\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'center',\n          currentOffset: 50,\n          targetIndex: 1,\n        }),\n      ).toEqual(0);\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'start',\n          currentOffset: 0,\n          targetIndex: 9,\n        }),\n      ).toEqual(50);\n\n      // TRICKY: We would expect this to be positioned at 50.\n      // But since the :estimatedCellSize is 15 and we only measure up to the 8th item,\n      // The helper assumes it can scroll farther than it actually can.\n      // Not sure if this edge case is worth \"fixing\" or just acknowledging...\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          align: 'center',\n          currentOffset: 0,\n          targetIndex: 8,\n        }),\n      ).toEqual(55);\n    });\n\n    it('should always return an offset of 0 when :containerSize is 0', () => {\n      expect(\n        getUpdatedOffsetForIndexHelper({\n          containerSize: 0,\n          currentOffset: 50,\n          targetIndex: 2,\n        }),\n      ).toEqual(0);\n    });\n  });\n\n  describe('getVisibleCellRange', () => {\n    it('should not return any indices if :cellCount is 0', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager({\n        cellCount: 0,\n      });\n      const {start, stop} = cellSizeAndPositionManager.getVisibleCellRange({\n        containerSize: 50,\n        offset: 0,\n      });\n      expect(start).toEqual(undefined);\n      expect(stop).toEqual(undefined);\n    });\n\n    it('should return a visible range of cells for the beginning of the list', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      const {start, stop} = cellSizeAndPositionManager.getVisibleCellRange({\n        containerSize: 50,\n        offset: 0,\n      });\n      expect(start).toEqual(0);\n      expect(stop).toEqual(4);\n    });\n\n    it('should return a visible range of cells for the middle of the list where some are partially visible', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      const {start, stop} = cellSizeAndPositionManager.getVisibleCellRange({\n        containerSize: 50,\n        offset: 425,\n      });\n      // 42 and 47 are partially visible\n      expect(start).toEqual(42);\n      expect(stop).toEqual(47);\n    });\n\n    it('should return a visible range of cells for the end of the list', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      const {start, stop} = cellSizeAndPositionManager.getVisibleCellRange({\n        containerSize: 50,\n        offset: 950,\n      });\n      expect(start).toEqual(95);\n      expect(stop).toEqual(99);\n    });\n  });\n\n  describe('resetCell', () => {\n    it('should clear size and position metadata for the specified index and all cells after it', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      cellSizeAndPositionManager.resetCell(3);\n      expect(cellSizeAndPositionManager.getLastMeasuredIndex()).toEqual(2);\n      cellSizeAndPositionManager.resetCell(0);\n      expect(cellSizeAndPositionManager.getLastMeasuredIndex()).toEqual(-1);\n    });\n\n    it('should not clear size and position metadata for cells before the specified index', () => {\n      const {\n        cellSizeAndPositionManager,\n        cellSizeGetterCalls,\n      } = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      cellSizeGetterCalls.splice(0);\n      cellSizeAndPositionManager.resetCell(3);\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(4);\n      expect(cellSizeGetterCalls).toEqual([3, 4]);\n    });\n\n    it('should not skip over any unmeasured or previously-cleared cells', () => {\n      const {cellSizeAndPositionManager} = getCellSizeAndPositionManager();\n      cellSizeAndPositionManager.getSizeAndPositionOfCell(5);\n      cellSizeAndPositionManager.resetCell(2);\n      expect(cellSizeAndPositionManager.getLastMeasuredIndex()).toEqual(1);\n      cellSizeAndPositionManager.resetCell(4);\n      expect(cellSizeAndPositionManager.getLastMeasuredIndex()).toEqual(1);\n      cellSizeAndPositionManager.resetCell(0);\n      expect(cellSizeAndPositionManager.getLastMeasuredIndex()).toEqual(-1);\n    });\n  });\n});\n"
  },
  {
    "path": "source/Grid/utils/CellSizeAndPositionManager.js",
    "content": "/** @flow */\n\nimport type {Alignment, CellSizeGetter, VisibleCellRange} from '../types';\n\ntype CellSizeAndPositionManagerParams = {\n  cellCount: number,\n  cellSizeGetter: CellSizeGetter,\n  estimatedCellSize: number,\n};\n\ntype ConfigureParams = {\n  cellCount: number,\n  estimatedCellSize: number,\n  cellSizeGetter: CellSizeGetter,\n};\n\ntype GetUpdatedOffsetForIndex = {\n  align: Alignment,\n  containerSize: number,\n  currentOffset: number,\n  targetIndex: number,\n};\n\ntype GetVisibleCellRangeParams = {\n  containerSize: number,\n  offset: number,\n};\n\ntype SizeAndPositionData = {\n  offset: number,\n  size: number,\n};\n\n/**\n * Just-in-time calculates and caches size and position information for a collection of cells.\n */\n\nexport default class CellSizeAndPositionManager {\n  // Cache of size and position data for cells, mapped by cell index.\n  // Note that invalid values may exist in this map so only rely on cells up to this._lastMeasuredIndex\n  _cellSizeAndPositionData = {};\n\n  // Measurements for cells up to this index can be trusted; cells afterward should be estimated.\n  _lastMeasuredIndex = -1;\n\n  // Used in deferred mode to track which cells have been queued for measurement.\n  _lastBatchedIndex = -1;\n\n  _cellCount: number;\n  _cellSizeGetter: CellSizeGetter;\n  _estimatedCellSize: number;\n\n  constructor({\n    cellCount,\n    cellSizeGetter,\n    estimatedCellSize,\n  }: CellSizeAndPositionManagerParams) {\n    this._cellSizeGetter = cellSizeGetter;\n    this._cellCount = cellCount;\n    this._estimatedCellSize = estimatedCellSize;\n  }\n\n  areOffsetsAdjusted() {\n    return false;\n  }\n\n  configure({cellCount, estimatedCellSize, cellSizeGetter}: ConfigureParams) {\n    this._cellCount = cellCount;\n    this._estimatedCellSize = estimatedCellSize;\n    this._cellSizeGetter = cellSizeGetter;\n  }\n\n  getCellCount(): number {\n    return this._cellCount;\n  }\n\n  getEstimatedCellSize(): number {\n    return this._estimatedCellSize;\n  }\n\n  getLastMeasuredIndex(): number {\n    return this._lastMeasuredIndex;\n  }\n\n  getOffsetAdjustment() {\n    return 0;\n  }\n\n  /**\n   * This method returns the size and position for the cell at the specified index.\n   * It just-in-time calculates (or used cached values) for cells leading up to the index.\n   */\n  getSizeAndPositionOfCell(index: number): SizeAndPositionData {\n    if (index < 0 || index >= this._cellCount) {\n      throw Error(\n        `Requested index ${index} is outside of range 0..${this._cellCount}`,\n      );\n    }\n\n    if (index > this._lastMeasuredIndex) {\n      let lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell();\n      let offset =\n        lastMeasuredCellSizeAndPosition.offset +\n        lastMeasuredCellSizeAndPosition.size;\n\n      for (var i = this._lastMeasuredIndex + 1; i <= index; i++) {\n        let size = this._cellSizeGetter({index: i});\n\n        // undefined or NaN probably means a logic error in the size getter.\n        // null means we're using CellMeasurer and haven't yet measured a given index.\n        if (size === undefined || isNaN(size)) {\n          throw Error(`Invalid size returned for cell ${i} of value ${size}`);\n        } else if (size === null) {\n          this._cellSizeAndPositionData[i] = {\n            offset,\n            size: 0,\n          };\n\n          this._lastBatchedIndex = index;\n        } else {\n          this._cellSizeAndPositionData[i] = {\n            offset,\n            size,\n          };\n\n          offset += size;\n\n          this._lastMeasuredIndex = index;\n        }\n      }\n    }\n\n    return this._cellSizeAndPositionData[index];\n  }\n\n  getSizeAndPositionOfLastMeasuredCell(): SizeAndPositionData {\n    return this._lastMeasuredIndex >= 0\n      ? this._cellSizeAndPositionData[this._lastMeasuredIndex]\n      : {\n          offset: 0,\n          size: 0,\n        };\n  }\n\n  /**\n   * Total size of all cells being measured.\n   * This value will be completely estimated initially.\n   * As cells are measured, the estimate will be updated.\n   */\n  getTotalSize(): number {\n    const lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell();\n    const totalSizeOfMeasuredCells =\n      lastMeasuredCellSizeAndPosition.offset +\n      lastMeasuredCellSizeAndPosition.size;\n    const numUnmeasuredCells = this._cellCount - this._lastMeasuredIndex - 1;\n    const totalSizeOfUnmeasuredCells =\n      numUnmeasuredCells * this._estimatedCellSize;\n    return totalSizeOfMeasuredCells + totalSizeOfUnmeasuredCells;\n  }\n\n  /**\n   * Determines a new offset that ensures a certain cell is visible, given the current offset.\n   * If the cell is already visible then the current offset will be returned.\n   * If the current offset is too great or small, it will be adjusted just enough to ensure the specified index is visible.\n   *\n   * @param align Desired alignment within container; one of \"auto\" (default), \"start\", or \"end\"\n   * @param containerSize Size (width or height) of the container viewport\n   * @param currentOffset Container's current (x or y) offset\n   * @param totalSize Total size (width or height) of all cells\n   * @return Offset to use to ensure the specified cell is visible\n   */\n  getUpdatedOffsetForIndex({\n    align = 'auto',\n    containerSize,\n    currentOffset,\n    targetIndex,\n  }: GetUpdatedOffsetForIndex): number {\n    if (containerSize <= 0) {\n      return 0;\n    }\n\n    const datum = this.getSizeAndPositionOfCell(targetIndex);\n    const maxOffset = datum.offset;\n    const minOffset = maxOffset - containerSize + datum.size;\n\n    let idealOffset;\n\n    switch (align) {\n      case 'start':\n        idealOffset = maxOffset;\n        break;\n      case 'end':\n        idealOffset = minOffset;\n        break;\n      case 'center':\n        idealOffset = maxOffset - (containerSize - datum.size) / 2;\n        break;\n      default:\n        idealOffset = Math.max(minOffset, Math.min(maxOffset, currentOffset));\n        break;\n    }\n\n    const totalSize = this.getTotalSize();\n\n    return Math.max(0, Math.min(totalSize - containerSize, idealOffset));\n  }\n\n  getVisibleCellRange(params: GetVisibleCellRangeParams): VisibleCellRange {\n    let {containerSize, offset} = params;\n\n    const totalSize = this.getTotalSize();\n\n    if (totalSize === 0) {\n      return {};\n    }\n\n    const maxOffset = offset + containerSize;\n    const start = this._findNearestCell(offset);\n\n    const datum = this.getSizeAndPositionOfCell(start);\n    offset = datum.offset + datum.size;\n\n    let stop = start;\n\n    while (offset < maxOffset && stop < this._cellCount - 1) {\n      stop++;\n\n      offset += this.getSizeAndPositionOfCell(stop).size;\n    }\n\n    return {\n      start,\n      stop,\n    };\n  }\n\n  /**\n   * Clear all cached values for cells after the specified index.\n   * This method should be called for any cell that has changed its size.\n   * It will not immediately perform any calculations; they'll be performed the next time getSizeAndPositionOfCell() is called.\n   */\n  resetCell(index: number): void {\n    this._lastMeasuredIndex = Math.min(this._lastMeasuredIndex, index - 1);\n  }\n\n  _binarySearch(high: number, low: number, offset: number): number {\n    while (low <= high) {\n      const middle = low + Math.floor((high - low) / 2);\n      const currentOffset = this.getSizeAndPositionOfCell(middle).offset;\n\n      if (currentOffset === offset) {\n        return middle;\n      } else if (currentOffset < offset) {\n        low = middle + 1;\n      } else if (currentOffset > offset) {\n        high = middle - 1;\n      }\n    }\n\n    if (low > 0) {\n      return low - 1;\n    } else {\n      return 0;\n    }\n  }\n\n  _exponentialSearch(index: number, offset: number): number {\n    let interval = 1;\n\n    while (\n      index < this._cellCount &&\n      this.getSizeAndPositionOfCell(index).offset < offset\n    ) {\n      index += interval;\n      interval *= 2;\n    }\n\n    return this._binarySearch(\n      Math.min(index, this._cellCount - 1),\n      Math.floor(index / 2),\n      offset,\n    );\n  }\n\n  /**\n   * Searches for the cell (index) nearest the specified offset.\n   *\n   * If no exact match is found the next lowest cell index will be returned.\n   * This allows partially visible cells (with offsets just before/above the fold) to be visible.\n   */\n  _findNearestCell(offset: number): number {\n    if (isNaN(offset)) {\n      throw Error(`Invalid offset ${offset} specified`);\n    }\n\n    // Our search algorithms find the nearest match at or below the specified offset.\n    // So make sure the offset is at least 0 or no match will be found.\n    offset = Math.max(0, offset);\n\n    const lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell();\n    const lastMeasuredIndex = Math.max(0, this._lastMeasuredIndex);\n\n    if (lastMeasuredCellSizeAndPosition.offset >= offset) {\n      // If we've already measured cells within this range just use a binary search as it's faster.\n      return this._binarySearch(lastMeasuredIndex, 0, offset);\n    } else {\n      // If we haven't yet measured this high, fallback to an exponential search with an inner binary search.\n      // The exponential search avoids pre-computing sizes for the full set of cells as a binary search would.\n      // The overall complexity for this approach is O(log n).\n      return this._exponentialSearch(lastMeasuredIndex, offset);\n    }\n  }\n}\n"
  },
  {
    "path": "source/Grid/utils/ScalingCellSizeAndPositionManager.jest.js",
    "content": "import ScalingCellSizeAndPositionManager from './ScalingCellSizeAndPositionManager';\n\ndescribe('ScalingCellSizeAndPositionManager', () => {\n  function init({\n    cellCount = 10,\n    cellSize = 10,\n    estimatedCellSize = 10,\n    maxScrollSize = 50,\n  } = {}) {\n    const cellSizeAndPositionManager = new ScalingCellSizeAndPositionManager({\n      cellCount,\n      cellSizeGetter: () => cellSize,\n      estimatedCellSize,\n      maxScrollSize,\n    });\n\n    return cellSizeAndPositionManager;\n  }\n\n  describe('_getOffsetPercentage', () => {\n    it('should return the correct offset fraction', () => {\n      var expectations = [\n        {offset: 0, expectedOffsetPercentage: 0},\n        {offset: 35, expectedOffsetPercentage: 0.5},\n        {offset: 70, expectedOffsetPercentage: 1},\n      ];\n      const instance = init();\n      expectations.forEach(expectation => {\n        expect(\n          instance._getOffsetPercentage({\n            containerSize: 30,\n            offset: expectation.offset,\n            totalSize: 100,\n          }),\n        ).toBe(expectation.expectedOffsetPercentage);\n      });\n    });\n  });\n\n  describe('getOffsetAdjustment', () => {\n    it('should always return 0 as the adjustment for unscaled lists', () => {\n      const maxScrollSizes = [100, 150];\n      maxScrollSizes.forEach(maxScrollSize => {\n        const instance = init({\n          cellCount: 10,\n          maxScrollSize,\n        });\n        const offsets = [0, 35, 70];\n        offsets.forEach(offset => {\n          expect(\n            instance.getOffsetAdjustment({\n              containerSize: 30,\n              offset,\n            }),\n          ).toBe(0);\n        });\n      });\n    });\n\n    it('should properly scale an offset at the beginning, middle, and end of the list', () => {\n      var offsetsAndExpectedAdjustements = [\n        {offset: 0, expectedAdjustment: -0},\n        {offset: 10, expectedAdjustment: -25},\n        {offset: 20, expectedAdjustment: -50},\n      ];\n      const instance = init();\n      offsetsAndExpectedAdjustements.forEach(offsetAndExpectedAdjustement => {\n        expect(\n          instance.getOffsetAdjustment({\n            containerSize: 30,\n            offset: offsetAndExpectedAdjustement.offset,\n          }),\n        ).toBe(offsetAndExpectedAdjustement.expectedAdjustment);\n      });\n    });\n  });\n\n  describe('getTotalSize', () => {\n    it('should return :totalSize if it is not greater than :maxScrollSize', () => {\n      const maxScrollSizes = [500, 750];\n      maxScrollSizes.forEach(maxScrollSize => {\n        const instance = init({\n          cellCount: 50,\n          maxScrollSize,\n        });\n        expect(instance.getTotalSize()).toEqual(500);\n      });\n    });\n\n    it('should return :maxScrollSize if :totalSize is greater', () => {\n      const instance = init({\n        cellCount: 100,\n        maxScrollSize: 100,\n      });\n      expect(instance.getTotalSize()).toEqual(100);\n    });\n  });\n\n  describe('getUpdatedOffsetForIndex', () => {\n    it('should scroll to a cell before the current range', () => {\n      const data = [\n        {targetIndex: 0, expectedOffset: 0},\n        {targetIndex: 1, expectedOffset: 3}, // (unsafe: 10)\n        {targetIndex: 2, expectedOffset: 6}, // (unsafe: 20)\n      ];\n      const instance = init();\n      data.forEach(datum => {\n        expect(\n          instance.getUpdatedOffsetForIndex({\n            containerSize: 30,\n            currentOffset: 10, // (unsafe: 35)\n            targetIndex: datum.targetIndex,\n          }),\n        ).toBe(datum.expectedOffset);\n      });\n    });\n\n    it('should scroll to a cell after the current range', () => {\n      const data = [\n        {targetIndex: 7, expectedOffset: 14}, // (unsafe: 50)\n        {targetIndex: 9, expectedOffset: 20}, // (unsafe: 70)\n      ];\n      const instance = init();\n      data.forEach(datum => {\n        expect(\n          instance.getUpdatedOffsetForIndex({\n            containerSize: 30,\n            currentOffset: 0,\n            targetIndex: datum.targetIndex,\n          }),\n        ).toBe(datum.expectedOffset);\n      });\n    });\n\n    it('should not scroll to a cell already visible within the current range', () => {\n      const instance = init();\n      expect(\n        instance.getUpdatedOffsetForIndex({\n          containerSize: 30,\n          currentOffset: 10, // (unsafe: 35)\n          targetIndex: 4,\n        }),\n      ).toBe(10);\n    });\n  });\n\n  describe('getVisibleCellRange', () => {\n    it('should correct identify the first set of cells', () => {\n      const instance = init();\n      expect(\n        instance.getVisibleCellRange({\n          containerSize: 30,\n          offset: 0,\n        }),\n      ).toEqual({\n        start: 0,\n        stop: 2,\n      });\n    });\n\n    it('should correct identify cells in the middle', () => {\n      const instance = init();\n      expect(\n        instance.getVisibleCellRange({\n          containerSize: 30,\n          offset: 2.85, // (unsafe: 10)\n        }),\n      ).toEqual({\n        start: 1,\n        stop: 3,\n      });\n    });\n\n    it('should correct identify partially visible cells', () => {\n      const instance = init();\n      expect(\n        instance.getVisibleCellRange({\n          containerSize: 30,\n          offset: 10, // (unsafe: 35)\n        }),\n      ).toEqual({\n        start: 3,\n        stop: 6,\n      });\n    });\n\n    it('should correct identify the last set of cells', () => {\n      const instance = init();\n      expect(\n        instance.getVisibleCellRange({\n          containerSize: 30,\n          offset: 20,\n        }),\n      ).toEqual({\n        start: 7,\n        stop: 9,\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "source/Grid/utils/ScalingCellSizeAndPositionManager.js",
    "content": "/** @flow */\n\nimport type {Alignment, CellSizeGetter, VisibleCellRange} from '../types';\n\nimport CellSizeAndPositionManager from './CellSizeAndPositionManager';\nimport {getMaxElementSize} from './maxElementSize.js';\n\ntype ContainerSizeAndOffset = {\n  containerSize: number,\n  offset: number,\n};\n\n/**\n * Browsers have scroll offset limitations (eg Chrome stops scrolling at ~33.5M pixels where as Edge tops out at ~1.5M pixels).\n * After a certain position, the browser won't allow the user to scroll further (even via JavaScript scroll offset adjustments).\n * This util picks a lower ceiling for max size and artificially adjusts positions within to make it transparent for users.\n */\n\ntype Params = {\n  maxScrollSize?: number,\n  cellCount: number,\n  cellSizeGetter: CellSizeGetter,\n  estimatedCellSize: number,\n};\n\n/**\n * Extends CellSizeAndPositionManager and adds scaling behavior for lists that are too large to fit within a browser's native limits.\n */\nexport default class ScalingCellSizeAndPositionManager {\n  _cellSizeAndPositionManager: CellSizeAndPositionManager;\n  _maxScrollSize: number;\n\n  constructor({maxScrollSize = getMaxElementSize(), ...params}: Params) {\n    // Favor composition over inheritance to simplify IE10 support\n    this._cellSizeAndPositionManager = new CellSizeAndPositionManager(params);\n    this._maxScrollSize = maxScrollSize;\n  }\n\n  areOffsetsAdjusted(): boolean {\n    return (\n      this._cellSizeAndPositionManager.getTotalSize() > this._maxScrollSize\n    );\n  }\n\n  configure(params: {\n    cellCount: number,\n    estimatedCellSize: number,\n    cellSizeGetter: CellSizeGetter,\n  }) {\n    this._cellSizeAndPositionManager.configure(params);\n  }\n\n  getCellCount(): number {\n    return this._cellSizeAndPositionManager.getCellCount();\n  }\n\n  getEstimatedCellSize(): number {\n    return this._cellSizeAndPositionManager.getEstimatedCellSize();\n  }\n\n  getLastMeasuredIndex(): number {\n    return this._cellSizeAndPositionManager.getLastMeasuredIndex();\n  }\n\n  /**\n   * Number of pixels a cell at the given position (offset) should be shifted in order to fit within the scaled container.\n   * The offset passed to this function is scaled (safe) as well.\n   */\n  getOffsetAdjustment({\n    containerSize,\n    offset, // safe\n  }: ContainerSizeAndOffset): number {\n    const totalSize = this._cellSizeAndPositionManager.getTotalSize();\n    const safeTotalSize = this.getTotalSize();\n    const offsetPercentage = this._getOffsetPercentage({\n      containerSize,\n      offset,\n      totalSize: safeTotalSize,\n    });\n\n    return Math.round(offsetPercentage * (safeTotalSize - totalSize));\n  }\n\n  getSizeAndPositionOfCell(index: number) {\n    return this._cellSizeAndPositionManager.getSizeAndPositionOfCell(index);\n  }\n\n  getSizeAndPositionOfLastMeasuredCell() {\n    return this._cellSizeAndPositionManager.getSizeAndPositionOfLastMeasuredCell();\n  }\n\n  /** See CellSizeAndPositionManager#getTotalSize */\n  getTotalSize(): number {\n    return Math.min(\n      this._maxScrollSize,\n      this._cellSizeAndPositionManager.getTotalSize(),\n    );\n  }\n\n  /** See CellSizeAndPositionManager#getUpdatedOffsetForIndex */\n  getUpdatedOffsetForIndex({\n    align = 'auto',\n    containerSize,\n    currentOffset, // safe\n    targetIndex,\n  }: {\n    align: Alignment,\n    containerSize: number,\n    currentOffset: number,\n    targetIndex: number,\n  }) {\n    currentOffset = this._safeOffsetToOffset({\n      containerSize,\n      offset: currentOffset,\n    });\n\n    const offset = this._cellSizeAndPositionManager.getUpdatedOffsetForIndex({\n      align,\n      containerSize,\n      currentOffset,\n      targetIndex,\n    });\n\n    return this._offsetToSafeOffset({\n      containerSize,\n      offset,\n    });\n  }\n\n  /** See CellSizeAndPositionManager#getVisibleCellRange */\n  getVisibleCellRange({\n    containerSize,\n    offset, // safe\n  }: ContainerSizeAndOffset): VisibleCellRange {\n    offset = this._safeOffsetToOffset({\n      containerSize,\n      offset,\n    });\n\n    return this._cellSizeAndPositionManager.getVisibleCellRange({\n      containerSize,\n      offset,\n    });\n  }\n\n  resetCell(index: number): void {\n    this._cellSizeAndPositionManager.resetCell(index);\n  }\n\n  _getOffsetPercentage({\n    containerSize,\n    offset, // safe\n    totalSize,\n  }: {\n    containerSize: number,\n    offset: number,\n    totalSize: number,\n  }) {\n    return totalSize <= containerSize\n      ? 0\n      : offset / (totalSize - containerSize);\n  }\n\n  _offsetToSafeOffset({\n    containerSize,\n    offset, // unsafe\n  }: ContainerSizeAndOffset): number {\n    const totalSize = this._cellSizeAndPositionManager.getTotalSize();\n    const safeTotalSize = this.getTotalSize();\n\n    if (totalSize === safeTotalSize) {\n      return offset;\n    } else {\n      const offsetPercentage = this._getOffsetPercentage({\n        containerSize,\n        offset,\n        totalSize,\n      });\n\n      return Math.round(offsetPercentage * (safeTotalSize - containerSize));\n    }\n  }\n\n  _safeOffsetToOffset({\n    containerSize,\n    offset, // safe\n  }: ContainerSizeAndOffset): number {\n    const totalSize = this._cellSizeAndPositionManager.getTotalSize();\n    const safeTotalSize = this.getTotalSize();\n\n    if (totalSize === safeTotalSize) {\n      return offset;\n    } else {\n      const offsetPercentage = this._getOffsetPercentage({\n        containerSize,\n        offset,\n        totalSize: safeTotalSize,\n      });\n\n      return Math.round(offsetPercentage * (totalSize - containerSize));\n    }\n  }\n}\n"
  },
  {
    "path": "source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.jest.js",
    "content": "import calculateSizeAndPositionDataAndUpdateScrollOffset from './calculateSizeAndPositionDataAndUpdateScrollOffset';\n\ndescribe('calculateSizeAndPositionDataAndUpdateScrollOffset', () => {\n  function helper({\n    cellCount = 100,\n    cellSize = 10,\n    computeMetadataCallbackProps = {},\n    nextCellsCount = 100,\n    nextCellSize = 10,\n    nextScrollToIndex,\n    scrollToIndex,\n  } = {}) {\n    const computeMetadataCallbackCalls = [];\n    const updateScrollOffsetForScrollToIndexCalls = [];\n\n    calculateSizeAndPositionDataAndUpdateScrollOffset({\n      cellCount,\n      cellSize,\n      computeMetadataCallback: params =>\n        computeMetadataCallbackCalls.push(params),\n      computeMetadataCallbackProps,\n      nextCellsCount,\n      nextCellSize,\n      nextScrollToIndex,\n      scrollToIndex,\n      updateScrollOffsetForScrollToIndex: params =>\n        updateScrollOffsetForScrollToIndexCalls.push(params),\n    });\n\n    return {\n      computeMetadataCallbackCalls,\n      updateScrollOffsetForScrollToIndexCalls,\n    };\n  }\n\n  it('should call :computeMetadataCallback if :cellCount has changed', () => {\n    const {computeMetadataCallbackCalls} = helper({\n      cellCount: 100,\n      nextCellsCount: 200,\n    });\n    expect(computeMetadataCallbackCalls.length).toEqual(1);\n  });\n\n  it('should call :computeMetadataCallback if numeric :cellSize has changed', () => {\n    const {computeMetadataCallbackCalls} = helper({\n      cellSize: 10,\n      nextCellSize: 20,\n    });\n    expect(computeMetadataCallbackCalls.length).toEqual(1);\n  });\n\n  it('should not call :computeMetadataCallback if :cellSize callback has changed', () => {\n    const {computeMetadataCallbackCalls} = helper({\n      cellSize: () => {},\n      nextCellSize: () => {},\n    });\n    expect(computeMetadataCallbackCalls.length).toEqual(0);\n  });\n\n  it('should not call :updateScrollOffsetForScrollToIndex if :scrollToIndex is not specified', () => {\n    const {updateScrollOffsetForScrollToIndexCalls} = helper();\n    expect(updateScrollOffsetForScrollToIndexCalls.length).toEqual(0);\n  });\n\n  it('should not call :updateScrollOffsetForScrollToIndex if :scrollToIndex has also changed', () => {\n    const {updateScrollOffsetForScrollToIndexCalls} = helper({\n      scrollToIndex: 10,\n      nextScrollToIndex: 20,\n    });\n    expect(updateScrollOffsetForScrollToIndexCalls.length).toEqual(0);\n  });\n\n  it('should not call :computeMetadataCallback if the above conditions are not true', () => {\n    const {computeMetadataCallbackCalls} = helper();\n    expect(computeMetadataCallbackCalls.length).toEqual(0);\n  });\n});\n"
  },
  {
    "path": "source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.js",
    "content": "// @flow\n\n/**\n * Helper method that determines when to recalculate row or column metadata.\n */\n\ntype Params<T> = {\n  // Number of rows or columns in the current axis\n  cellCount: number,\n\n  // Width or height of cells for the current axis\n  cellSize: ?number,\n\n  // Method to invoke if cell metadata should be recalculated\n  computeMetadataCallback: (props: T) => void,\n\n  // Parameters to pass to :computeMetadataCallback\n  computeMetadataCallbackProps: T,\n\n  // Newly updated number of rows or columns in the current axis\n  nextCellsCount: number,\n\n  // Newly updated width or height of cells for the current axis\n  nextCellSize: ?number,\n\n  // Newly updated scroll-to-index\n  nextScrollToIndex: number,\n\n  // Scroll-to-index\n  scrollToIndex: number,\n\n  // Callback to invoke if the scroll position should be recalculated\n  updateScrollOffsetForScrollToIndex: () => void,\n};\n\nexport default function calculateSizeAndPositionDataAndUpdateScrollOffset({\n  cellCount,\n  cellSize,\n  computeMetadataCallback,\n  computeMetadataCallbackProps,\n  nextCellsCount,\n  nextCellSize,\n  nextScrollToIndex,\n  scrollToIndex,\n  updateScrollOffsetForScrollToIndex,\n}: Params<*>) {\n  // Don't compare cell sizes if they are functions because inline functions would cause infinite loops.\n  // In that event users should use the manual recompute methods to inform of changes.\n  if (\n    cellCount !== nextCellsCount ||\n    ((typeof cellSize === 'number' || typeof nextCellSize === 'number') &&\n      cellSize !== nextCellSize)\n  ) {\n    computeMetadataCallback(computeMetadataCallbackProps);\n\n    // Updated cell metadata may have hidden the previous scrolled-to item.\n    // In this case we should also update the scrollTop to ensure it stays visible.\n    if (scrollToIndex >= 0 && scrollToIndex === nextScrollToIndex) {\n      updateScrollOffsetForScrollToIndex();\n    }\n  }\n}\n"
  },
  {
    "path": "source/Grid/utils/maxElementSize.js",
    "content": "// @flow\n\nconst DEFAULT_MAX_ELEMENT_SIZE = 1500000;\nconst CHROME_MAX_ELEMENT_SIZE = 1.67771e7;\n\nconst isBrowser = () => typeof window !== 'undefined';\n\nconst isChrome = () => !!window.chrome;\n\nexport const getMaxElementSize = (): number => {\n  if (isBrowser()) {\n    if (isChrome()) {\n      return CHROME_MAX_ELEMENT_SIZE;\n    }\n  }\n  return DEFAULT_MAX_ELEMENT_SIZE;\n};\n"
  },
  {
    "path": "source/Grid/utils/updateScrollIndexHelper.jest.js",
    "content": "import updateScrollIndexHelper from './updateScrollIndexHelper';\nimport CellSizeAndPositionManager from './CellSizeAndPositionManager';\n\n// Default cell sizes and offsets for use in shared tests\nexport function getCellSizeAndPositionManager({\n  cellCount = CELL_SIZES.length,\n  estimatedCellSize = 10,\n}) {\n  return new CellSizeAndPositionManager({\n    cellCount,\n    cellSizeGetter: ({index}) => CELL_SIZES[index % CELL_SIZES.length],\n    estimatedCellSize,\n  });\n}\n\nconst CELL_SIZES = [\n  10, // 0: 0..0 (min)\n  20, // 1: 0..10\n  15, // 2: 0..30\n  10, // 3: 5..45\n  15, // 4: 20..55\n  30, // 5: 50..70\n  20, // 6: 70..100\n  10, // 7: 80..110\n  30, //  8: 110..110 (max)\n];\n\ndescribe('updateScrollIndexHelper', () => {\n  function helper({\n    cellCount = undefined,\n    cellSizeAndPositionManager,\n    cellSize = 10,\n    previousCellsCount = undefined,\n    previousCellSize = 10,\n    previousScrollToAlignment = 'auto',\n    previousScrollToIndex,\n    previousSize = 50,\n    scrollOffset = 0,\n    scrollToAlignment = 'auto',\n    scrollToIndex,\n    size = 50,\n  } = {}) {\n    cellSizeAndPositionManager =\n      cellSizeAndPositionManager || getCellSizeAndPositionManager({cellCount});\n    cellCount =\n      cellCount === undefined\n        ? cellSizeAndPositionManager.getCellCount()\n        : cellCount;\n    previousCellsCount =\n      previousCellsCount === undefined ? cellCount : previousCellsCount;\n\n    let updateScrollIndexCallbackCalled = false;\n\n    function updateScrollIndexCallback() {\n      updateScrollIndexCallbackCalled = true;\n    }\n\n    updateScrollIndexHelper({\n      cellCount,\n      cellSizeAndPositionManager,\n      cellSize,\n      previousCellsCount,\n      previousCellSize,\n      previousScrollToAlignment,\n      previousScrollToIndex,\n      previousSize,\n      scrollOffset,\n      scrollToAlignment,\n      scrollToIndex,\n      size,\n      updateScrollIndexCallback,\n    });\n\n    return updateScrollIndexCallbackCalled;\n  }\n\n  it('should not call :updateScrollIndexCallback if there is no :scrollToIndex and size has not changed', () => {\n    expect(helper()).toEqual(false);\n  });\n\n  it('should not call :updateScrollIndexCallback if an invalid :scrollToIndex has been specified', () => {\n    expect(\n      helper({\n        size: 100,\n        previousSize: 50,\n        scrollToIndex: -1,\n      }),\n    ).toEqual(false);\n  });\n\n  it('should call :updateScrollIndexCallback if there is a :scrollToIndex and :size has changed', () => {\n    expect(\n      helper({\n        cellCount: 100,\n        size: 100,\n        previousSize: 50,\n        scrollToIndex: 10,\n      }),\n    ).toEqual(true);\n  });\n\n  it('should call :updateScrollIndexCallback if there is a :scrollToIndex and :cellSize has changed', () => {\n    expect(\n      helper({\n        cellCount: 100,\n        cellSize: 15,\n        previousCellSize: 20,\n        scrollToIndex: 10,\n      }),\n    ).toEqual(true);\n  });\n\n  it('should call :updateScrollIndexCallback if previous :scrollToIndex has changed', () => {\n    expect(\n      helper({\n        cellCount: 15,\n        previousScrollToIndex: 20,\n        scrollToIndex: 10,\n      }),\n    ).toEqual(true);\n  });\n\n  it('should call :updateScrollIndexCallback if :cellCount has been reduced past the current scroll offset', () => {\n    expect(\n      helper({\n        previousCellsCount: 100,\n        scrollOffset: 510,\n      }),\n    ).toEqual(true);\n  });\n\n  it('should call :updateScrollIndexCallback if there is no :scrollToIndex but :size has been reduced', () => {\n    expect(\n      helper({\n        previousSize: 100,\n        scrollOffset: 510,\n        size: 50,\n      }),\n    ).toEqual(true);\n  });\n\n  it('should not measure rows if :size or :cellCount have been reduced but only use already measured (or estimated) total size', () => {\n    const cellSizeAndPositionManager = {\n      getCellCount: () => CELL_SIZES.length,\n      getTotalSize: () => 560,\n    };\n\n    expect(\n      helper({\n        cellSizeAndPositionManager,\n        previousSize: 100,\n        scrollOffset: 510,\n        size: 50,\n      }),\n    ).toEqual(false);\n  });\n\n  it('should not call :updateScrollIndexCallback if there is no :scrollToIndex but :cellCount has been increased', () => {\n    expect(\n      helper({\n        cellCount: 100,\n        previousCellsCount: 50,\n      }),\n    ).toEqual(false);\n  });\n\n  it('should not call :updateScrollIndexCallback if there is no :scrollToIndex but :size has been increased', () => {\n    expect(\n      helper({\n        previousSize: 50,\n        size: 100,\n      }),\n    ).toEqual(false);\n  });\n\n  it('should call :updateScrollIndexCallback if :scrollToAlignment has changed', () => {\n    expect(\n      helper({\n        previousScrollToAlignment: 'start',\n        scrollToAlignment: 'end',\n        scrollToIndex: 5,\n      }),\n    ).toEqual(true);\n  });\n\n  it('should not call :updateScrollIndexCallback if :scrollToAlignment has changed but there is no :scrollToIndex', () => {\n    expect(\n      helper({\n        previousScrollToAlignment: 'start',\n        scrollToAlignment: 'end',\n      }),\n    ).toEqual(false);\n  });\n});\n"
  },
  {
    "path": "source/Grid/utils/updateScrollIndexHelper.js",
    "content": "// @flow\n\nimport type {Alignment, CellSize} from '../types';\n\nimport ScalingCellSizeAndPositionManager from './ScalingCellSizeAndPositionManager.js';\n\n/**\n * Helper function that determines when to update scroll offsets to ensure that a scroll-to-index remains visible.\n * This function also ensures that the scroll ofset isn't past the last column/row of cells.\n */\n\ntype Params = {\n  // Width or height of cells for the current axis\n  cellSize?: CellSize,\n\n  // Manages size and position metadata of cells\n  cellSizeAndPositionManager: ScalingCellSizeAndPositionManager,\n\n  // Previous number of rows or columns\n  previousCellsCount: number,\n\n  // Previous width or height of cells\n  previousCellSize: CellSize,\n\n  previousScrollToAlignment: Alignment,\n\n  // Previous scroll-to-index\n  previousScrollToIndex: number,\n\n  // Previous width or height of the virtualized container\n  previousSize: number,\n\n  // Current scrollLeft or scrollTop\n  scrollOffset: number,\n\n  scrollToAlignment: Alignment,\n\n  // Scroll-to-index\n  scrollToIndex: number,\n\n  // Width or height of the virtualized container\n  size: number,\n\n  sizeJustIncreasedFromZero: boolean,\n\n  // Callback to invoke with an scroll-to-index value\n  updateScrollIndexCallback: (index: number) => void,\n};\n\nexport default function updateScrollIndexHelper({\n  cellSize,\n  cellSizeAndPositionManager,\n  previousCellsCount,\n  previousCellSize,\n  previousScrollToAlignment,\n  previousScrollToIndex,\n  previousSize,\n  scrollOffset,\n  scrollToAlignment,\n  scrollToIndex,\n  size,\n  sizeJustIncreasedFromZero,\n  updateScrollIndexCallback,\n}: Params) {\n  const cellCount = cellSizeAndPositionManager.getCellCount();\n  const hasScrollToIndex = scrollToIndex >= 0 && scrollToIndex < cellCount;\n  const sizeHasChanged =\n    size !== previousSize ||\n    sizeJustIncreasedFromZero ||\n    !previousCellSize ||\n    (typeof cellSize === 'number' && cellSize !== previousCellSize);\n\n  // If we have a new scroll target OR if height/row-height has changed,\n  // We should ensure that the scroll target is visible.\n  if (\n    hasScrollToIndex &&\n    (sizeHasChanged ||\n      scrollToAlignment !== previousScrollToAlignment ||\n      scrollToIndex !== previousScrollToIndex)\n  ) {\n    updateScrollIndexCallback(scrollToIndex);\n\n    // If we don't have a selected item but list size or number of children have decreased,\n    // Make sure we aren't scrolled too far past the current content.\n  } else if (\n    !hasScrollToIndex &&\n    cellCount > 0 &&\n    (size < previousSize || cellCount < previousCellsCount)\n  ) {\n    // We need to ensure that the current scroll offset is still within the collection's range.\n    // To do this, we don't need to measure everything; CellMeasurer would perform poorly.\n    // Just check to make sure we're still okay.\n    // Only adjust the scroll position if we've scrolled below the last set of rows.\n    if (scrollOffset > cellSizeAndPositionManager.getTotalSize() - size) {\n      updateScrollIndexCallback(cellCount - 1);\n    }\n  }\n}\n"
  },
  {
    "path": "source/InfiniteLoader/InfiniteLoader.example.css",
    "content": ".List {\n  width: 100%;\n  border: 1px solid #DDD;\n}\n\n.row {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  padding: 0 25px;\n  background-color: #fff;\n  border-bottom: 1px solid #e0e0e0;\n}\n\n.placeholder {\n  display: inline-block;\n  height: 1em;\n  background-color: #DDD;\n}\n\n.cacheButtonAndCountRow {\n  display: flex;\n  align-items: center;\n}\n.button {\n  flex: 0 0 auto;\n  background-color: #4db6ac;\n  color: #fff;\n  appearance: none;\n  border: none;\n  padding: .5em 1em;\n  border-radius: .35em;\n  font-size: 1em;\n}\n.cacheCountRow {\n  flex: 1 1 auto;\n  text-align: right;\n  color: #bdbdbd;\n  font-size: .75em;\n  font-weight: 100;\n}\n"
  },
  {
    "path": "source/InfiniteLoader/InfiniteLoader.example.js",
    "content": "/** @flow */\nimport * as React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport Immutable from 'immutable';\nimport AutoSizer from '../AutoSizer';\nimport InfiniteLoader from './InfiniteLoader';\nimport List from '../List';\nimport styles from './InfiniteLoader.example.css';\n\nconst STATUS_LOADING = 1;\nconst STATUS_LOADED = 2;\n\nexport default class InfiniteLoaderExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      loadedRowCount: 0,\n      loadedRowsMap: {},\n      loadingRowCount: 0,\n    };\n\n    this._timeoutIdMap = {};\n\n    this._clearData = this._clearData.bind(this);\n    this._isRowLoaded = this._isRowLoaded.bind(this);\n    this._loadMoreRows = this._loadMoreRows.bind(this);\n    this._rowRenderer = this._rowRenderer.bind(this);\n  }\n\n  componentWillUnmount() {\n    Object.keys(this._timeoutIdMap).forEach(timeoutId => {\n      clearTimeout(timeoutId);\n    });\n  }\n\n  render() {\n    const {list} = this.context;\n    const {loadedRowCount, loadingRowCount} = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"InfiniteLoader\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/InfiniteLoader/InfiniteLoader.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/InfiniteLoader.md\"\n        />\n\n        <ContentBoxParagraph>\n          This component manages just-in-time data fetching to ensure that the\n          all visible rows have been loaded. It also uses a threshold to\n          determine how early to pre-fetch rows (before a user scrolls to them).\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <div className={styles.cacheButtonAndCountRow}>\n            <button className={styles.button} onClick={this._clearData}>\n              Flush Cached Data\n            </button>\n\n            <div className={styles.cacheCountRow}>\n              {loadingRowCount} loading, {loadedRowCount} loaded\n            </div>\n          </div>\n        </ContentBoxParagraph>\n\n        <InfiniteLoader\n          isRowLoaded={this._isRowLoaded}\n          loadMoreRows={this._loadMoreRows}\n          rowCount={list.size}>\n          {({onRowsRendered, registerChild}) => (\n            <AutoSizer disableHeight>\n              {({width}) => (\n                <List\n                  ref={registerChild}\n                  className={styles.List}\n                  height={200}\n                  onRowsRendered={onRowsRendered}\n                  rowCount={list.size}\n                  rowHeight={30}\n                  rowRenderer={this._rowRenderer}\n                  width={width}\n                />\n              )}\n            </AutoSizer>\n          )}\n        </InfiniteLoader>\n      </ContentBox>\n    );\n  }\n\n  _clearData() {\n    this.setState({\n      loadedRowCount: 0,\n      loadedRowsMap: {},\n      loadingRowCount: 0,\n    });\n  }\n\n  _isRowLoaded({index}) {\n    const {loadedRowsMap} = this.state;\n    return !!loadedRowsMap[index]; // STATUS_LOADING or STATUS_LOADED\n  }\n\n  _loadMoreRows({startIndex, stopIndex}) {\n    const {loadedRowsMap, loadingRowCount} = this.state;\n    const increment = stopIndex - startIndex + 1;\n\n    for (var i = startIndex; i <= stopIndex; i++) {\n      loadedRowsMap[i] = STATUS_LOADING;\n    }\n\n    this.setState({\n      loadingRowCount: loadingRowCount + increment,\n    });\n\n    const timeoutId = setTimeout(() => {\n      const {loadedRowCount, loadingRowCount} = this.state;\n\n      delete this._timeoutIdMap[timeoutId];\n\n      for (var i = startIndex; i <= stopIndex; i++) {\n        loadedRowsMap[i] = STATUS_LOADED;\n      }\n\n      this.setState({\n        loadingRowCount: loadingRowCount - increment,\n        loadedRowCount: loadedRowCount + increment,\n      });\n\n      promiseResolver();\n    }, 1000 + Math.round(Math.random() * 2000));\n\n    this._timeoutIdMap[timeoutId] = true;\n\n    let promiseResolver;\n\n    return new Promise(resolve => {\n      promiseResolver = resolve;\n    });\n  }\n\n  _rowRenderer({index, key, style}) {\n    const {list} = this.context;\n    const {loadedRowsMap} = this.state;\n\n    const row = list.get(index);\n    let content;\n\n    if (loadedRowsMap[index] === STATUS_LOADED) {\n      content = row.name;\n    } else {\n      content = (\n        <div className={styles.placeholder} style={{width: row.size}} />\n      );\n    }\n\n    return (\n      <div className={styles.row} key={key} style={style}>\n        {content}\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "source/InfiniteLoader/InfiniteLoader.jest.js",
    "content": "import InfiniteLoader, {\n  forceUpdateReactVirtualizedComponent,\n  isRangeVisible,\n  scanForUnloadedRanges,\n} from './InfiniteLoader';\nimport * as React from 'react';\nimport List from '../List';\nimport {render} from '../TestUtils';\n\ndescribe('InfiniteLoader', () => {\n  let innerOnRowsRendered;\n  let isRowLoadedCalls = [];\n  let isRowLoadedMap = {};\n  let loadMoreRowsCalls = [];\n  let rowRendererCalls = [];\n\n  beforeEach(() => {\n    isRowLoadedCalls = [];\n    isRowLoadedMap = {};\n    loadMoreRowsCalls = [];\n    rowRendererCalls = [];\n  });\n\n  function defaultIsRowLoaded({index}) {\n    isRowLoadedCalls.push(index);\n    return !!isRowLoadedMap[index];\n  }\n\n  function defaultLoadMoreRows({startIndex, stopIndex}) {\n    loadMoreRowsCalls.push({startIndex, stopIndex});\n  }\n\n  function rowRenderer({index, key, style}) {\n    rowRendererCalls.push(index);\n    return <div key={key} style={style} />;\n  }\n\n  function getMarkup({\n    height = 100,\n    isRowLoaded = defaultIsRowLoaded,\n    loadMoreRows = defaultLoadMoreRows,\n    minimumBatchSize = 1,\n    rowHeight = 20,\n    rowCount = 100,\n    scrollToIndex,\n    threshold = 10,\n    width = 200,\n  } = {}) {\n    return (\n      <InfiniteLoader\n        isRowLoaded={isRowLoaded}\n        loadMoreRows={loadMoreRows}\n        minimumBatchSize={minimumBatchSize}\n        rowCount={rowCount}\n        threshold={threshold}>\n        {({onRowsRendered, registerChild}) => {\n          innerOnRowsRendered = onRowsRendered;\n\n          return (\n            <List\n              ref={registerChild}\n              height={height}\n              onRowsRendered={onRowsRendered}\n              rowHeight={rowHeight}\n              rowRenderer={rowRenderer}\n              rowCount={rowCount}\n              scrollToIndex={scrollToIndex}\n              width={width}\n            />\n          );\n        }}\n      </InfiniteLoader>\n    );\n  }\n\n  it('should call :isRowLoaded for all rows within the threshold each time a range of rows are rendered', () => {\n    render(getMarkup());\n    expect(isRowLoadedCalls).toEqual([\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9,\n      10,\n      11,\n      12,\n      13,\n      14,\n    ]);\n  });\n\n  it('should call :isRowLoaded for all rows within the rowCount each time a range of rows are rendered', () => {\n    render(getMarkup({rowCount: 10}));\n    expect(isRowLoadedCalls).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  });\n\n  it('should call :loadMoreRows for unloaded rows within the threshold', () => {\n    render(getMarkup());\n    expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 14}]);\n  });\n\n  it('should call :loadMoreRows for unloaded rows within the rowCount', () => {\n    render(getMarkup({rowCount: 10}));\n    expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 9}]);\n  });\n\n  it('should :forceUpdate once rows have loaded if :loadMoreRows returns a Promise', async done => {\n    let savedResolve;\n    function loadMoreRows() {\n      return new Promise(resolve => {\n        savedResolve = resolve;\n      });\n    }\n    render(getMarkup({loadMoreRows}));\n    rowRendererCalls.splice(0);\n    await savedResolve();\n    expect(rowRendererCalls.length > 0).toEqual(true);\n    done();\n  });\n\n  it('should not :forceUpdate once rows have loaded rows are no longer visible', async done => {\n    let resolves = [];\n    function loadMoreRows() {\n      return new Promise(resolve => {\n        resolves.push(resolve);\n      });\n    }\n    render(getMarkup({loadMoreRows}));\n    // Simulate a new range of rows being loaded\n    innerOnRowsRendered({startIndex: 100, stopIndex: 101});\n    rowRendererCalls.splice(0);\n    await resolves[0](); // Resolve the first request only, not the simulated row-change\n    expect(rowRendererCalls.length).toEqual(0);\n    done();\n  });\n\n  describe('minimumBatchSize', () => {\n    it('should respect the specified :minimumBatchSize when scrolling down', () => {\n      render(\n        getMarkup({\n          minimumBatchSize: 10,\n          threshold: 0,\n        }),\n      );\n      expect(loadMoreRowsCalls.length).toEqual(1);\n      expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 9}]);\n    });\n\n    it('should respect the specified :minimumBatchSize when scrolling up', () => {\n      render(\n        getMarkup({\n          minimumBatchSize: 10,\n          scrollToIndex: 20,\n          threshold: 0,\n        }),\n      );\n      loadMoreRowsCalls.splice(0);\n      render(\n        getMarkup({\n          isRowLoaded: ({index}) => index >= 20,\n          minimumBatchSize: 10,\n          scrollToIndex: 15,\n          threshold: 0,\n        }),\n      );\n      expect(loadMoreRowsCalls.length).toEqual(1);\n      expect(loadMoreRowsCalls).toEqual([{startIndex: 10, stopIndex: 19}]);\n    });\n\n    it('should not interfere with :threshold', () => {\n      render(\n        getMarkup({\n          minimumBatchSize: 10,\n          threshold: 10,\n        }),\n      );\n      expect(loadMoreRowsCalls.length).toEqual(1);\n      expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 14}]);\n    });\n\n    it('should respect the specified :minimumBatchSize if a user scrolls past the previous range', () => {\n      const isRowLoadedIndices = {};\n\n      function isRowLoaded({index}) {\n        if (!isRowLoadedIndices[index]) {\n          isRowLoadedIndices[index] = true;\n\n          return false;\n        } else {\n          return true;\n        }\n      }\n\n      render(\n        getMarkup({\n          isRowLoaded,\n          minimumBatchSize: 10,\n          threshold: 0,\n        }),\n      );\n      // Simulate a new range of rows being loaded\n      innerOnRowsRendered({startIndex: 5, stopIndex: 10});\n      expect(loadMoreRowsCalls).toEqual([\n        {startIndex: 0, stopIndex: 9},\n        {startIndex: 10, stopIndex: 19},\n      ]);\n    });\n\n    it('should not exceed ending boundaries if :minimumBatchSize is larger than needed', () => {\n      render(\n        getMarkup({\n          minimumBatchSize: 10,\n          rowCount: 25,\n          threshold: 0,\n        }),\n      );\n      // Simulate a new range of rows being loaded\n      innerOnRowsRendered({startIndex: 18, stopIndex: 22});\n      expect(loadMoreRowsCalls).toEqual([\n        {startIndex: 0, stopIndex: 9},\n        {startIndex: 15, stopIndex: 24},\n      ]);\n    });\n\n    it('should not exceed beginning boundaries if :minimumBatchSize is larger than needed', () => {\n      render(\n        getMarkup({\n          minimumBatchSize: 10,\n          scrollToIndex: 15,\n          threshold: 0,\n        }),\n      );\n      loadMoreRowsCalls.splice(0);\n      render(\n        getMarkup({\n          isRowLoaded: ({index}) => index >= 6,\n          minimumBatchSize: 10,\n          scrollToIndex: 2,\n          threshold: 0,\n        }),\n      );\n      expect(loadMoreRowsCalls.length).toEqual(1);\n      expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 5}]);\n    });\n  });\n\n  // Verifies improved memoization; see bvaughn/react-virtualized/issues/345\n  it('should memoize calls to :loadMoreRows (not calling unless unloaded ranges have changed)', () => {\n    render(\n      getMarkup({\n        isRowLoaded: () => false,\n        minimumBatchSize: 20,\n        threshold: 0,\n      }),\n    );\n    expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 19}]);\n    innerOnRowsRendered({startIndex: 0, stopIndex: 15});\n    expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 19}]);\n    loadMoreRowsCalls.splice(0);\n    innerOnRowsRendered({startIndex: 0, stopIndex: 20});\n    expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 20}]);\n  });\n\n  it('resetLoadMoreRowsCache should reset memoized state', () => {\n    const component = render(\n      getMarkup({\n        isRowLoaded: () => false,\n        minimumBatchSize: 20,\n        threshold: 0,\n      }),\n    );\n    expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 19}]);\n    innerOnRowsRendered({startIndex: 0, stopIndex: 15});\n    loadMoreRowsCalls.splice(0);\n    expect(loadMoreRowsCalls).toEqual([]);\n    component.resetLoadMoreRowsCache();\n    innerOnRowsRendered({startIndex: 0, stopIndex: 15});\n    expect(loadMoreRowsCalls).toEqual([{startIndex: 0, stopIndex: 19}]);\n  });\n\n  it('resetLoadMoreRowsCache should call :loadMoreRows if :autoReload parameter is true', () => {\n    const component = render(\n      getMarkup({\n        isRowLoaded: () => false,\n        minimumBatchSize: 1,\n        threshold: 0,\n      }),\n    );\n\n    // Simulate a new range of rows being loaded\n    loadMoreRowsCalls.splice(0);\n    innerOnRowsRendered({startIndex: 0, stopIndex: 10});\n    component.resetLoadMoreRowsCache(true);\n    expect(loadMoreRowsCalls[loadMoreRowsCalls.length - 1]).toEqual({\n      startIndex: 0,\n      stopIndex: 10,\n    });\n\n    // Simulate a new range of rows being loaded\n    loadMoreRowsCalls.splice(0);\n    innerOnRowsRendered({startIndex: 20, stopIndex: 30});\n    expect(loadMoreRowsCalls[loadMoreRowsCalls.length - 1]).toEqual({\n      startIndex: 20,\n      stopIndex: 30,\n    });\n\n    loadMoreRowsCalls.splice(0);\n    component.resetLoadMoreRowsCache(true);\n    expect(loadMoreRowsCalls[loadMoreRowsCalls.length - 1]).toEqual({\n      startIndex: 20,\n      stopIndex: 30,\n    });\n  });\n});\n\ndescribe('scanForUnloadedRanges', () => {\n  function createIsRowLoaded(rows) {\n    return ({index}) => rows[index];\n  }\n\n  it('should return an empty array for a range of rows that have all been loaded', () => {\n    expect(\n      scanForUnloadedRanges({\n        isRowLoaded: createIsRowLoaded([true, true, true]),\n        startIndex: 0,\n        stopIndex: 2,\n      }),\n    ).toEqual([]);\n  });\n\n  it('return a range of only 1 unloaded row', () => {\n    expect(\n      scanForUnloadedRanges({\n        isRowLoaded: createIsRowLoaded([true, false, true]),\n        startIndex: 0,\n        stopIndex: 2,\n      }),\n    ).toEqual([{startIndex: 1, stopIndex: 1}]);\n  });\n\n  it('return a range of multiple unloaded rows', () => {\n    expect(\n      scanForUnloadedRanges({\n        isRowLoaded: createIsRowLoaded([false, false, true]),\n        startIndex: 0,\n        stopIndex: 2,\n      }),\n    ).toEqual([{startIndex: 0, stopIndex: 1}]);\n  });\n\n  it('return multiple ranges of unloaded rows', () => {\n    expect(\n      scanForUnloadedRanges({\n        isRowLoaded: createIsRowLoaded([\n          true,\n          false,\n          false,\n          true,\n          false,\n          true,\n          false,\n        ]),\n        startIndex: 0,\n        stopIndex: 6,\n      }),\n    ).toEqual([\n      {startIndex: 1, stopIndex: 2},\n      {startIndex: 4, stopIndex: 4},\n      {startIndex: 6, stopIndex: 6},\n    ]);\n  });\n});\n\ndescribe('isRangeVisible', () => {\n  it('first row(s) are visible', () => {\n    expect(\n      isRangeVisible({\n        lastRenderedStartIndex: 10,\n        lastRenderedStopIndex: 20,\n        startIndex: 20,\n        stopIndex: 30,\n      }),\n    ).toEqual(true);\n  });\n\n  it('last row(s) are visible', () => {\n    expect(\n      isRangeVisible({\n        lastRenderedStartIndex: 10,\n        lastRenderedStopIndex: 20,\n        startIndex: 0,\n        stopIndex: 10,\n      }),\n    ).toEqual(true);\n  });\n\n  it('all row(s) are visible', () => {\n    expect(\n      isRangeVisible({\n        lastRenderedStartIndex: 10,\n        lastRenderedStopIndex: 20,\n        startIndex: 12,\n        stopIndex: 14,\n      }),\n    ).toEqual(true);\n  });\n\n  it('no row(s) are visible', () => {\n    expect(\n      isRangeVisible({\n        lastRenderedStartIndex: 10,\n        lastRenderedStopIndex: 20,\n        startIndex: 0,\n        stopIndex: 9,\n      }),\n    ).toEqual(false);\n\n    expect(\n      isRangeVisible({\n        lastRenderedStartIndex: 10,\n        lastRenderedStopIndex: 20,\n        startIndex: 21,\n        stopIndex: 30,\n      }),\n    ).toEqual(false);\n  });\n});\n\ndescribe('forceUpdateReactVirtualizedComponent', () => {\n  it('should call :recomputeGridSize if defined', () => {\n    const recomputeGridSize = jest.fn();\n    class TestComponent extends React.Component {\n      recomputeGridSize = recomputeGridSize;\n      render() {\n        return <div />;\n      }\n    }\n    forceUpdateReactVirtualizedComponent(render(<TestComponent />), 10);\n    expect(recomputeGridSize).toHaveBeenCalledTimes(1);\n    expect(recomputeGridSize).toHaveBeenCalledWith(10);\n  });\n\n  it('should called :recomputeRowHeights if defined', () => {\n    const recomputeRowHeights = jest.fn();\n    class TestComponent extends React.Component {\n      recomputeRowHeights = recomputeRowHeights;\n      render() {\n        return <div />;\n      }\n    }\n    forceUpdateReactVirtualizedComponent(render(<TestComponent />), 10);\n    expect(recomputeRowHeights).toHaveBeenCalledTimes(1);\n    expect(recomputeRowHeights).toHaveBeenCalledWith(10);\n  });\n\n  it('should call :forceUpdate otherwise', () => {\n    const forceUpdate = jest.fn();\n    class TestComponent extends React.Component {\n      forceUpdate = forceUpdate;\n      render() {\n        return <div />;\n      }\n    }\n    forceUpdateReactVirtualizedComponent(render(<TestComponent />), 10);\n    expect(forceUpdate).toHaveBeenCalledTimes(1);\n  });\n});\n"
  },
  {
    "path": "source/InfiniteLoader/InfiniteLoader.js",
    "content": "/** @flow */\nimport * as React from 'react';\nimport PropTypes from 'prop-types';\nimport createCallbackMemoizer from '../utils/createCallbackMemoizer';\n\n/**\n * Higher-order component that manages lazy-loading for \"infinite\" data.\n * This component decorates a virtual component and just-in-time prefetches rows as a user scrolls.\n * It is intended as a convenience component; fork it if you'd like finer-grained control over data-loading.\n */\nexport default class InfiniteLoader extends React.PureComponent {\n  static propTypes = {\n    /**\n     * Function responsible for rendering a virtualized component.\n     * This function should implement the following signature:\n     * ({ onRowsRendered, registerChild }) => PropTypes.element\n     *\n     * The specified :onRowsRendered function should be passed through to the child's :onRowsRendered property.\n     * The :registerChild callback should be set as the virtualized component's :ref.\n     */\n    children: PropTypes.func.isRequired,\n\n    /**\n     * Function responsible for tracking the loaded state of each row.\n     * It should implement the following signature: ({ index: number }): boolean\n     */\n    isRowLoaded: PropTypes.func.isRequired,\n\n    /**\n     * Callback to be invoked when more rows must be loaded.\n     * It should implement the following signature: ({ startIndex, stopIndex }): Promise\n     * The returned Promise should be resolved once row data has finished loading.\n     * It will be used to determine when to refresh the list with the newly-loaded data.\n     * This callback may be called multiple times in reaction to a single scroll event.\n     */\n    loadMoreRows: PropTypes.func.isRequired,\n\n    /**\n     * Minimum number of rows to be loaded at a time.\n     * This property can be used to batch requests to reduce HTTP requests.\n     */\n    minimumBatchSize: PropTypes.number.isRequired,\n\n    /**\n     * Number of rows in list; can be arbitrary high number if actual number is unknown.\n     */\n    rowCount: PropTypes.number.isRequired,\n\n    /**\n     * Threshold at which to pre-fetch data.\n     * A threshold X means that data will start loading when a user scrolls within X rows.\n     * This value defaults to 15.\n     */\n    threshold: PropTypes.number.isRequired,\n  };\n\n  static defaultProps = {\n    minimumBatchSize: 10,\n    rowCount: 0,\n    threshold: 15,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._loadMoreRowsMemoizer = createCallbackMemoizer();\n\n    this._onRowsRendered = this._onRowsRendered.bind(this);\n    this._registerChild = this._registerChild.bind(this);\n  }\n\n  resetLoadMoreRowsCache(autoReload) {\n    this._loadMoreRowsMemoizer = createCallbackMemoizer();\n\n    if (autoReload) {\n      this._doStuff(this._lastRenderedStartIndex, this._lastRenderedStopIndex);\n    }\n  }\n\n  render() {\n    const {children} = this.props;\n\n    return children({\n      onRowsRendered: this._onRowsRendered,\n      registerChild: this._registerChild,\n    });\n  }\n\n  _loadUnloadedRanges(unloadedRanges) {\n    const {loadMoreRows} = this.props;\n\n    unloadedRanges.forEach(unloadedRange => {\n      let promise = loadMoreRows(unloadedRange);\n      if (promise) {\n        promise.then(() => {\n          // Refresh the visible rows if any of them have just been loaded.\n          // Otherwise they will remain in their unloaded visual state.\n          if (\n            isRangeVisible({\n              lastRenderedStartIndex: this._lastRenderedStartIndex,\n              lastRenderedStopIndex: this._lastRenderedStopIndex,\n              startIndex: unloadedRange.startIndex,\n              stopIndex: unloadedRange.stopIndex,\n            })\n          ) {\n            if (this._registeredChild) {\n              forceUpdateReactVirtualizedComponent(\n                this._registeredChild,\n                this._lastRenderedStartIndex,\n              );\n            }\n          }\n        });\n      }\n    });\n  }\n\n  _onRowsRendered({startIndex, stopIndex}) {\n    this._lastRenderedStartIndex = startIndex;\n    this._lastRenderedStopIndex = stopIndex;\n\n    this._doStuff(startIndex, stopIndex);\n  }\n\n  _doStuff(startIndex, stopIndex) {\n    const {isRowLoaded, minimumBatchSize, rowCount, threshold} = this.props;\n\n    const unloadedRanges = scanForUnloadedRanges({\n      isRowLoaded,\n      minimumBatchSize,\n      rowCount,\n      startIndex: Math.max(0, startIndex - threshold),\n      stopIndex: Math.min(rowCount - 1, stopIndex + threshold),\n    });\n\n    // For memoize comparison\n    const squashedUnloadedRanges = [].concat(\n      ...unloadedRanges.map(({startIndex, stopIndex}) => [\n        startIndex,\n        stopIndex,\n      ]),\n    );\n\n    this._loadMoreRowsMemoizer({\n      callback: () => {\n        this._loadUnloadedRanges(unloadedRanges);\n      },\n      indices: {squashedUnloadedRanges},\n    });\n  }\n\n  _registerChild(registeredChild) {\n    this._registeredChild = registeredChild;\n  }\n}\n\n/**\n * Determines if the specified start/stop range is visible based on the most recently rendered range.\n */\nexport function isRangeVisible({\n  lastRenderedStartIndex,\n  lastRenderedStopIndex,\n  startIndex,\n  stopIndex,\n}) {\n  return !(\n    startIndex > lastRenderedStopIndex || stopIndex < lastRenderedStartIndex\n  );\n}\n\n/**\n * Returns all of the ranges within a larger range that contain unloaded rows.\n */\nexport function scanForUnloadedRanges({\n  isRowLoaded,\n  minimumBatchSize,\n  rowCount,\n  startIndex,\n  stopIndex,\n}) {\n  const unloadedRanges = [];\n\n  let rangeStartIndex = null;\n  let rangeStopIndex = null;\n\n  for (let index = startIndex; index <= stopIndex; index++) {\n    let loaded = isRowLoaded({index});\n\n    if (!loaded) {\n      rangeStopIndex = index;\n      if (rangeStartIndex === null) {\n        rangeStartIndex = index;\n      }\n    } else if (rangeStopIndex !== null) {\n      unloadedRanges.push({\n        startIndex: rangeStartIndex,\n        stopIndex: rangeStopIndex,\n      });\n\n      rangeStartIndex = rangeStopIndex = null;\n    }\n  }\n\n  // If :rangeStopIndex is not null it means we haven't ran out of unloaded rows.\n  // Scan forward to try filling our :minimumBatchSize.\n  if (rangeStopIndex !== null) {\n    const potentialStopIndex = Math.min(\n      Math.max(rangeStopIndex, rangeStartIndex + minimumBatchSize - 1),\n      rowCount - 1,\n    );\n\n    for (let index = rangeStopIndex + 1; index <= potentialStopIndex; index++) {\n      if (!isRowLoaded({index})) {\n        rangeStopIndex = index;\n      } else {\n        break;\n      }\n    }\n\n    unloadedRanges.push({\n      startIndex: rangeStartIndex,\n      stopIndex: rangeStopIndex,\n    });\n  }\n\n  // Check to see if our first range ended prematurely.\n  // In this case we should scan backwards to try filling our :minimumBatchSize.\n  if (unloadedRanges.length) {\n    const firstUnloadedRange = unloadedRanges[0];\n\n    while (\n      firstUnloadedRange.stopIndex - firstUnloadedRange.startIndex + 1 <\n        minimumBatchSize &&\n      firstUnloadedRange.startIndex > 0\n    ) {\n      let index = firstUnloadedRange.startIndex - 1;\n\n      if (!isRowLoaded({index})) {\n        firstUnloadedRange.startIndex = index;\n      } else {\n        break;\n      }\n    }\n  }\n\n  return unloadedRanges;\n}\n\n/**\n * Since RV components use shallowCompare we need to force a render (even though props haven't changed).\n * However InfiniteLoader may wrap a Grid or it may wrap a Table or List.\n * In the first case the built-in React forceUpdate() method is sufficient to force a re-render,\n * But in the latter cases we need to use the RV-specific forceUpdateGrid() method.\n * Else the inner Grid will not be re-rendered and visuals may be stale.\n *\n * Additionally, while a Grid is scrolling the cells can be cached,\n * So it's important to invalidate that cache by recalculating sizes\n * before forcing a rerender.\n */\nexport function forceUpdateReactVirtualizedComponent(\n  component,\n  currentIndex = 0,\n) {\n  const recomputeSize =\n    typeof component.recomputeGridSize === 'function'\n      ? component.recomputeGridSize\n      : component.recomputeRowHeights;\n\n  if (recomputeSize) {\n    recomputeSize.call(component, currentIndex);\n  } else {\n    component.forceUpdate();\n  }\n}\n"
  },
  {
    "path": "source/InfiniteLoader/index.js",
    "content": "/** @flow */\nimport InfiniteLoader from './InfiniteLoader';\n\nexport default InfiniteLoader;\nexport {InfiniteLoader};\n"
  },
  {
    "path": "source/List/List.example.css",
    "content": ".List {\n  width: 100%;\n  border: 1px solid #DDD;\n  margin-top: 15px;\n}\n\n.row {\n  height: 100%;\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  padding: 0 25px;\n  background-color: #fff;\n  border-bottom: 1px solid #e0e0e0;\n}\n.letter {\n  display: inline-block;\n  height: 40px;\n  width: 40px;\n  line-height: 40px;\n  text-align: center;\n  border-radius: 40px;\n  color: white;\n  font-size: 1.5em;\n  margin-right: 25px;\n}\n.name {\n  font-weight: bold;\n  margin-bottom: 2px;\n}\n.index {\n  color: #37474f;\n}\n.height {\n  flex: 1;\n  text-align: right;\n  color: #bdbdbd;\n  font-size: .75em;\n  font-weight: 100;\n}\n\n.noRows {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #bdbdbd;\n}\n\n.checkboxLabel {\n  margin-left: .5rem;\n}\n.checkboxLabel:first-of-type {\n  margin-left: 0;\n}\n.checkbox {\n  margin-right: 5px;\n}\n\n.isScrollingPlaceholder {\n  color: #DDD;\n  font-style: italic;\n}\n"
  },
  {
    "path": "source/List/List.example.js",
    "content": "import clsx from 'clsx';\nimport Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport styles from './List.example.css';\nimport AutoSizer from '../AutoSizer';\nimport List from './List';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\n\nexport default class ListExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      listHeight: 300,\n      listRowHeight: 50,\n      overscanRowCount: 10,\n      rowCount: context.list.size,\n      scrollToIndex: undefined,\n      showScrollingPlaceholder: false,\n      useDynamicRowHeight: false,\n    };\n\n    this._getRowHeight = this._getRowHeight.bind(this);\n    this._noRowsRenderer = this._noRowsRenderer.bind(this);\n    this._onRowCountChange = this._onRowCountChange.bind(this);\n    this._onScrollToRowChange = this._onScrollToRowChange.bind(this);\n    this._rowRenderer = this._rowRenderer.bind(this);\n  }\n\n  render() {\n    const {\n      listHeight,\n      listRowHeight,\n      overscanRowCount,\n      rowCount,\n      scrollToIndex,\n      showScrollingPlaceholder,\n      useDynamicRowHeight,\n    } = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"List\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/List/List.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md\"\n        />\n\n        <ContentBoxParagraph>\n          The list below is windowed (or \"virtualized\") meaning that only the\n          visible rows are rendered. Adjust its configurable properties below to\n          see how it reacts.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Use dynamic row heights?\"\n              checked={useDynamicRowHeight}\n              className={styles.checkbox}\n              type=\"checkbox\"\n              onChange={event =>\n                this.setState({useDynamicRowHeight: event.target.checked})\n              }\n            />\n            Use dynamic row heights?\n          </label>\n\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Show scrolling placeholder?\"\n              checked={showScrollingPlaceholder}\n              className={styles.checkbox}\n              type=\"checkbox\"\n              onChange={event =>\n                this.setState({\n                  showScrollingPlaceholder: event.target.checked,\n                })\n              }\n            />\n            Show scrolling placeholder?\n          </label>\n        </ContentBoxParagraph>\n\n        <InputRow>\n          <LabeledInput\n            label=\"Num rows\"\n            name=\"rowCount\"\n            onChange={this._onRowCountChange}\n            value={rowCount}\n          />\n          <LabeledInput\n            label=\"Scroll to\"\n            name=\"onScrollToRow\"\n            placeholder=\"Index...\"\n            onChange={this._onScrollToRowChange}\n            value={scrollToIndex || ''}\n          />\n          <LabeledInput\n            label=\"List height\"\n            name=\"listHeight\"\n            onChange={event =>\n              this.setState({\n                listHeight: parseInt(event.target.value, 10) || 1,\n              })\n            }\n            value={listHeight}\n          />\n          <LabeledInput\n            disabled={useDynamicRowHeight}\n            label=\"Row height\"\n            name=\"listRowHeight\"\n            onChange={event =>\n              this.setState({\n                listRowHeight: parseInt(event.target.value, 10) || 1,\n              })\n            }\n            value={listRowHeight}\n          />\n          <LabeledInput\n            label=\"Overscan\"\n            name=\"overscanRowCount\"\n            onChange={event =>\n              this.setState({\n                overscanRowCount: parseInt(event.target.value, 10) || 0,\n              })\n            }\n            value={overscanRowCount}\n          />\n        </InputRow>\n\n        <div>\n          <AutoSizer disableHeight>\n            {({width}) => (\n              <List\n                ref=\"List\"\n                className={styles.List}\n                height={listHeight}\n                overscanRowCount={overscanRowCount}\n                noRowsRenderer={this._noRowsRenderer}\n                rowCount={rowCount}\n                rowHeight={\n                  useDynamicRowHeight ? this._getRowHeight : listRowHeight\n                }\n                rowRenderer={this._rowRenderer}\n                scrollToIndex={scrollToIndex}\n                width={width}\n              />\n            )}\n          </AutoSizer>\n        </div>\n      </ContentBox>\n    );\n  }\n\n  _getDatum(index) {\n    const {list} = this.context;\n\n    return list.get(index % list.size);\n  }\n\n  _getRowHeight({index}) {\n    return this._getDatum(index).size;\n  }\n\n  _noRowsRenderer() {\n    return <div className={styles.noRows}>No rows</div>;\n  }\n\n  _onRowCountChange(event) {\n    const rowCount = parseInt(event.target.value, 10) || 0;\n\n    this.setState({rowCount});\n  }\n\n  _onScrollToRowChange(event) {\n    const {rowCount} = this.state;\n    let scrollToIndex = Math.min(\n      rowCount - 1,\n      parseInt(event.target.value, 10),\n    );\n\n    if (isNaN(scrollToIndex)) {\n      scrollToIndex = undefined;\n    }\n\n    this.setState({scrollToIndex});\n  }\n\n  _rowRenderer({index, isScrolling, key, style}) {\n    const {showScrollingPlaceholder, useDynamicRowHeight} = this.state;\n\n    if (showScrollingPlaceholder && isScrolling) {\n      return (\n        <div\n          className={clsx(styles.row, styles.isScrollingPlaceholder)}\n          key={key}\n          style={style}>\n          Scrolling...\n        </div>\n      );\n    }\n\n    const datum = this._getDatum(index);\n\n    let additionalContent;\n\n    if (useDynamicRowHeight) {\n      switch (datum.size) {\n        case 75:\n          additionalContent = <div>It is medium-sized.</div>;\n          break;\n        case 100:\n          additionalContent = (\n            <div>\n              It is large-sized.\n              <br />\n              It has a 3rd row.\n            </div>\n          );\n          break;\n      }\n    }\n\n    return (\n      <div className={styles.row} key={key} style={style}>\n        <div\n          className={styles.letter}\n          style={{\n            backgroundColor: datum.color,\n          }}>\n          {datum.name.charAt(0)}\n        </div>\n        <div>\n          <div className={styles.name}>{datum.name}</div>\n          <div className={styles.index}>This is row {index}</div>\n          {additionalContent}\n        </div>\n        {useDynamicRowHeight && (\n          <span className={styles.height}>{datum.size}px</span>\n        )}\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "source/List/List.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport {Simulate} from 'react-dom/test-utils';\nimport Immutable from 'immutable';\nimport List from './List';\nimport {defaultOverscanIndicesGetter} from '../Grid';\n\ndescribe('List', () => {\n  const array = [];\n  for (var i = 0; i < 100; i++) {\n    array.push(`Name ${i}`);\n  }\n  const names = Immutable.fromJS(array);\n\n  // Override default behavior of overscanning by at least 1 (for accessibility)\n  // Because it makes for simple tests below\n  function overscanIndicesGetter({startIndex, stopIndex}) {\n    return {\n      overscanStartIndex: startIndex,\n      overscanStopIndex: stopIndex,\n    };\n  }\n\n  function getMarkup(props = {}) {\n    function rowRenderer({index, key, style}) {\n      return (\n        <div className=\"listItem\" key={key} style={style}>\n          {names.get(index)}\n        </div>\n      );\n    }\n\n    return (\n      <List\n        height={100}\n        overscanIndicesGetter={overscanIndicesGetter}\n        overscanRowCount={0}\n        rowHeight={10}\n        rowCount={names.size}\n        rowRenderer={rowRenderer}\n        width={100}\n        {...props}\n      />\n    );\n  }\n\n  describe('number of rendered children', () => {\n    it('should render enough children to fill the view', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.querySelectorAll('.listItem').length).toEqual(10);\n    });\n\n    it('should not render more children than available if the list is not filled', () => {\n      const rendered = findDOMNode(render(getMarkup({rowCount: 5})));\n      expect(rendered.querySelectorAll('.listItem').length).toEqual(5);\n    });\n  });\n\n  describe('scrollToPosition', () => {\n    it('should scroll to the top', () => {\n      const instance = render(\n        getMarkup({\n          rowHeight: 10,\n        }),\n      );\n      instance.scrollToPosition(100);\n      const rendered = findDOMNode(instance);\n      expect(rendered.textContent).toContain('Name 10');\n      expect(rendered.textContent).toContain('Name 19');\n    });\n  });\n\n  /** Tests scrolling via initial props */\n  describe('scrollToIndex', () => {\n    it('should scroll to the top', () => {\n      const rendered = findDOMNode(render(getMarkup({scrollToIndex: 0})));\n      expect(rendered.textContent).toContain('Name 0');\n    });\n\n    it('should scroll down to the middle', () => {\n      const rendered = findDOMNode(render(getMarkup({scrollToIndex: 49})));\n      // 100 items * 10 item height = 1,000 total item height\n      // 10 items can be visible at a time and :scrollTop is initially 0,\n      // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view).\n      expect(rendered.textContent).toContain('Name 49');\n    });\n\n    it('should scroll to the bottom', () => {\n      const rendered = findDOMNode(render(getMarkup({scrollToIndex: 99})));\n      // 100 height - 10 header = 90 available scroll space.\n      // 100 items * 10 item height = 1,000 total item height\n      // Target height for the last item then is 1000 - 90\n      expect(rendered.textContent).toContain('Name 99');\n    });\n\n    it('should scroll to the correct position for :scrollToAlignment \"start\"', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            scrollToAlignment: 'start',\n            scrollToIndex: 49,\n          }),\n        ),\n      );\n      // 100 items * 10 item height = 1,000 total item height; 10 items can be visible at a time.\n      expect(rendered.textContent).toContain('Name 49');\n      expect(rendered.textContent).toContain('Name 58');\n    });\n\n    it('should scroll to the correct position for :scrollToAlignment \"end\"', () => {\n      render(\n        getMarkup({\n          scrollToIndex: 99,\n        }),\n      );\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            scrollToAlignment: 'end',\n            scrollToIndex: 49,\n          }),\n        ),\n      );\n      // 100 items * 10 item height = 1,000 total item height; 10 items can be visible at a time.\n      expect(rendered.textContent).toContain('Name 40');\n      expect(rendered.textContent).toContain('Name 49');\n    });\n\n    it('should scroll to the correct position for :scrollToAlignment \"center\"', () => {\n      render(\n        getMarkup({\n          scrollToIndex: 99,\n        }),\n      );\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            scrollToAlignment: 'center',\n            scrollToIndex: 49,\n          }),\n        ),\n      );\n      // 100 items * 10 item height = 1,000 total item height; 11 items can be visible at a time (the first and last item are only partially visible)\n      expect(rendered.textContent).toContain('Name 44');\n      expect(rendered.textContent).toContain('Name 54');\n    });\n  });\n\n  describe('property updates', () => {\n    it('should update :scrollToIndex position when :rowHeight changes', () => {\n      let rendered = findDOMNode(render(getMarkup({scrollToIndex: 50})));\n      expect(rendered.textContent).toContain('Name 50');\n      // Making rows taller pushes name off/beyond the scrolled area\n      rendered = findDOMNode(\n        render(getMarkup({scrollToIndex: 50, rowHeight: 20})),\n      );\n      expect(rendered.textContent).toContain('Name 50');\n    });\n\n    it('should update :scrollToIndex position when :height changes', () => {\n      let rendered = findDOMNode(render(getMarkup({scrollToIndex: 50})));\n      expect(rendered.textContent).toContain('Name 50');\n      // Making the list shorter leaves only room for 1 item\n      rendered = findDOMNode(\n        render(getMarkup({scrollToIndex: 50, height: 20})),\n      );\n      expect(rendered.textContent).toContain('Name 50');\n    });\n\n    it('should update :scrollToIndex position when :scrollToIndex changes', () => {\n      let rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.textContent).not.toContain('Name 50');\n      rendered = findDOMNode(render(getMarkup({scrollToIndex: 50})));\n      expect(rendered.textContent).toContain('Name 50');\n    });\n\n    it('should update scroll position if size shrinks smaller than the current scroll', () => {\n      findDOMNode(render(getMarkup({scrollToIndex: 500})));\n      findDOMNode(render(getMarkup()));\n      const rendered = findDOMNode(\n        render(getMarkup({scrollToIndex: 500, rowCount: 10})),\n      );\n      expect(rendered.textContent).toContain('Name 9');\n    });\n  });\n\n  describe('noRowsRenderer', () => {\n    it('should call :noRowsRenderer if :rowCount is 0', () => {\n      let rendered = findDOMNode(\n        render(\n          getMarkup({\n            noRowsRenderer: () => <div>No rows!</div>,\n            rowCount: 0,\n          }),\n        ),\n      );\n      expect(rendered.textContent).toEqual('No rows!');\n    });\n\n    it('should render an empty body if :rowCount is 0 and there is no :noRowsRenderer', () => {\n      let rendered = findDOMNode(\n        render(\n          getMarkup({\n            rowCount: 0,\n          }),\n        ),\n      );\n      expect(rendered.textContent).toEqual('');\n    });\n  });\n\n  describe('onRowsRendered', () => {\n    it('should call :onRowsRendered if at least one row is rendered', () => {\n      let startIndex, stopIndex;\n      render(\n        getMarkup({\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n        }),\n      );\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(9);\n    });\n\n    it('should not call :onRowsRendered unless the start or stop indices have changed', () => {\n      let numCalls = 0;\n      let startIndex;\n      let stopIndex;\n      const onRowsRendered = params => {\n        startIndex = params.startIndex;\n        stopIndex = params.stopIndex;\n        numCalls++;\n      };\n      findDOMNode(render(getMarkup({onRowsRendered})));\n      expect(numCalls).toEqual(1);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(9);\n      findDOMNode(render(getMarkup({onRowsRendered})));\n      expect(numCalls).toEqual(1);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(9);\n    });\n\n    it('should call :onRowsRendered if the start or stop indices have changed', () => {\n      let numCalls = 0;\n      let startIndex;\n      let stopIndex;\n      const onRowsRendered = params => {\n        startIndex = params.startIndex;\n        stopIndex = params.stopIndex;\n        numCalls++;\n      };\n      findDOMNode(render(getMarkup({onRowsRendered})));\n      expect(numCalls).toEqual(1);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(9);\n      findDOMNode(\n        render(\n          getMarkup({\n            height: 50,\n            onRowsRendered,\n          }),\n        ),\n      );\n      expect(numCalls).toEqual(2);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(4);\n    });\n\n    it('should not call :onRowsRendered if no rows are rendered', () => {\n      let startIndex, stopIndex;\n      render(\n        getMarkup({\n          height: 0,\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n        }),\n      );\n      expect(startIndex).toEqual(undefined);\n      expect(stopIndex).toEqual(undefined);\n    });\n  });\n\n  describe(':scrollTop property', () => {\n    it('should render correctly when an initial :scrollTop property is specified', () => {\n      let startIndex, stopIndex;\n      render(\n        getMarkup({\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n          scrollTop: 100,\n        }),\n      );\n      expect(startIndex).toEqual(10);\n      expect(stopIndex).toEqual(19);\n    });\n\n    it('should render correctly when :scrollTop property is updated', () => {\n      let startIndex, stopIndex;\n\n      findDOMNode(\n        render(\n          getMarkup({\n            onRowsRendered: params => ({startIndex, stopIndex} = params),\n          }),\n        ),\n      );\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(9);\n\n      findDOMNode(\n        render(\n          getMarkup({\n            onRowsRendered: params => ({startIndex, stopIndex} = params),\n            scrollTop: 100,\n          }),\n        ),\n      );\n      expect(startIndex).toEqual(10);\n      expect(stopIndex).toEqual(19);\n    });\n  });\n\n  describe('styles, classNames, and ids', () => {\n    it('should use the expected global CSS classNames', () => {\n      const node = findDOMNode(render(getMarkup()));\n      expect(node.className).toContain('ReactVirtualized__List');\n    });\n\n    it('should use a custom :className if specified', () => {\n      const node = findDOMNode(render(getMarkup({className: 'foo'})));\n      expect(node.className).toContain('foo');\n    });\n\n    it('should use a custom :id if specified', () => {\n      const node = findDOMNode(render(getMarkup({id: 'bar'})));\n      expect(node.getAttribute('id')).toEqual('bar');\n    });\n\n    it('should use a custom :style if specified', () => {\n      const style = {backgroundColor: 'red'};\n      const rendered = findDOMNode(render(getMarkup({style})));\n      expect(rendered.style.backgroundColor).toEqual('red');\n    });\n\n    it('should set the width of a row to be 100% by default', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const cell = rendered.querySelector('.listItem');\n      expect(cell.style.width).toEqual('100%');\n    });\n  });\n\n  describe('overscanRowCount', () => {\n    it('should not overscan by default', () => {\n      const mock = jest.fn();\n      mock.mockImplementation(overscanIndicesGetter);\n\n      render(\n        getMarkup({\n          overscanIndicesGetter: mock,\n        }),\n      );\n      expect(mock.mock.calls[0][0].overscanCellsCount).toEqual(0);\n      expect(mock.mock.calls[1][0].overscanCellsCount).toEqual(0);\n    });\n\n    it('should overscan the specified amount', () => {\n      const mock = jest.fn();\n      mock.mockImplementation(overscanIndicesGetter);\n\n      render(\n        getMarkup({\n          overscanIndicesGetter: mock,\n          overscanRowCount: 10,\n        }),\n      );\n      expect(mock.mock.calls[0][0].overscanCellsCount).toEqual(0);\n      expect(mock.mock.calls[1][0].overscanCellsCount).toEqual(10);\n    });\n  });\n\n  describe('onScroll', () => {\n    it('should trigger callback when component initially mounts', () => {\n      const onScrollCalls = [];\n      render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      expect(onScrollCalls).toEqual([\n        {\n          clientHeight: 100,\n          scrollHeight: 1000,\n          scrollTop: 0,\n        },\n      ]);\n    });\n\n    it('should trigger callback when component scrolls', () => {\n      const onScrollCalls = [];\n      const rendered = render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      const target = {\n        scrollLeft: 0,\n        scrollTop: 100,\n      };\n      rendered.Grid._scrollingContainer = target; // HACK to work around _onScroll target check\n      Simulate.scroll(findDOMNode(rendered), {target});\n      expect(onScrollCalls[onScrollCalls.length - 1]).toEqual({\n        clientHeight: 100,\n        scrollHeight: 1000,\n        scrollTop: 100,\n      });\n    });\n  });\n\n  describe('measureAllRows', () => {\n    it('should measure any unmeasured rows', () => {\n      const rendered = render(\n        getMarkup({\n          estimatedRowSize: 15,\n          height: 0,\n          rowCount: 10,\n          rowHeight: () => 20,\n          width: 0,\n        }),\n      );\n      expect(\n        rendered.Grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize(),\n      ).toEqual(150);\n      rendered.measureAllRows();\n      expect(\n        rendered.Grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize(),\n      ).toEqual(200);\n    });\n  });\n\n  describe('recomputeRowHeights', () => {\n    it('should recompute row heights and other values when called', () => {\n      const indices = [];\n      const rowHeight = ({index}) => {\n        indices.push(index);\n        return 10;\n      };\n      const component = render(\n        getMarkup({\n          rowHeight,\n          rowCount: 50,\n        }),\n      );\n\n      indices.splice(0);\n      component.recomputeRowHeights();\n\n      // Only the rows required to fill the current viewport will be rendered\n      expect(indices[0]).toEqual(0);\n      expect(indices[indices.length - 1]).toEqual(9);\n\n      indices.splice(0);\n      component.recomputeRowHeights(4);\n\n      expect(indices[0]).toEqual(4);\n      expect(indices[indices.length - 1]).toEqual(9);\n    });\n  });\n\n  describe('forceUpdateGrid', () => {\n    it('should refresh inner Grid content when called', () => {\n      let marker = 'a';\n      function rowRenderer({index, key, style}) {\n        return (\n          <div key={key} style={style}>\n            {index}\n            {marker}\n          </div>\n        );\n      }\n      const component = render(getMarkup({rowRenderer}));\n      const node = findDOMNode(component);\n      expect(node.textContent).toContain('1a');\n      marker = 'b';\n      component.forceUpdateGrid();\n      expect(node.textContent).toContain('1b');\n    });\n  });\n\n  describe('tabIndex', () => {\n    it('should be focusable by default', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(rendered.tabIndex).toEqual(0);\n    });\n\n    it('should allow tabIndex to be overridden', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            tabIndex: -1,\n          }),\n        ),\n      );\n      expect(rendered.tabIndex).toEqual(-1);\n    });\n  });\n\n  it('should pass the cellRenderer an :isVisible flag', () => {\n    const rowRendererCalls = [];\n    function rowRenderer(props) {\n      rowRendererCalls.push(props);\n      return null;\n    }\n    findDOMNode(\n      render(\n        getMarkup({\n          height: 50,\n          overscanIndicesGetter: defaultOverscanIndicesGetter,\n          overscanRowCount: 1,\n          rowHeight: 50,\n          rowRenderer,\n        }),\n      ),\n    );\n    expect(rowRendererCalls[0].isVisible).toEqual(true);\n    expect(rowRendererCalls[1].isVisible).toEqual(false);\n  });\n\n  it('should relay the Grid :parent param to the :rowRenderer', () => {\n    const rowRenderer = jest.fn().mockReturnValue(null);\n    findDOMNode(render(getMarkup({rowRenderer})));\n    expect(rowRenderer.mock.calls[0][0].parent).not.toBeUndefined();\n  });\n\n  describe('pure', () => {\n    it('should not re-render unless props have changed', () => {\n      let rowRendererCalled = false;\n      function rowRenderer({index, key, style}) {\n        rowRendererCalled = true;\n        return (\n          <div key={key} style={style}>\n            {index}\n          </div>\n        );\n      }\n      const markup = getMarkup({rowRenderer});\n      render(markup);\n      expect(rowRendererCalled).toEqual(true);\n      rowRendererCalled = false;\n      render(markup);\n      expect(rowRendererCalled).toEqual(false);\n    });\n  });\n\n  it('should set the width of the single-column inner Grid to auto', () => {\n    const rendered = findDOMNode(render(getMarkup()));\n    expect(\n      rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer')\n        .style.width,\n    ).toEqual('auto');\n  });\n});\n"
  },
  {
    "path": "source/List/List.js",
    "content": "/** @flow */\n\nimport type {\n  NoContentRenderer,\n  Alignment,\n  CellSize,\n  CellPosition,\n  OverscanIndicesGetter,\n  RenderedSection,\n  CellRendererParams,\n  Scroll as GridScroll,\n} from '../Grid';\nimport type {RowRenderer, RenderedRows, Scroll} from './types';\n\nimport Grid, {accessibilityOverscanIndicesGetter} from '../Grid';\nimport * as React from 'react';\nimport clsx from 'clsx';\n\n/**\n * It is inefficient to create and manage a large list of DOM elements within a scrolling container\n * if only a few of those elements are visible. The primary purpose of this component is to improve\n * performance by only rendering the DOM nodes that a user is able to see based on their current\n * scroll position.\n *\n * This component renders a virtualized list of elements with either fixed or dynamic heights.\n */\n\ntype Props = {\n  'aria-label'?: string,\n\n  /**\n   * Removes fixed height from the scrollingContainer so that the total height\n   * of rows can stretch the window. Intended for use with WindowScroller\n   */\n  autoHeight: boolean,\n\n  /** Optional CSS class name */\n  className?: string,\n\n  /**\n   * Used to estimate the total height of a List before all of its rows have actually been measured.\n   * The estimated total height is adjusted as rows are rendered.\n   */\n  estimatedRowSize: number,\n\n  /** Height constraint for list (determines how many actual rows are rendered) */\n  height: number,\n\n  /** Optional renderer to be used in place of rows when rowCount is 0 */\n  noRowsRenderer: NoContentRenderer,\n\n  /** Callback invoked with information about the slice of rows that were just rendered.  */\n\n  onRowsRendered: (params: RenderedRows) => void,\n\n  /**\n   * Callback invoked whenever the scroll offset changes within the inner scrollable region.\n   * This callback can be used to sync scrolling between lists, tables, or grids.\n   */\n  onScroll: (params: Scroll) => void,\n\n  /** See Grid#overscanIndicesGetter */\n  overscanIndicesGetter: OverscanIndicesGetter,\n\n  /**\n   * Number of rows to render above/below the visible bounds of the list.\n   * These rows can help for smoother scrolling on touch devices.\n   */\n  overscanRowCount: number,\n\n  /** Either a fixed row height (number) or a function that returns the height of a row given its index.  */\n  rowHeight: CellSize,\n\n  /** Responsible for rendering a row given an index; ({ index: number }): node */\n  rowRenderer: RowRenderer,\n\n  /** Number of rows in list. */\n  rowCount: number,\n\n  /** See Grid#scrollToAlignment */\n  scrollToAlignment: Alignment,\n\n  /** Row index to ensure visible (by forcefully scrolling if necessary) */\n  scrollToIndex: number,\n\n  /** Vertical offset. */\n  scrollTop?: number,\n\n  /** Optional inline style */\n  style: Object,\n\n  /** Tab index for focus */\n  tabIndex?: number,\n\n  /** Width of list */\n  width: number,\n};\n\nexport default class List extends React.PureComponent<Props> {\n  static defaultProps = {\n    autoHeight: false,\n    estimatedRowSize: 30,\n    onScroll: () => {},\n    noRowsRenderer: () => null,\n    onRowsRendered: () => {},\n    overscanIndicesGetter: accessibilityOverscanIndicesGetter,\n    overscanRowCount: 10,\n    scrollToAlignment: 'auto',\n    scrollToIndex: -1,\n    style: {},\n  };\n\n  Grid: ?React.ElementRef<typeof Grid>;\n\n  forceUpdateGrid() {\n    if (this.Grid) {\n      this.Grid.forceUpdate();\n    }\n  }\n\n  /** See Grid#getOffsetForCell */\n  getOffsetForRow({alignment, index}: {alignment: Alignment, index: number}) {\n    if (this.Grid) {\n      const {scrollTop} = this.Grid.getOffsetForCell({\n        alignment,\n        rowIndex: index,\n        columnIndex: 0,\n      });\n\n      return scrollTop;\n    }\n    return 0;\n  }\n\n  /** CellMeasurer compatibility */\n  invalidateCellSizeAfterRender({columnIndex, rowIndex}: CellPosition) {\n    if (this.Grid) {\n      this.Grid.invalidateCellSizeAfterRender({\n        rowIndex,\n        columnIndex,\n      });\n    }\n  }\n\n  /** See Grid#measureAllCells */\n  measureAllRows() {\n    if (this.Grid) {\n      this.Grid.measureAllCells();\n    }\n  }\n\n  /** CellMeasurer compatibility */\n  recomputeGridSize({columnIndex = 0, rowIndex = 0}: CellPosition = {}) {\n    if (this.Grid) {\n      this.Grid.recomputeGridSize({\n        rowIndex,\n        columnIndex,\n      });\n    }\n  }\n\n  /** See Grid#recomputeGridSize */\n  recomputeRowHeights(index: number = 0) {\n    if (this.Grid) {\n      this.Grid.recomputeGridSize({\n        rowIndex: index,\n        columnIndex: 0,\n      });\n    }\n  }\n\n  /** See Grid#scrollToPosition */\n  scrollToPosition(scrollTop: number = 0) {\n    if (this.Grid) {\n      this.Grid.scrollToPosition({scrollTop});\n    }\n  }\n\n  /** See Grid#scrollToCell */\n  scrollToRow(index: number = 0) {\n    if (this.Grid) {\n      this.Grid.scrollToCell({\n        columnIndex: 0,\n        rowIndex: index,\n      });\n    }\n  }\n\n  render() {\n    const {className, noRowsRenderer, scrollToIndex, width} = this.props;\n\n    const classNames = clsx('ReactVirtualized__List', className);\n\n    return (\n      <Grid\n        {...this.props}\n        autoContainerWidth\n        cellRenderer={this._cellRenderer}\n        className={classNames}\n        columnWidth={width}\n        columnCount={1}\n        noContentRenderer={noRowsRenderer}\n        onScroll={this._onScroll}\n        onSectionRendered={this._onSectionRendered}\n        ref={this._setRef}\n        scrollToRow={scrollToIndex}\n      />\n    );\n  }\n\n  _cellRenderer = ({\n    parent,\n    rowIndex,\n    style,\n    isScrolling,\n    isVisible,\n    key,\n  }: CellRendererParams) => {\n    const {rowRenderer} = this.props;\n\n    // TRICKY The style object is sometimes cached by Grid.\n    // This prevents new style objects from bypassing shallowCompare().\n    // However as of React 16, style props are auto-frozen (at least in dev mode)\n    // Check to make sure we can still modify the style before proceeding.\n    // https://github.com/facebook/react/commit/977357765b44af8ff0cfea327866861073095c12#commitcomment-20648713\n    const widthDescriptor = Object.getOwnPropertyDescriptor(style, 'width');\n    if (widthDescriptor && widthDescriptor.writable) {\n      // By default, List cells should be 100% width.\n      // This prevents them from flowing under a scrollbar (if present).\n      style.width = '100%';\n    }\n\n    return rowRenderer({\n      index: rowIndex,\n      style,\n      isScrolling,\n      isVisible,\n      key,\n      parent,\n    });\n  };\n\n  _setRef = (ref: ?React.ElementRef<typeof Grid>) => {\n    this.Grid = ref;\n  };\n\n  _onScroll = ({clientHeight, scrollHeight, scrollTop}: GridScroll) => {\n    const {onScroll} = this.props;\n\n    onScroll({clientHeight, scrollHeight, scrollTop});\n  };\n\n  _onSectionRendered = ({\n    rowOverscanStartIndex,\n    rowOverscanStopIndex,\n    rowStartIndex,\n    rowStopIndex,\n  }: RenderedSection) => {\n    const {onRowsRendered} = this.props;\n\n    onRowsRendered({\n      overscanStartIndex: rowOverscanStartIndex,\n      overscanStopIndex: rowOverscanStopIndex,\n      startIndex: rowStartIndex,\n      stopIndex: rowStopIndex,\n    });\n  };\n}\n"
  },
  {
    "path": "source/List/index.js",
    "content": "/** @flow */\n\nexport type {RowRendererParams} from './types';\n\nexport {default} from './List';\nexport {default as List} from './List';\n"
  },
  {
    "path": "source/List/types.js",
    "content": "// @flow\n\nimport * as React from 'react';\n\nexport type RowRendererParams = {\n  index: number,\n  isScrolling: boolean,\n  isVisible: boolean,\n  key: string,\n  parent: Object,\n  style: Object,\n};\n\nexport type RowRenderer = (params: RowRendererParams) => React.Element<*>;\n\nexport type RenderedRows = {\n  overscanStartIndex: number,\n  overscanStopIndex: number,\n  startIndex: number,\n  stopIndex: number,\n};\n\nexport type Scroll = {\n  clientHeight: number,\n  scrollHeight: number,\n  scrollTop: number,\n};\n"
  },
  {
    "path": "source/Masonry/Masonry.example.css",
    "content": ".Cell {\n  display: flex;\n  flex-direction: column;\n  border-radius: .5rem;\n  padding: 0.5rem;\n  background-color: #f7f7f7;\n  word-break: break-all;\n}\n\n.checkboxLabel {\n  margin-left: .5rem;\n}\n.checkboxLabel:first-of-type {\n  margin-left: 0;\n}\n.checkbox {\n  margin-right: 5px;\n}"
  },
  {
    "path": "source/Masonry/Masonry.example.js",
    "content": "/** @flow */\nimport Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\nimport {CellMeasurer, CellMeasurerCache} from '../CellMeasurer';\nimport AutoSizer from '../AutoSizer';\nimport WindowScroller from '../WindowScroller';\nimport createCellPositioner from './createCellPositioner';\nimport Masonry from './Masonry';\nimport styles from './Masonry.example.css';\n\nexport default class GridExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this._columnCount = 0;\n\n    this._cache = new CellMeasurerCache({\n      defaultHeight: 250,\n      defaultWidth: 200,\n      fixedWidth: true,\n    });\n\n    this.state = {\n      columnWidth: 200,\n      height: 300,\n      gutterSize: 10,\n      overscanByPixels: 0,\n      windowScrollerEnabled: false,\n    };\n\n    this._cellRenderer = this._cellRenderer.bind(this);\n    this._onResize = this._onResize.bind(this);\n    this._renderAutoSizer = this._renderAutoSizer.bind(this);\n    this._renderMasonry = this._renderMasonry.bind(this);\n    this._setMasonryRef = this._setMasonryRef.bind(this);\n  }\n\n  render() {\n    const {\n      columnWidth,\n      height,\n      gutterSize,\n      overscanByPixels,\n      windowScrollerEnabled,\n    } = this.state;\n\n    let child;\n\n    if (windowScrollerEnabled) {\n      child = (\n        <WindowScroller overscanByPixels={overscanByPixels}>\n          {this._renderAutoSizer}\n        </WindowScroller>\n      );\n    } else {\n      child = this._renderAutoSizer({height});\n    }\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"Masonry\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/Masonry/Masonry.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/Masonry.md\"\n        />\n\n        <ContentBoxParagraph>\n          Optimized for masonry layouts. Cells are j.i.t. measured and layed out\n          as a user scrolls. Sizes are cached so that resize/reflow is fast and\n          does not require re-measuring.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Use WindowScroller?\"\n              checked={windowScrollerEnabled}\n              className={styles.checkbox}\n              type=\"checkbox\"\n              onChange={event => {\n                // HACK Because this demo switches between using WindowScroller and not,\n                // It's easier to clear the cache when toggling modes to avoid a partially stale state.\n                this._cache.clearAll();\n                this.setState({\n                  windowScrollerEnabled: event.target.checked,\n                });\n              }}\n            />\n            Use <code>WindowScroller</code>?\n          </label>\n          <label className={styles.checkboxLabel}>\n            <button onClick={this._resetList}>Reset List Data</button>\n          </label>\n        </ContentBoxParagraph>\n\n        <InputRow>\n          <LabeledInput\n            label=\"Height\"\n            name=\"height\"\n            onChange={event => {\n              this.setState({\n                height: parseInt(event.target.value, 10) || 300,\n              });\n            }}\n            value={height}\n          />\n          <LabeledInput\n            label=\"Column Width\"\n            name=\"columnWidth\"\n            onChange={event => {\n              this._cache.clearAll();\n              this.setState(\n                {\n                  columnWidth: parseInt(event.target.value, 10) || 200,\n                },\n                () => {\n                  this._calculateColumnCount();\n                  this._resetCellPositioner();\n                  this._masonry.clearCellPositions();\n                },\n              );\n            }}\n            value={columnWidth}\n          />\n          <LabeledInput\n            label=\"Gutter Size\"\n            name=\"gutterSize\"\n            onChange={event => {\n              this.setState(\n                {\n                  gutterSize: parseInt(event.target.value, 10) || 10,\n                },\n                () => {\n                  this._calculateColumnCount();\n                  this._resetCellPositioner();\n                  this._masonry.recomputeCellPositions();\n                },\n              );\n            }}\n            value={gutterSize}\n          />\n          <LabeledInput\n            label=\"Overscan (px)\"\n            name=\"overscanByPixels\"\n            onChange={event => {\n              this.setState({\n                overscanByPixels: parseInt(event.target.value, 10) || 0,\n              });\n            }}\n            value={overscanByPixels}\n          />\n        </InputRow>\n\n        {child}\n      </ContentBox>\n    );\n  }\n\n  _calculateColumnCount() {\n    const {columnWidth, gutterSize} = this.state;\n\n    this._columnCount = Math.floor(this._width / (columnWidth + gutterSize));\n  }\n\n  _cellRenderer({index, key, parent, style}) {\n    const {list} = this.context;\n    const {columnWidth} = this.state;\n\n    const datum = list.get(index % list.size);\n\n    return (\n      <CellMeasurer cache={this._cache} index={index} key={key} parent={parent}>\n        <div\n          className={styles.Cell}\n          style={{\n            ...style,\n            width: columnWidth,\n          }}>\n          <div\n            style={{\n              backgroundColor: datum.color,\n              borderRadius: '0.5rem',\n              height: datum.size * 3,\n              marginBottom: '0.5rem',\n              width: '100%',\n              fontSize: 20,\n              color: 'white',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n            }}>\n            {index}\n          </div>\n          {datum.random}\n        </div>\n      </CellMeasurer>\n    );\n  }\n\n  _initCellPositioner() {\n    if (typeof this._cellPositioner === 'undefined') {\n      const {columnWidth, gutterSize} = this.state;\n\n      this._cellPositioner = createCellPositioner({\n        cellMeasurerCache: this._cache,\n        columnCount: this._columnCount,\n        columnWidth,\n        spacer: gutterSize,\n      });\n    }\n  }\n\n  _onResize({width}) {\n    this._width = width;\n\n    this._calculateColumnCount();\n    this._resetCellPositioner();\n    this._masonry.recomputeCellPositions();\n  }\n\n  _renderAutoSizer({height, scrollTop}) {\n    this._height = height;\n    this._scrollTop = scrollTop;\n\n    const {overscanByPixels} = this.state;\n\n    return (\n      <AutoSizer\n        disableHeight\n        height={height}\n        onResize={this._onResize}\n        overscanByPixels={overscanByPixels}\n        scrollTop={this._scrollTop}>\n        {this._renderMasonry}\n      </AutoSizer>\n    );\n  }\n\n  _renderMasonry({width}) {\n    this._width = width;\n\n    this._calculateColumnCount();\n    this._initCellPositioner();\n\n    const {height, overscanByPixels, windowScrollerEnabled} = this.state;\n\n    return (\n      <Masonry\n        autoHeight={windowScrollerEnabled}\n        cellCount={1000}\n        cellMeasurerCache={this._cache}\n        cellPositioner={this._cellPositioner}\n        cellRenderer={this._cellRenderer}\n        height={windowScrollerEnabled ? this._height : height}\n        overscanByPixels={overscanByPixels}\n        ref={this._setMasonryRef}\n        scrollTop={this._scrollTop}\n        width={width}\n      />\n    );\n  }\n\n  // This is a bit of a hack to simulate newly loaded cells\n  _resetList = () => {\n    const ROW_HEIGHTS = [25, 50, 75, 100];\n\n    const {list} = this.context;\n    list.forEach(datum => {\n      datum.size = ROW_HEIGHTS[Math.floor(Math.random() * ROW_HEIGHTS.length)];\n    });\n\n    this._cache.clearAll();\n    this._resetCellPositioner();\n    this._masonry.clearCellPositions();\n  };\n\n  _resetCellPositioner() {\n    const {columnWidth, gutterSize} = this.state;\n\n    this._cellPositioner.reset({\n      columnCount: this._columnCount,\n      columnWidth,\n      spacer: gutterSize,\n    });\n  }\n\n  _setMasonryRef(ref) {\n    this._masonry = ref;\n  }\n}\n"
  },
  {
    "path": "source/Masonry/Masonry.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {Simulate} from 'react-dom/test-utils';\nimport {render} from '../TestUtils';\nimport createCellPositionerUtil from './createCellPositioner';\nimport Masonry from './Masonry';\nimport {CellMeasurer, CellMeasurerCache} from '../CellMeasurer';\n\nconst ALTERNATING_CELL_HEIGHTS = [100, 50, 100, 150];\nconst CELL_SIZE_MULTIPLIER = 50;\nconst COLUMN_COUNT = 3;\n\nfunction assertVisibleCells(rendered, text) {\n  expect(\n    Array.from(rendered.querySelectorAll('.cell'))\n      .map(node => node.textContent)\n      .sort()\n      .join(','),\n  ).toEqual(text);\n}\n\nfunction createCellMeasurerCache(props = {}) {\n  return new CellMeasurerCache({\n    defaultHeight: CELL_SIZE_MULTIPLIER,\n    defaultWidth: CELL_SIZE_MULTIPLIER,\n    fixedWidth: true,\n    keyMapper: index => index,\n    ...props,\n  });\n}\n\nfunction createCellPositioner(cache) {\n  return createCellPositionerUtil({\n    cellMeasurerCache: cache,\n    columnCount: COLUMN_COUNT,\n    columnWidth: CELL_SIZE_MULTIPLIER,\n  });\n}\n\nfunction createCellRenderer(cache, renderCallback) {\n  renderCallback =\n    typeof renderCallback === 'function' ? renderCallback : index => index;\n\n  return function cellRenderer({index, isScrolling, key, parent, style}) {\n    const height =\n      ALTERNATING_CELL_HEIGHTS[index % ALTERNATING_CELL_HEIGHTS.length];\n    const width = CELL_SIZE_MULTIPLIER;\n\n    return (\n      <CellMeasurer cache={cache} index={index} key={key} parent={parent}>\n        <div\n          className=\"cell\"\n          ref={ref => {\n            if (ref) {\n              // Accounts for the fact that JSDom doesn't support measurements.\n              Object.defineProperty(ref, 'offsetHeight', {\n                configurable: true,\n                value: height,\n              });\n              Object.defineProperty(ref, 'offsetWidth', {\n                configurable: true,\n                value: width,\n              });\n            }\n          }}\n          style={{\n            ...style,\n            minHeight: height,\n            minWidth: width,\n          }}>\n          {renderCallback(index, {index, isScrolling, key, parent, style})}\n        </div>\n      </CellMeasurer>\n    );\n  };\n}\n\nfunction getMarkup(props = {}) {\n  const cellMeasurerCache =\n    props.cellMeasurerCache || createCellMeasurerCache();\n\n  return (\n    <Masonry\n      cellCount={1000}\n      cellMeasurerCache={cellMeasurerCache}\n      cellPositioner={createCellPositioner(cellMeasurerCache)}\n      cellRenderer={createCellRenderer(cellMeasurerCache)}\n      columnCount={COLUMN_COUNT}\n      height={CELL_SIZE_MULTIPLIER * 2}\n      overscanByPixels={CELL_SIZE_MULTIPLIER}\n      width={CELL_SIZE_MULTIPLIER * COLUMN_COUNT}\n      {...props}\n    />\n  );\n}\n\nfunction simulateScroll(masonry, scrollTop = 0) {\n  const target = {scrollTop};\n  masonry._scrollingContainer = target; // HACK to work around _onScroll target check\n\n  const masonryNode = findDOMNode(masonry);\n  masonryNode.scrollTop = scrollTop;\n  Simulate.scroll(masonryNode);\n}\n\ndescribe('Masonry', () => {\n  beforeEach(render.unmount);\n\n  describe('layout and measuring', () => {\n    it('should measure only enough cells required for initial render', () => {\n      // avg cell size: CELL_SIZE_MULTIPLIER\n      // width: CELL_SIZE_MULTIPLIER * 3\n      // height: CELL_SIZE_MULTIPLIER * 2\n      // overcsan by: CELL_SIZE_MULTIPLIER\n      // Expected to measure 9 cells\n      const cellMeasurerCache = createCellMeasurerCache();\n      render(getMarkup({cellMeasurerCache}));\n      for (let i = 0; i <= 8; i++) {\n        expect(cellMeasurerCache.has(i)).toBe(true);\n      }\n      expect(cellMeasurerCache.has(9)).toBe(false);\n    });\n\n    it('should not measure cells while scrolling until they are needed', () => {\n      // Expected to measure 9 cells\n      const cellMeasurerCache = createCellMeasurerCache();\n      const renderCallback = jest.fn().mockImplementation(index => index);\n      const cellRenderer = createCellRenderer(\n        cellMeasurerCache,\n        renderCallback,\n      );\n      const rendered = findDOMNode(\n        render(getMarkup({cellMeasurerCache, cellRenderer})),\n      );\n      renderCallback.mockClear();\n      // Scroll a little bit, but not so much to require re-measuring\n      simulateScroll(rendered, 51);\n      // Verify that render was only called enough times to fill view port (no extra for measuring)\n      expect(renderCallback).toHaveBeenCalledTimes(9);\n    });\n\n    it('should measure additional cells on scroll when it runs out of measured cells', () => {\n      const cellMeasurerCache = createCellMeasurerCache();\n      const renderCallback = jest.fn().mockImplementation(index => index);\n      const cellRenderer = createCellRenderer(\n        cellMeasurerCache,\n        renderCallback,\n      );\n      const rendered = findDOMNode(\n        render(getMarkup({cellRenderer, cellMeasurerCache})),\n      );\n      expect(cellMeasurerCache.has(9)).toBe(false);\n\n      renderCallback.mockClear();\n\n      simulateScroll(rendered, 101);\n      expect(cellMeasurerCache.has(9)).toBe(true);\n      expect(cellMeasurerCache.has(10)).toBe(false);\n    });\n\n    // Masonry used to do a render pass for only unmeasured cells,\n    // But this resulting in removing (and later re-adding) measured cells from the DOM,\n    // Which was bad for performance. See GitHub issue #875\n    it('should not remove previously-measured cells when measuring new ones', () => {\n      const log = [];\n\n      const cellMeasurerCache = createCellMeasurerCache();\n      const renderCallback = index => {\n        log.push(index);\n      };\n      const cellRenderer = createCellRenderer(\n        cellMeasurerCache,\n        renderCallback,\n      );\n\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellMeasurerCache,\n            cellRenderer,\n          }),\n        ),\n      );\n\n      // Expected to have rendered twice:\n      // 1st time to measure 9 cells (b'c of esimated size)\n      // 2nd time to render and position 9 cells (b'c of actual size)\n      expect(log).toHaveLength(18);\n\n      log.splice(0);\n\n      simulateScroll(rendered, 101);\n\n      // Expected to have rendered twice:\n      // 1st time to measure additional cells (based on estimated size)\n      // 2nd time to render and position with new cells\n      // The 1st render should also have included the pre-measured cells,\n      // To prevent them from being removed, recreated, and re-added to the DOM.\n      expect(log).toHaveLength(18);\n    });\n\n    it('should only render enough cells to fill the viewport', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            overscanByPixels: 0,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '0,1,2,3,4,5');\n      simulateScroll(rendered, 51);\n      assertVisibleCells(rendered, '0,2,3,4,5,6');\n      simulateScroll(rendered, 101);\n      assertVisibleCells(rendered, '3,4,5,6,7,8');\n      simulateScroll(rendered, 1001);\n      assertVisibleCells(rendered, '30,31,32,33,34,35');\n    });\n\n    it('should only render enough cells to fill the viewport plus overscanByPixels', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            overscanByPixels: 100,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '0,1,10,11,2,3,4,5,6,7,8,9');\n      simulateScroll(rendered, 51);\n      assertVisibleCells(rendered, '0,1,10,11,2,3,4,5,6,7,8,9');\n      simulateScroll(rendered, 101);\n      assertVisibleCells(rendered, '0,1,10,11,2,3,4,5,6,7,8,9');\n      simulateScroll(rendered, 1001);\n      assertVisibleCells(rendered, '26,27,28,29,30,31,32,33,34,35,36,37');\n    });\n\n    it('should still render correctly when autoHeight is true (eg WindowScroller)', () => {\n      // Share instances between renders to avoid resetting state in ways we don't intend\n      const cellMeasurerCache = createCellMeasurerCache();\n      const cellPositioner = createCellPositioner(cellMeasurerCache);\n\n      let rendered = findDOMNode(\n        render(\n          getMarkup({\n            autoHeight: true,\n            cellMeasurerCache,\n            cellPositioner,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '0,1,2,3,4,5,6,7,8');\n      rendered = findDOMNode(\n        render(\n          getMarkup({\n            autoHeight: true,\n            cellMeasurerCache,\n            cellPositioner,\n            scrollTop: 51,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '0,1,2,3,4,5,6,7,8');\n      rendered = findDOMNode(\n        render(\n          getMarkup({\n            autoHeight: true,\n            cellMeasurerCache,\n            cellPositioner,\n            scrollTop: 101,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '0,2,3,4,5,6,7,8,9');\n      rendered = findDOMNode(\n        render(\n          getMarkup({\n            autoHeight: true,\n            cellMeasurerCache,\n            cellPositioner,\n            scrollTop: 1001,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '27,29,30,31,32,33,34,35,36');\n    });\n\n    it('should set right instead of left in a cell styles for rtl row direction', () => {\n      // Share instances between renders to avoid resetting state in ways we don't intend\n      const cellMeasurerCache = createCellMeasurerCache();\n      const cellPositioner = createCellPositioner(cellMeasurerCache);\n\n      let rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellMeasurerCache,\n            cellPositioner,\n            rowDirection: 'rtl',\n          }),\n        ),\n      );\n      Array.from(rendered.querySelectorAll('.cell')).map(node => {\n        expect(node.style.right).toMatch(/px/);\n      });\n    });\n\n    it('should consider scroll only of the container element and not of any ancestor element', () => {\n      const cellMeasurerCache = createCellMeasurerCache();\n      const renderScrollableCell = index => (\n        <div\n          style={{height: '50px', overflow: 'visible'}}\n          id={`scrollable-cell-${index}`}>\n          <div style={{height: '500px'}}>{index}</div>\n        </div>\n      );\n      const cellRenderer = createCellRenderer(\n        cellMeasurerCache,\n        renderScrollableCell,\n      );\n\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            overscanByPixels: 0,\n            cellMeasurerCache,\n            cellRenderer,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '0,1,2,3,4,5');\n      const cellEl = rendered.querySelector('#scrollable-cell-1');\n      Simulate.scroll(cellEl, {target: {scrollTop: 100}});\n      assertVisibleCells(rendered, '0,1,2,3,4,5');\n    });\n  });\n\n  describe('recomputeCellPositions', () => {\n    it('should refresh all cell positions', () => {\n      // Share instances between renders to avoid resetting state in ways we don't intend\n      const cellMeasurerCache = createCellMeasurerCache();\n      const cellPositioner = jest\n        .fn()\n        .mockImplementation(createCellPositioner(cellMeasurerCache));\n\n      let rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellMeasurerCache,\n            cellPositioner,\n          }),\n        ),\n      );\n      assertVisibleCells(rendered, '0,1,2,3,4,5,6,7,8');\n\n      cellPositioner.mockImplementation(index => ({\n        left: 0,\n        top: index * CELL_SIZE_MULTIPLIER,\n      }));\n\n      const component = render(\n        getMarkup({\n          cellMeasurerCache,\n          cellPositioner,\n        }),\n      );\n      rendered = findDOMNode(component);\n      assertVisibleCells(rendered, '0,1,2,3,4,5,6,7,8');\n      component.recomputeCellPositions();\n      assertVisibleCells(rendered, '0,1,2,3,4');\n    });\n\n    it('should not reset measurement cache', () => {\n      const cellMeasurerCache = createCellMeasurerCache();\n      const component = render(getMarkup({cellMeasurerCache}));\n      const rendered = findDOMNode(component);\n      simulateScroll(rendered, 101);\n      expect(cellMeasurerCache.has(9)).toBe(true);\n      simulateScroll(rendered, 0);\n      component.recomputeCellPositions();\n      for (let i = 0; i <= 9; i++) {\n        expect(cellMeasurerCache.has(i)).toBe(true);\n      }\n    });\n  });\n\n  describe('isScrolling', () => {\n    it('should be true for cellRenderer while scrolling is in progress', () => {\n      const cellMeasurerCache = createCellMeasurerCache();\n      const renderCallback = jest.fn().mockImplementation(index => index);\n      const cellRenderer = createCellRenderer(\n        cellMeasurerCache,\n        renderCallback,\n      );\n      const rendered = findDOMNode(\n        render(getMarkup({cellMeasurerCache, cellRenderer})),\n      );\n      renderCallback.mockClear();\n      simulateScroll(rendered, 51);\n      expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(true);\n    });\n\n    it('should be reset after a small debounce when scrolling stops', () => {\n      const cellMeasurerCache = createCellMeasurerCache();\n      const renderCallback = jest.fn().mockImplementation(index => index);\n      const cellRenderer = createCellRenderer(\n        cellMeasurerCache,\n        renderCallback,\n      );\n      const rendered = findDOMNode(\n        render(getMarkup({cellMeasurerCache, cellRenderer})),\n      );\n      simulateScroll(rendered, 51);\n      renderCallback.mockClear();\n      setTimeout(() => {\n        expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(false);\n      }, 0);\n    });\n  });\n\n  describe('callbacks', () => {\n    it('should call onCellsRendered when rendered cells change', () => {\n      const onCellsRendered = jest.fn();\n      const rendered = findDOMNode(render(getMarkup({onCellsRendered})));\n      expect(onCellsRendered.mock.calls).toEqual([\n        [{startIndex: 0, stopIndex: 8}],\n      ]);\n      simulateScroll(rendered, 51);\n      expect(onCellsRendered.mock.calls).toEqual([\n        [{startIndex: 0, stopIndex: 8}],\n      ]);\n      simulateScroll(rendered, 101);\n      expect(onCellsRendered.mock.calls).toEqual([\n        [{startIndex: 0, stopIndex: 8}],\n        [{startIndex: 0, stopIndex: 9}],\n      ]);\n    });\n\n    it('should call onScroll when scroll position changes', () => {\n      const onScroll = jest.fn();\n      const rendered = findDOMNode(render(getMarkup({onScroll})));\n      expect(onScroll.mock.calls).toEqual([\n        [{clientHeight: 100, scrollHeight: 16900, scrollTop: 0}],\n      ]);\n      simulateScroll(rendered, 51);\n      expect(onScroll.mock.calls).toEqual([\n        [{clientHeight: 100, scrollHeight: 16900, scrollTop: 0}],\n        [{clientHeight: 100, scrollHeight: 16900, scrollTop: 51}],\n      ]);\n      simulateScroll(rendered, 0);\n      expect(onScroll.mock.calls).toEqual([\n        [{clientHeight: 100, scrollHeight: 16900, scrollTop: 0}],\n        [{clientHeight: 100, scrollHeight: 16900, scrollTop: 51}],\n        [{clientHeight: 100, scrollHeight: 16900, scrollTop: 0}],\n      ]);\n    });\n  });\n\n  describe('keyMapper', () => {\n    it('should pass the correct key to rendered cells', () => {\n      const keyMapper = jest.fn().mockImplementation(index => `key:${index}`);\n      const cellRenderer = jest\n        .fn()\n        .mockImplementation(({index, key, style}) => (\n          <div key={key} style={style}>\n            {index}\n          </div>\n        ));\n      findDOMNode(render(getMarkup({cellRenderer, keyMapper})));\n      expect(keyMapper).toHaveBeenCalled();\n      expect(cellRenderer).toHaveBeenCalled();\n      expect(cellRenderer.mock.calls[0][0].key).toEqual('key:0');\n    });\n  });\n});\n"
  },
  {
    "path": "source/Masonry/Masonry.js",
    "content": "/** @flow */\nimport clsx from 'clsx';\nimport * as React from 'react';\nimport {polyfill} from 'react-lifecycles-compat';\nimport PositionCache from './PositionCache';\nimport {\n  requestAnimationTimeout,\n  cancelAnimationTimeout,\n} from '../utils/requestAnimationTimeout';\n\nimport type {AnimationTimeoutId} from '../utils/requestAnimationTimeout';\n\ntype Props = {\n  autoHeight: boolean,\n  cellCount: number,\n  cellMeasurerCache: CellMeasurerCache,\n  cellPositioner: Positioner,\n  cellRenderer: CellRenderer,\n  className: ?string,\n  height: number,\n  id: ?string,\n  keyMapper: KeyMapper,\n  onCellsRendered: ?OnCellsRenderedCallback,\n  onScroll: ?OnScrollCallback,\n  overscanByPixels: number,\n  role: string,\n  scrollingResetTimeInterval: number,\n  style: mixed,\n  tabIndex: number,\n  width: number,\n  rowDirection: string,\n  scrollTop?: number,\n};\n\ntype State = {\n  isScrolling: boolean,\n  scrollTop: number,\n};\n\nconst emptyObject = {};\n\n/**\n * Specifies the number of milliseconds during which to disable pointer events while a scroll is in progress.\n * This improves performance and makes scrolling smoother.\n */\nexport const DEFAULT_SCROLLING_RESET_TIME_INTERVAL = 150;\n\n/**\n * This component efficiently displays arbitrarily positioned cells using windowing techniques.\n * Cell position is determined by an injected `cellPositioner` property.\n * Windowing is vertical; this component does not support horizontal scrolling.\n *\n * Rendering occurs in two phases:\n * 1) First pass uses estimated cell sizes (provided by the cache) to determine how many cells to measure in a batch.\n *    Batch size is chosen using a fast, naive layout algorithm that stacks images in order until the viewport has been filled.\n *    After measurement is complete (componentDidMount or componentDidUpdate) this component evaluates positioned cells\n *    in order to determine if another measurement pass is required (eg if actual cell sizes were less than estimated sizes).\n *    All measurements are permanently cached (keyed by `keyMapper`) for performance purposes.\n * 2) Second pass uses the external `cellPositioner` to layout cells.\n *    At this time the positioner has access to cached size measurements for all cells.\n *    The positions it returns are cached by Masonry for fast access later.\n *    Phase one is repeated if the user scrolls beyond the current layout's bounds.\n *    If the layout is invalidated due to eg a resize, cached positions can be cleared using `recomputeCellPositions()`.\n *\n * Animation constraints:\n *   Simple animations are supported (eg translate/slide into place on initial reveal).\n *   More complex animations are not (eg flying from one position to another on resize).\n *\n * Layout constraints:\n *   This component supports multi-column layout.\n *   The height of each item may vary.\n *   The width of each item must not exceed the width of the column it is \"in\".\n *   The left position of all items within a column must align.\n *   (Items may not span multiple columns.)\n */\nclass Masonry extends React.PureComponent<Props, State> {\n  static defaultProps = {\n    autoHeight: false,\n    keyMapper: identity,\n    onCellsRendered: noop,\n    onScroll: noop,\n    overscanByPixels: 20,\n    role: 'grid',\n    scrollingResetTimeInterval: DEFAULT_SCROLLING_RESET_TIME_INTERVAL,\n    style: emptyObject,\n    tabIndex: 0,\n    rowDirection: 'ltr',\n  };\n\n  state = {\n    isScrolling: false,\n    scrollTop: 0,\n  };\n\n  _debounceResetIsScrollingId: AnimationTimeoutId;\n  _invalidateOnUpdateStartIndex: ?number = null;\n  _invalidateOnUpdateStopIndex: ?number = null;\n  _positionCache: PositionCache = new PositionCache();\n  _startIndex: ?number = null;\n  _startIndexMemoized: ?number = null;\n  _stopIndex: ?number = null;\n  _stopIndexMemoized: ?number = null;\n\n  clearCellPositions() {\n    this._positionCache = new PositionCache();\n    this.forceUpdate();\n  }\n\n  // HACK This method signature was intended for Grid\n  invalidateCellSizeAfterRender({rowIndex: index}) {\n    if (this._invalidateOnUpdateStartIndex === null) {\n      this._invalidateOnUpdateStartIndex = index;\n      this._invalidateOnUpdateStopIndex = index;\n    } else {\n      this._invalidateOnUpdateStartIndex = Math.min(\n        this._invalidateOnUpdateStartIndex,\n        index,\n      );\n      this._invalidateOnUpdateStopIndex = Math.max(\n        this._invalidateOnUpdateStopIndex,\n        index,\n      );\n    }\n  }\n\n  recomputeCellPositions() {\n    const stopIndex = this._positionCache.count - 1;\n\n    this._positionCache = new PositionCache();\n    this._populatePositionCache(0, stopIndex);\n\n    this.forceUpdate();\n  }\n\n  static getDerivedStateFromProps(\n    nextProps: Props,\n    prevState: State,\n  ): $Shape<State> {\n    if (\n      nextProps.scrollTop !== undefined &&\n      prevState.scrollTop !== nextProps.scrollTop\n    ) {\n      return {\n        isScrolling: true,\n        scrollTop: nextProps.scrollTop,\n      };\n    }\n\n    return null;\n  }\n\n  componentDidMount() {\n    this._checkInvalidateOnUpdate();\n    this._invokeOnScrollCallback();\n    this._invokeOnCellsRenderedCallback();\n  }\n\n  componentDidUpdate(prevProps: Props, prevState: State) {\n    this._checkInvalidateOnUpdate();\n    this._invokeOnScrollCallback();\n    this._invokeOnCellsRenderedCallback();\n\n    if (this.props.scrollTop !== prevProps.scrollTop) {\n      this._debounceResetIsScrolling();\n    }\n  }\n\n  componentWillUnmount() {\n    if (this._debounceResetIsScrollingId) {\n      cancelAnimationTimeout(this._debounceResetIsScrollingId);\n    }\n  }\n\n  render() {\n    const {\n      autoHeight,\n      cellCount,\n      cellMeasurerCache,\n      cellRenderer,\n      className,\n      height,\n      id,\n      keyMapper,\n      overscanByPixels,\n      role,\n      style,\n      tabIndex,\n      width,\n      rowDirection,\n    } = this.props;\n\n    const {isScrolling, scrollTop} = this.state;\n\n    const children = [];\n\n    const estimateTotalHeight = this._getEstimatedTotalHeight();\n\n    const shortestColumnSize = this._positionCache.shortestColumnSize;\n    const measuredCellCount = this._positionCache.count;\n\n    let startIndex = 0;\n    let stopIndex;\n\n    this._positionCache.range(\n      Math.max(0, scrollTop - overscanByPixels),\n      height + overscanByPixels * 2,\n      (index: number, left: number, top: number) => {\n        if (typeof stopIndex === 'undefined') {\n          startIndex = index;\n          stopIndex = index;\n        } else {\n          startIndex = Math.min(startIndex, index);\n          stopIndex = Math.max(stopIndex, index);\n        }\n\n        children.push(\n          cellRenderer({\n            index,\n            isScrolling,\n            key: keyMapper(index),\n            parent: this,\n            style: {\n              height: cellMeasurerCache.getHeight(index),\n              [rowDirection === 'ltr' ? 'left' : 'right']: left,\n              position: 'absolute',\n              top,\n              width: cellMeasurerCache.getWidth(index),\n            },\n          }),\n        );\n      },\n    );\n\n    // We need to measure additional cells for this layout\n    if (\n      shortestColumnSize < scrollTop + height + overscanByPixels &&\n      measuredCellCount < cellCount\n    ) {\n      const batchSize = Math.min(\n        cellCount - measuredCellCount,\n        Math.ceil(\n          (((scrollTop + height + overscanByPixels - shortestColumnSize) /\n            cellMeasurerCache.defaultHeight) *\n            width) /\n            cellMeasurerCache.defaultWidth,\n        ),\n      );\n\n      for (\n        let index = measuredCellCount;\n        index < measuredCellCount + batchSize;\n        index++\n      ) {\n        stopIndex = index;\n\n        children.push(\n          cellRenderer({\n            index: index,\n            isScrolling,\n            key: keyMapper(index),\n            parent: this,\n            style: {\n              width: cellMeasurerCache.getWidth(index),\n            },\n          }),\n        );\n      }\n    }\n\n    this._startIndex = startIndex;\n    this._stopIndex = stopIndex;\n\n    return (\n      <div\n        ref={this._setScrollingContainerRef}\n        aria-label={this.props['aria-label']}\n        className={clsx('ReactVirtualized__Masonry', className)}\n        id={id}\n        onScroll={this._onScroll}\n        role={role}\n        style={{\n          boxSizing: 'border-box',\n          direction: 'ltr',\n          height: autoHeight ? 'auto' : height,\n          overflowX: 'hidden',\n          overflowY: estimateTotalHeight < height ? 'hidden' : 'auto',\n          position: 'relative',\n          width,\n          WebkitOverflowScrolling: 'touch',\n          willChange: 'transform',\n          ...style,\n        }}\n        tabIndex={tabIndex}>\n        <div\n          className=\"ReactVirtualized__Masonry__innerScrollContainer\"\n          style={{\n            width: '100%',\n            height: estimateTotalHeight,\n            maxWidth: '100%',\n            maxHeight: estimateTotalHeight,\n            overflow: 'hidden',\n            pointerEvents: isScrolling ? 'none' : '',\n            position: 'relative',\n          }}>\n          {children}\n        </div>\n      </div>\n    );\n  }\n\n  _checkInvalidateOnUpdate() {\n    if (typeof this._invalidateOnUpdateStartIndex === 'number') {\n      const startIndex = this._invalidateOnUpdateStartIndex;\n      const stopIndex = this._invalidateOnUpdateStopIndex;\n\n      this._invalidateOnUpdateStartIndex = null;\n      this._invalidateOnUpdateStopIndex = null;\n\n      // Query external layout logic for position of newly-measured cells\n      this._populatePositionCache(startIndex, stopIndex);\n\n      this.forceUpdate();\n    }\n  }\n\n  _debounceResetIsScrolling() {\n    const {scrollingResetTimeInterval} = this.props;\n\n    if (this._debounceResetIsScrollingId) {\n      cancelAnimationTimeout(this._debounceResetIsScrollingId);\n    }\n\n    this._debounceResetIsScrollingId = requestAnimationTimeout(\n      this._debounceResetIsScrollingCallback,\n      scrollingResetTimeInterval,\n    );\n  }\n\n  _debounceResetIsScrollingCallback = () => {\n    this.setState({\n      isScrolling: false,\n    });\n  };\n\n  _getEstimatedTotalHeight() {\n    const {cellCount, cellMeasurerCache, width} = this.props;\n\n    const estimatedColumnCount = Math.max(\n      1,\n      Math.floor(width / cellMeasurerCache.defaultWidth),\n    );\n\n    return this._positionCache.estimateTotalHeight(\n      cellCount,\n      estimatedColumnCount,\n      cellMeasurerCache.defaultHeight,\n    );\n  }\n\n  _invokeOnScrollCallback() {\n    const {height, onScroll} = this.props;\n    const {scrollTop} = this.state;\n\n    if (this._onScrollMemoized !== scrollTop) {\n      onScroll({\n        clientHeight: height,\n        scrollHeight: this._getEstimatedTotalHeight(),\n        scrollTop,\n      });\n\n      this._onScrollMemoized = scrollTop;\n    }\n  }\n\n  _invokeOnCellsRenderedCallback() {\n    if (\n      this._startIndexMemoized !== this._startIndex ||\n      this._stopIndexMemoized !== this._stopIndex\n    ) {\n      const {onCellsRendered} = this.props;\n\n      onCellsRendered({\n        startIndex: this._startIndex,\n        stopIndex: this._stopIndex,\n      });\n\n      this._startIndexMemoized = this._startIndex;\n      this._stopIndexMemoized = this._stopIndex;\n    }\n  }\n\n  _populatePositionCache(startIndex: number, stopIndex: number) {\n    const {cellMeasurerCache, cellPositioner} = this.props;\n\n    for (let index = startIndex; index <= stopIndex; index++) {\n      const {left, top} = cellPositioner(index);\n\n      this._positionCache.setPosition(\n        index,\n        left,\n        top,\n        cellMeasurerCache.getHeight(index),\n      );\n    }\n  }\n\n  _setScrollingContainerRef = ref => {\n    this._scrollingContainer = ref;\n  };\n\n  _onScroll = event => {\n    const {height} = this.props;\n\n    const eventScrollTop = event.currentTarget.scrollTop;\n\n    // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events,\n    // Gradually converging on a scrollTop that is within the bounds of the new, smaller height.\n    // This causes a series of rapid renders that is slow for long lists.\n    // We can avoid that by doing some simple bounds checking to ensure that scroll offsets never exceed their bounds.\n    const scrollTop = Math.min(\n      Math.max(0, this._getEstimatedTotalHeight() - height),\n      eventScrollTop,\n    );\n\n    // On iOS, we can arrive at negative offsets by swiping past the start or end.\n    // Avoid re-rendering in this case as it can cause problems; see #532 for more.\n    if (eventScrollTop !== scrollTop) {\n      return;\n    }\n\n    // Prevent pointer events from interrupting a smooth scroll\n    this._debounceResetIsScrolling();\n\n    // Certain devices (like Apple touchpad) rapid-fire duplicate events.\n    // Don't force a re-render if this is the case.\n    // The mouse may move faster then the animation frame does.\n    // Use requestAnimationFrame to avoid over-updating.\n    if (this.state.scrollTop !== scrollTop) {\n      this.setState({\n        isScrolling: true,\n        scrollTop,\n      });\n    }\n  };\n}\n\nfunction identity(value) {\n  return value;\n}\n\nfunction noop() {}\n\ntype KeyMapper = (index: number) => mixed;\n\nexport type CellMeasurerCache = {\n  defaultHeight: number,\n  defaultWidth: number,\n  getHeight: (index: number) => number,\n  getWidth: (index: number) => number,\n};\n\ntype CellRenderer = (params: {|\n  index: number,\n  isScrolling: boolean,\n  key: mixed,\n  parent: mixed,\n  style: mixed,\n|}) => mixed;\n\ntype OnCellsRenderedCallback = (params: {|\n  startIndex: number,\n  stopIndex: number,\n|}) => void;\n\ntype OnScrollCallback = (params: {|\n  clientHeight: number,\n  scrollHeight: number,\n  scrollTop: number,\n|}) => void;\n\ntype Position = {\n  left: number,\n  top: number,\n};\n\npolyfill(Masonry);\n\nexport default Masonry;\n\nexport type Positioner = (index: number) => Position;\n"
  },
  {
    "path": "source/Masonry/PositionCache.js",
    "content": "/** @flow */\nimport createIntervalTree from '../vendor/intervalTree';\n\ntype RenderCallback = (index: number, left: number, top: number) => void;\n\n// Position cache requirements:\n//   O(log(n)) lookup of cells to render for a given viewport size\n//   O(1) lookup of shortest measured column (so we know when to enter phase 1)\nexport default class PositionCache {\n  // Tracks the height of each column\n  _columnSizeMap: {[x: number]: number} = {};\n\n  // Store tops and bottoms of each cell for fast intersection lookup.\n  _intervalTree = createIntervalTree();\n\n  // Maps cell index to x coordinates for quick lookup.\n  _leftMap: {[index: number]: number} = {};\n\n  estimateTotalHeight(\n    cellCount: number,\n    columnCount: number,\n    defaultCellHeight: number,\n  ): number {\n    const unmeasuredCellCount = cellCount - this.count;\n    return (\n      this.tallestColumnSize +\n      Math.ceil(unmeasuredCellCount / columnCount) * defaultCellHeight\n    );\n  }\n\n  // Render all cells visible within the viewport range defined.\n  range(\n    scrollTop: number,\n    clientHeight: number,\n    renderCallback: RenderCallback,\n  ): void {\n    this._intervalTree.queryInterval(\n      scrollTop,\n      scrollTop + clientHeight,\n      ([top, _, index]) => renderCallback(index, this._leftMap[index], top),\n    );\n  }\n\n  setPosition(index: number, left: number, top: number, height: number): void {\n    this._intervalTree.insert([top, top + height, index]);\n    this._leftMap[index] = left;\n\n    const columnSizeMap = this._columnSizeMap;\n    const columnHeight = columnSizeMap[left];\n    if (columnHeight === undefined) {\n      columnSizeMap[left] = top + height;\n    } else {\n      columnSizeMap[left] = Math.max(columnHeight, top + height);\n    }\n  }\n\n  get count(): number {\n    return this._intervalTree.count;\n  }\n\n  get shortestColumnSize(): number {\n    const columnSizeMap = this._columnSizeMap;\n\n    let size = 0;\n\n    for (let i in columnSizeMap) {\n      let height = columnSizeMap[(i: any)];\n      size = size === 0 ? height : Math.min(size, height);\n    }\n\n    return size;\n  }\n\n  get tallestColumnSize(): number {\n    const columnSizeMap = this._columnSizeMap;\n\n    let size = 0;\n\n    for (let i in columnSizeMap) {\n      let height = columnSizeMap[(i: any)];\n      size = Math.max(size, height);\n    }\n\n    return size;\n  }\n}\n"
  },
  {
    "path": "source/Masonry/createCellPositioner.js",
    "content": "/** @flow */\nimport type {CellMeasurerCache, Positioner} from './Masonry';\n\ntype createCellPositionerParams = {\n  cellMeasurerCache: CellMeasurerCache,\n  columnCount: number,\n  columnWidth: number,\n  spacer?: number,\n};\n\ntype resetParams = {\n  columnCount: number,\n  columnWidth: number,\n  spacer?: number,\n};\n\nexport default function createCellPositioner({\n  cellMeasurerCache,\n  columnCount,\n  columnWidth,\n  spacer = 0,\n}: createCellPositionerParams): Positioner {\n  let columnHeights;\n\n  initOrResetDerivedValues();\n\n  function cellPositioner(index) {\n    // Find the shortest column and use it.\n    let columnIndex = 0;\n    for (let i = 1; i < columnHeights.length; i++) {\n      if (columnHeights[i] < columnHeights[columnIndex]) {\n        columnIndex = i;\n      }\n    }\n\n    const left = columnIndex * (columnWidth + spacer);\n    const top = columnHeights[columnIndex] || 0;\n\n    columnHeights[columnIndex] =\n      top + cellMeasurerCache.getHeight(index) + spacer;\n\n    return {\n      left,\n      top,\n    };\n  }\n\n  function initOrResetDerivedValues(): void {\n    // Track the height of each column.\n    // Layout algorithm below always inserts into the shortest column.\n    columnHeights = [];\n    for (let i = 0; i < columnCount; i++) {\n      columnHeights[i] = 0;\n    }\n  }\n\n  function reset(params: resetParams): void {\n    columnCount = params.columnCount;\n    columnWidth = params.columnWidth;\n    spacer = params.spacer;\n\n    initOrResetDerivedValues();\n  }\n\n  cellPositioner.reset = reset;\n\n  return cellPositioner;\n}\n"
  },
  {
    "path": "source/Masonry/index.js",
    "content": "/** @flow */\nimport createCellPositioner from './createCellPositioner';\nimport Masonry from './Masonry';\n\nexport default Masonry;\nexport {createCellPositioner, Masonry};\n"
  },
  {
    "path": "source/MultiGrid/CellMeasurerCacheDecorator.js",
    "content": "/** @flow */\nimport {CellMeasurerCache} from '../CellMeasurer';\n\ntype CellMeasurerCacheDecoratorParams = {\n  cellMeasurerCache: CellMeasurerCache,\n  columnIndexOffset: number,\n  rowIndexOffset: number,\n};\n\ntype IndexParam = {\n  index: number,\n};\n\n/**\n * Caches measurements for a given cell.\n */\nexport default class CellMeasurerCacheDecorator {\n  _cellMeasurerCache: CellMeasurerCache;\n  _columnIndexOffset: number;\n  _rowIndexOffset: number;\n\n  constructor(params: CellMeasurerCacheDecoratorParams = {}) {\n    const {\n      cellMeasurerCache,\n      columnIndexOffset = 0,\n      rowIndexOffset = 0,\n    } = params;\n\n    this._cellMeasurerCache = cellMeasurerCache;\n    this._columnIndexOffset = columnIndexOffset;\n    this._rowIndexOffset = rowIndexOffset;\n  }\n\n  clear(rowIndex: number, columnIndex: number): void {\n    this._cellMeasurerCache.clear(\n      rowIndex + this._rowIndexOffset,\n      columnIndex + this._columnIndexOffset,\n    );\n  }\n\n  clearAll(): void {\n    this._cellMeasurerCache.clearAll();\n  }\n\n  columnWidth = ({index}: IndexParam) => {\n    this._cellMeasurerCache.columnWidth({\n      index: index + this._columnIndexOffset,\n    });\n  };\n\n  get defaultHeight(): number {\n    return this._cellMeasurerCache.defaultHeight;\n  }\n\n  get defaultWidth(): number {\n    return this._cellMeasurerCache.defaultWidth;\n  }\n\n  hasFixedHeight(): boolean {\n    return this._cellMeasurerCache.hasFixedHeight();\n  }\n\n  hasFixedWidth(): boolean {\n    return this._cellMeasurerCache.hasFixedWidth();\n  }\n\n  getHeight(rowIndex: number, columnIndex: ?number = 0): ?number {\n    return this._cellMeasurerCache.getHeight(\n      rowIndex + this._rowIndexOffset,\n      columnIndex + this._columnIndexOffset,\n    );\n  }\n\n  getWidth(rowIndex: number, columnIndex: ?number = 0): ?number {\n    return this._cellMeasurerCache.getWidth(\n      rowIndex + this._rowIndexOffset,\n      columnIndex + this._columnIndexOffset,\n    );\n  }\n\n  has(rowIndex: number, columnIndex: ?number = 0): boolean {\n    return this._cellMeasurerCache.has(\n      rowIndex + this._rowIndexOffset,\n      columnIndex + this._columnIndexOffset,\n    );\n  }\n\n  rowHeight = ({index}: IndexParam) => {\n    this._cellMeasurerCache.rowHeight({\n      index: index + this._rowIndexOffset,\n    });\n  };\n\n  set(\n    rowIndex: number,\n    columnIndex: number,\n    width: number,\n    height: number,\n  ): void {\n    this._cellMeasurerCache.set(\n      rowIndex + this._rowIndexOffset,\n      columnIndex + this._columnIndexOffset,\n      (width: number),\n      (height: number),\n    );\n  }\n}\n"
  },
  {
    "path": "source/MultiGrid/MultiGrid.example.css",
    "content": ".Cell {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-bottom: 1px solid #eee;\n  border-right: 1px solid #eee;\n}"
  },
  {
    "path": "source/MultiGrid/MultiGrid.example.js",
    "content": "/** @flow */\nimport Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\nimport AutoSizer from '../AutoSizer';\nimport MultiGrid from './MultiGrid';\nimport styles from './MultiGrid.example.css';\n\nconst STYLE = {\n  border: '1px solid #ddd',\n};\nconst STYLE_BOTTOM_LEFT_GRID = {\n  borderRight: '2px solid #aaa',\n  backgroundColor: '#f7f7f7',\n};\nconst STYLE_TOP_LEFT_GRID = {\n  borderBottom: '2px solid #aaa',\n  borderRight: '2px solid #aaa',\n  fontWeight: 'bold',\n};\nconst STYLE_TOP_RIGHT_GRID = {\n  borderBottom: '2px solid #aaa',\n  fontWeight: 'bold',\n};\n\nexport default class MultiGridExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      fixedColumnCount: 2,\n      fixedRowCount: 1,\n      scrollToColumn: 0,\n      scrollToRow: 0,\n    };\n\n    this._cellRenderer = this._cellRenderer.bind(this);\n    this._onFixedColumnCountChange = this._createEventHandler(\n      'fixedColumnCount',\n    );\n    this._onFixedRowCountChange = this._createEventHandler('fixedRowCount');\n    this._onScrollToColumnChange = this._createEventHandler('scrollToColumn');\n    this._onScrollToRowChange = this._createEventHandler('scrollToRow');\n  }\n\n  render() {\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"MultiGrid\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/MultiGrid/MultiGrid.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/MultiGrid.md\"\n        />\n\n        <ContentBoxParagraph>\n          This component stitches together several grids to provide a fixed\n          column/row interface.\n        </ContentBoxParagraph>\n\n        <InputRow>\n          {this._createLabeledInput(\n            'fixedColumnCount',\n            this._onFixedColumnCountChange,\n          )}\n          {this._createLabeledInput(\n            'fixedRowCount',\n            this._onFixedRowCountChange,\n          )}\n          {this._createLabeledInput(\n            'scrollToColumn',\n            this._onScrollToColumnChange,\n          )}\n          {this._createLabeledInput('scrollToRow', this._onScrollToRowChange)}\n        </InputRow>\n\n        <AutoSizer disableHeight>\n          {({width}) => (\n            <MultiGrid\n              {...this.state}\n              cellRenderer={this._cellRenderer}\n              columnWidth={75}\n              columnCount={50}\n              enableFixedColumnScroll\n              enableFixedRowScroll\n              height={300}\n              rowHeight={40}\n              rowCount={100}\n              style={STYLE}\n              styleBottomLeftGrid={STYLE_BOTTOM_LEFT_GRID}\n              styleTopLeftGrid={STYLE_TOP_LEFT_GRID}\n              styleTopRightGrid={STYLE_TOP_RIGHT_GRID}\n              width={width}\n              hideTopRightGridScrollbar\n              hideBottomLeftGridScrollbar\n            />\n          )}\n        </AutoSizer>\n      </ContentBox>\n    );\n  }\n\n  _cellRenderer({columnIndex, key, rowIndex, style}) {\n    return (\n      <div className={styles.Cell} key={key} style={style}>\n        {columnIndex}, {rowIndex}\n      </div>\n    );\n  }\n\n  _createEventHandler(property) {\n    return event => {\n      const value = parseInt(event.target.value, 10) || 0;\n\n      this.setState({\n        [property]: value,\n      });\n    };\n  }\n\n  _createLabeledInput(property, eventHandler) {\n    const value = this.state[property];\n\n    return (\n      <LabeledInput\n        label={property}\n        name={property}\n        onChange={eventHandler}\n        value={value}\n      />\n    );\n  }\n}\n"
  },
  {
    "path": "source/MultiGrid/MultiGrid.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport MultiGrid from './MultiGrid';\nimport {CellMeasurerCache} from '../CellMeasurer';\n\n// These tests only focus on what MultiGrid does specifically.\n// The inner Grid component is tested in depth elsewhere.\ndescribe('MultiGrid', () => {\n  function defaultCellRenderer({columnIndex, key, rowIndex, style}) {\n    return (\n      <div className=\"gridItem\" key={key} style={style}>\n        {`row:${rowIndex}, column:${columnIndex}`}\n      </div>\n    );\n  }\n\n  function getMarkup(props = {}) {\n    return (\n      <MultiGrid\n        cellRenderer={defaultCellRenderer}\n        columnCount={50}\n        columnWidth={50}\n        fixedColumnCount={2}\n        fixedRowCount={1}\n        height={300}\n        overscanColumnCount={0}\n        overscanRowCount={0}\n        autoHeight={false}\n        rowHeight={20}\n        rowCount={100}\n        width={400}\n        {...props}\n      />\n    );\n  }\n\n  describe('fixed columns and rows', () => {\n    it('should render 4 Grids when configured for fixed columns and rows', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 1,\n            fixedRowCount: 1,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(grids.length).toEqual(4);\n      const [topLeft, topRight, bottomLeft, bottomRight] = grids;\n      expect(topLeft.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(topLeft.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(topRight.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(topRight.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(bottomLeft.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(bottomLeft.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(bottomRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(bottomRight.style.getPropertyValue('overflow-y')).toEqual('auto');\n    });\n\n    it('should render 2 Grids when configured for fixed columns only', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 1,\n            fixedRowCount: 0,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(grids.length).toEqual(2);\n      const [bottomLeft, bottomRight] = grids;\n      expect(bottomLeft.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(bottomLeft.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(bottomRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(bottomRight.style.getPropertyValue('overflow-y')).toEqual('auto');\n    });\n\n    it('should render 2 Grids when configured for fixed rows only', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 0,\n            fixedRowCount: 1,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(grids.length).toEqual(2);\n      const [topRight, bottomRight] = grids;\n      expect(topRight.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(topRight.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(bottomRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(bottomRight.style.getPropertyValue('overflow-y')).toEqual('auto');\n    });\n\n    it('should render 1 Grid when configured for neither fixed columns and rows', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 0,\n            fixedRowCount: 0,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(grids.length).toEqual(1);\n      const [bottomRight] = grids;\n      expect(bottomRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(bottomRight.style.getPropertyValue('overflow-y')).toEqual('auto');\n    });\n\n    it('should adjust the number of Grids when fixed column or row counts change', () => {\n      let rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 2,\n            fixedRowCount: 1,\n          }),\n        ),\n      );\n      expect(\n        rendered.querySelectorAll('.ReactVirtualized__Grid').length,\n      ).toEqual(4);\n      rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 0,\n            fixedRowCount: 0,\n          }),\n        ),\n      );\n      expect(\n        rendered.querySelectorAll('.ReactVirtualized__Grid').length,\n      ).toEqual(1);\n      rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 0,\n            fixedRowCount: 2,\n          }),\n        ),\n      );\n      expect(\n        rendered.querySelectorAll('.ReactVirtualized__Grid').length,\n      ).toEqual(2);\n    });\n\n    it('should allow scrolling of fixed Grids when configured for fixed columns and rows with scroll interaction', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            enableFixedColumnScroll: true,\n            enableFixedRowScroll: true,\n            fixedColumnCount: 1,\n            fixedRowCount: 1,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(grids.length).toEqual(4);\n      const [topLeft, topRight, bottomLeft, bottomRight] = grids;\n      expect(topLeft.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(topLeft.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(topRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(topRight.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(bottomLeft.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(bottomLeft.style.getPropertyValue('overflow-y')).toEqual('auto');\n      expect(bottomRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(bottomRight.style.getPropertyValue('overflow-y')).toEqual('auto');\n    });\n  });\n\n  describe('hideTopRightGridScrollbar, hideBottomLeftGridScrollbar should hide the scrollbars', () => {\n    function getScrollbarSize20() {\n      return 20;\n    }\n    it('should add scroll wrappers to hide scroll bar when configured for fixed columns and rows with scroll interaction', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            enableFixedColumnScroll: true,\n            enableFixedRowScroll: true,\n            fixedColumnCount: 1,\n            fixedRowCount: 1,\n            hideTopRightGridScrollbar: true,\n            hideBottomLeftGridScrollbar: true,\n            getScrollbarSize: getScrollbarSize20,\n          }),\n        ),\n      );\n      let wrappers = rendered.querySelectorAll('.TopRightGrid_ScrollWrapper');\n      expect(wrappers.length).toEqual(1);\n      const [topRightWrapper] = wrappers;\n      wrappers = rendered.querySelectorAll('.BottomLeftGrid_ScrollWrapper');\n      expect(wrappers.length).toEqual(1);\n      const [bottomLeftWrapper] = wrappers;\n\n      expect(topRightWrapper.style.getPropertyValue('overflow-x')).toEqual(\n        'hidden',\n      );\n      expect(topRightWrapper.style.getPropertyValue('overflow-y')).toEqual(\n        'hidden',\n      );\n      expect(bottomLeftWrapper.style.getPropertyValue('overflow-x')).toEqual(\n        'hidden',\n      );\n      expect(bottomLeftWrapper.style.getPropertyValue('overflow-y')).toEqual(\n        'hidden',\n      );\n\n      expect(topRightWrapper.style.getPropertyValue('height')).toEqual('20px');\n      expect(bottomLeftWrapper.style.getPropertyValue('height')).toEqual(\n        '280px',\n      );\n      expect(topRightWrapper.style.getPropertyValue('width')).toEqual('350px');\n      expect(bottomLeftWrapper.style.getPropertyValue('width')).toEqual('50px');\n\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(grids.length).toEqual(4);\n      const [topLeft, topRight, bottomLeft, bottomRight] = grids;\n      expect(topLeft.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(topLeft.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(topRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(topRight.style.getPropertyValue('overflow-y')).toEqual('hidden');\n      expect(topRight.style.getPropertyValue('height')).toEqual('40px');\n      expect(bottomLeft.style.getPropertyValue('overflow-x')).toEqual('hidden');\n      expect(bottomLeft.style.getPropertyValue('overflow-y')).toEqual('auto');\n      expect(bottomLeft.style.getPropertyValue('width')).toEqual('70px');\n      expect(bottomRight.style.getPropertyValue('overflow-x')).toEqual('auto');\n      expect(bottomRight.style.getPropertyValue('overflow-y')).toEqual('auto');\n    });\n  });\n\n  describe('#recomputeGridSize', () => {\n    it('should clear calculated cached styles in recomputeGridSize', () => {\n      let fixedRowHeight = 75;\n      let fixedColumnWidth = 100;\n\n      function variableRowHeight({index}) {\n        if (index === 0) {\n          return fixedRowHeight;\n        }\n        return 20;\n      }\n      function variableColumnWidth({index}) {\n        if (index === 0) {\n          return fixedColumnWidth;\n        }\n        return 50;\n      }\n\n      let multiGrid;\n      let rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 1,\n            fixedRowCount: 1,\n            rowHeight: variableRowHeight,\n            columnWidth: variableColumnWidth,\n            ref: ref => {\n              multiGrid = ref;\n            },\n          }),\n        ),\n      );\n\n      let grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(grids.length).toEqual(4);\n      let [topLeft, topRight, bottomLeft, bottomRight] = grids;\n      expect(topLeft.style.getPropertyValue('height')).toEqual('75px');\n      expect(topRight.style.getPropertyValue('height')).toEqual('75px');\n      expect(bottomLeft.style.getPropertyValue('height')).toEqual('225px');\n      expect(bottomRight.style.getPropertyValue('height')).toEqual('225px');\n\n      expect(topLeft.style.getPropertyValue('width')).toEqual('100px');\n      expect(topRight.style.getPropertyValue('width')).toEqual('300px');\n      expect(bottomLeft.style.getPropertyValue('width')).toEqual('100px');\n      expect(bottomRight.style.getPropertyValue('width')).toEqual('300px');\n\n      expect(multiGrid._topGridHeight).toEqual(75);\n      expect(multiGrid._leftGridWidth).toEqual(100);\n\n      fixedRowHeight = 125;\n      fixedColumnWidth = 75;\n      multiGrid.recomputeGridSize();\n      expect(multiGrid._topGridHeight).toEqual(125);\n      expect(multiGrid._leftGridWidth).toEqual(75);\n\n      multiGrid.forceUpdate();\n\n      let gridsAfter = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      expect(gridsAfter.length).toEqual(4);\n      let [\n        topLeftAfter,\n        topRightAfter,\n        bottomLeftAfter,\n        bottomRightAfter,\n      ] = gridsAfter;\n      expect(topLeftAfter.style.getPropertyValue('height')).toEqual('125px');\n      expect(topRightAfter.style.getPropertyValue('height')).toEqual('125px');\n      expect(bottomLeftAfter.style.getPropertyValue('height')).toEqual('175px');\n      expect(bottomRightAfter.style.getPropertyValue('height')).toEqual(\n        '175px',\n      );\n\n      expect(topLeftAfter.style.getPropertyValue('width')).toEqual('75px');\n      expect(topRightAfter.style.getPropertyValue('width')).toEqual('325px');\n      expect(bottomLeftAfter.style.getPropertyValue('width')).toEqual('75px');\n      expect(bottomRightAfter.style.getPropertyValue('width')).toEqual('325px');\n    });\n  });\n\n  describe('scrollToColumn and scrollToRow', () => {\n    it('should adjust :scrollLeft for the main Grid when scrollToColumn is used', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnWidth: 50,\n            fixedColumnCount: 2,\n            scrollToAlignment: 'start',\n            scrollToColumn: 19,\n          }),\n        ),\n      );\n      // Bottom-right Grid is the last Grid\n      const grid = rendered.querySelectorAll('.ReactVirtualized__Grid')[3];\n      // 20th column, less 2 for the fixed-column Grid, 50px column width\n      expect(grid.scrollLeft).toEqual(850);\n    });\n\n    it('should adjust :scrollTop for the main Grid when scrollToRow is used', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedRowCount: 1,\n            rowHeight: 50,\n            scrollToAlignment: 'start',\n            scrollToRow: 19,\n          }),\n        ),\n      );\n      // Bottom-right Grid is the last Grid\n      const grid = rendered.querySelectorAll('.ReactVirtualized__Grid')[3];\n      // 20th row, less 1 for the fixed-row Grid, 50px row width\n      expect(grid.scrollTop).toEqual(900);\n    });\n  });\n\n  describe('#forceUpdateGrids', () => {\n    it('should call forceUpdate() on inner Grids', () => {\n      const cellRenderer = jest.fn();\n      cellRenderer.mockImplementation(({key}) => <div key={key} style={{}} />);\n\n      const rendered = render(\n        getMarkup({\n          cellRenderer,\n          columnCount: 2,\n          fixedColumnCount: 1,\n          fixedRowCount: 1,\n          rowCount: 2,\n        }),\n      );\n\n      expect(cellRenderer.mock.calls).toHaveLength(4);\n\n      cellRenderer.mockReset();\n      rendered.forceUpdateGrids();\n\n      expect(cellRenderer.mock.calls).toHaveLength(4);\n    });\n  });\n\n  describe('#invalidateCellSizeAfterRender', () => {\n    it('should call invalidateCellSizeAfterRender() on inner Grids', () => {\n      const cellRenderer = jest.fn();\n      cellRenderer.mockImplementation(({key}) => <div key={key} style={{}} />);\n\n      const rendered = render(\n        getMarkup({\n          cellRenderer,\n          columnCount: 2,\n          fixedColumnCount: 1,\n          fixedRowCount: 1,\n          rowCount: 2,\n        }),\n      );\n\n      cellRenderer.mockReset();\n      rendered.invalidateCellSizeAfterRender({\n        columnIndex: 0,\n        rowIndex: 0,\n      });\n\n      rendered.forceUpdate();\n\n      expect(cellRenderer.mock.calls).toHaveLength(4);\n    });\n\n    it('should specify itself as the :parent for CellMeasurer rendered cells', () => {\n      // HACK For some reason, using Jest mock broke here\n      let savedParent;\n      function cellRenderer({key, parent}) {\n        savedParent = parent;\n        return <div key={key} style={{}} />;\n      }\n\n      const rendered = render(\n        getMarkup({\n          cellRenderer,\n          columnCount: 2,\n          fixedColumnCount: 1,\n          fixedRowCount: 1,\n          rowCount: 2,\n        }),\n      );\n\n      expect(savedParent).toBe(rendered);\n    });\n  });\n\n  describe('styles', () => {\n    it('should support custom style for the outer MultiGrid wrapper element', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            style: {backgroundColor: 'black'},\n          }),\n        ),\n      );\n      expect(rendered.style.backgroundColor).toEqual('black');\n    });\n\n    it('should support custom styles for each Grid', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            fixedColumnCount: 2,\n            fixedRowCount: 1,\n            styleBottomLeftGrid: {backgroundColor: 'green'},\n            styleBottomRightGrid: {backgroundColor: 'red'},\n            styleTopLeftGrid: {backgroundColor: 'blue'},\n            styleTopRightGrid: {backgroundColor: 'purple'},\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      const topLeftGrid = grids[0];\n      const topRightGrid = grids[1];\n      const bottomLeftGrid = grids[2];\n      const bottomRightGrid = grids[3];\n      expect(topLeftGrid.style.backgroundColor).toEqual('blue');\n      expect(topRightGrid.style.backgroundColor).toEqual('purple');\n      expect(bottomLeftGrid.style.backgroundColor).toEqual('green');\n      expect(bottomRightGrid.style.backgroundColor).toEqual('red');\n    });\n  });\n  describe('scrollTop and scrollLeft', () => {\n    it('should adjust :scrollLeft for top-right and main grids when scrollLeft is used', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnWidth: 50,\n            fixedColumnCount: 2,\n            scrollLeft: 850,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      const topRightGrid = grids[1];\n      const bottomRightGrid = grids[3];\n      expect(topRightGrid.scrollLeft).toEqual(850);\n      expect(bottomRightGrid.scrollLeft).toEqual(850);\n    });\n\n    it('should adjust :scrollTop for bottom-left and main grids when scrollTop is used', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnWidth: 50,\n            fixedColumnCount: 2,\n            scrollTop: 500,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      const bottomLeftGrid = grids[2];\n      const bottomRightGrid = grids[3];\n      expect(bottomLeftGrid.scrollTop).toEqual(500);\n      expect(bottomRightGrid.scrollTop).toEqual(500);\n    });\n\n    it('should adjust :scrollTop and :scrollLeft when scrollTop and scrollLeft change', () => {\n      render(getMarkup());\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            scrollTop: 750,\n            scrollLeft: 900,\n          }),\n        ),\n      );\n      const grids = rendered.querySelectorAll('.ReactVirtualized__Grid');\n      const topRightGrid = grids[1];\n      const bottomLeftGrid = grids[2];\n      const bottomRightGrid = grids[3];\n      expect(topRightGrid.scrollLeft).toEqual(900);\n      expect(bottomRightGrid.scrollLeft).toEqual(900);\n      expect(bottomLeftGrid.scrollTop).toEqual(750);\n      expect(bottomRightGrid.scrollTop).toEqual(750);\n    });\n\n    it('should not crash when decreasing :rowCount', () => {\n      render(getMarkup());\n      const updated = render(\n        getMarkup({\n          rowCount: 2,\n        }),\n      );\n      expect(updated.props.rowCount).toEqual(2);\n    });\n\n    it('should not crash when decreasing :columnCount', () => {\n      render(getMarkup());\n      const updated = render(\n        getMarkup({\n          columnCount: 3,\n        }),\n      );\n      expect(updated.props.columnCount).toEqual(3);\n    });\n  });\n\n  describe('deferredMeasurementCache', () => {\n    function getDeferredMeasurementCache() {\n      const deferredMeasurementCache = new CellMeasurerCache({\n        fixedHeight: true,\n        fixedWidth: true,\n      });\n\n      deferredMeasurementCache._columnIndices = {};\n      deferredMeasurementCache._rowIndices = {};\n      deferredMeasurementCache.has = (rowIndex, columnIndex) => {\n        deferredMeasurementCache._columnIndices[columnIndex] = columnIndex;\n        deferredMeasurementCache._rowIndices[rowIndex] = rowIndex;\n        return true;\n      };\n\n      return deferredMeasurementCache;\n    }\n\n    it('should wrap top-right and bottom-right deferredMeasurementCache if fixedColumnCount is > 0', () => {\n      const deferredMeasurementCache = getDeferredMeasurementCache();\n      render(\n        getMarkup({\n          deferredMeasurementCache: deferredMeasurementCache,\n          columnCount: 3,\n          fixedColumnCount: 1,\n          fixedRowCount: 0,\n          rowCount: 1,\n        }),\n      );\n\n      expect(Object.keys(deferredMeasurementCache._columnIndices)).toEqual([\n        '0',\n        '1',\n        '2',\n      ]);\n    });\n\n    it('should not wrap top-right and bottom-right deferredMeasurementCache if fixedColumnCount is 0', () => {\n      const deferredMeasurementCache = getDeferredMeasurementCache();\n      render(\n        getMarkup({\n          deferredMeasurementCache: deferredMeasurementCache,\n          columnCount: 2,\n          fixedColumnCount: 0,\n          fixedRowCount: 0,\n          rowCount: 1,\n        }),\n      );\n\n      expect(Object.keys(deferredMeasurementCache._columnIndices)).toEqual([\n        '0',\n        '1',\n      ]);\n    });\n\n    it('should wrap bottom-left and bottom-right deferredMeasurementCache if fixedRowCount is > 0', () => {\n      const deferredMeasurementCache = getDeferredMeasurementCache();\n      render(\n        getMarkup({\n          deferredMeasurementCache: deferredMeasurementCache,\n          columnCount: 1,\n          fixedColumnCount: 0,\n          fixedRowCount: 1,\n          rowCount: 3,\n        }),\n      );\n\n      expect(Object.keys(deferredMeasurementCache._rowIndices)).toEqual([\n        '0',\n        '1',\n        '2',\n      ]);\n    });\n\n    it('should not wrap bottom-left and bottom-right deferredMeasurementCache if fixedRowCount is 0', () => {\n      const deferredMeasurementCache = getDeferredMeasurementCache();\n      render(\n        getMarkup({\n          deferredMeasurementCache: deferredMeasurementCache,\n          columnCount: 1,\n          fixedColumnCount: 0,\n          fixedRowCount: 0,\n          rowCount: 2,\n        }),\n      );\n\n      expect(Object.keys(deferredMeasurementCache._rowIndices)).toEqual([\n        '0',\n        '1',\n      ]);\n    });\n  });\n\n  describe('onScrollbarPresenceChange', () => {\n    function getScrollbarSize20() {\n      return 20;\n    }\n\n    it('should not trigger on-mount if scrollbars are hidden', () => {\n      const onScrollbarPresenceChange = jest.fn();\n\n      render(\n        getMarkup({\n          columnCount: 1,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 1,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n    });\n\n    it('should trigger on-mount if scrollbars are visible', () => {\n      const onScrollbarPresenceChange = jest.fn();\n\n      render(\n        getMarkup({\n          columnCount: 100,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 100,\n        }),\n      );\n      expect(onScrollbarPresenceChange).toHaveBeenCalled();\n\n      const args = onScrollbarPresenceChange.mock.calls[0][0];\n      expect(args.horizontal).toBe(true);\n      expect(args.size).toBe(getScrollbarSize20());\n      expect(args.vertical).toBe(true);\n    });\n\n    it('should trigger on-update if scrollbar visibility has changed', () => {\n      const onScrollbarPresenceChange = jest.fn();\n      render(\n        getMarkup({\n          columnCount: 1,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 1,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n\n      render(\n        getMarkup({\n          columnCount: 100,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 100,\n        }),\n      );\n      expect(onScrollbarPresenceChange).toHaveBeenCalled();\n\n      const args = onScrollbarPresenceChange.mock.calls[0][0];\n      expect(args.horizontal).toBe(true);\n      expect(args.size).toBe(getScrollbarSize20());\n      expect(args.vertical).toBe(true);\n    });\n\n    it('should not trigger on-update if scrollbar visibility does not change', () => {\n      const onScrollbarPresenceChange = jest.fn();\n      render(\n        getMarkup({\n          columnCount: 1,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 1,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n      render(\n        getMarkup({\n          columnCount: 2,\n          getScrollbarSize: getScrollbarSize20,\n          onScrollbarPresenceChange,\n          rowCount: 2,\n        }),\n      );\n      expect(onScrollbarPresenceChange).not.toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "source/MultiGrid/MultiGrid.js",
    "content": "/** @flow */\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {polyfill} from 'react-lifecycles-compat';\nimport CellMeasurerCacheDecorator from './CellMeasurerCacheDecorator';\nimport Grid from '../Grid';\n\nconst SCROLLBAR_SIZE_BUFFER = 20;\n\n/**\n * Renders 1, 2, or 4 Grids depending on configuration.\n * A main (body) Grid will always be rendered.\n * Optionally, 1-2 Grids for sticky header rows will also be rendered.\n * If no sticky columns, only 1 sticky header Grid will be rendered.\n * If sticky columns, 2 sticky header Grids will be rendered.\n */\nclass MultiGrid extends React.PureComponent {\n  static propTypes = {\n    classNameBottomLeftGrid: PropTypes.string.isRequired,\n    classNameBottomRightGrid: PropTypes.string.isRequired,\n    classNameTopLeftGrid: PropTypes.string.isRequired,\n    classNameTopRightGrid: PropTypes.string.isRequired,\n    enableFixedColumnScroll: PropTypes.bool.isRequired,\n    enableFixedRowScroll: PropTypes.bool.isRequired,\n    fixedColumnCount: PropTypes.number.isRequired,\n    fixedRowCount: PropTypes.number.isRequired,\n    onScrollbarPresenceChange: PropTypes.func,\n    style: PropTypes.object.isRequired,\n    styleBottomLeftGrid: PropTypes.object.isRequired,\n    styleBottomRightGrid: PropTypes.object.isRequired,\n    styleTopLeftGrid: PropTypes.object.isRequired,\n    styleTopRightGrid: PropTypes.object.isRequired,\n    hideTopRightGridScrollbar: PropTypes.bool,\n    hideBottomLeftGridScrollbar: PropTypes.bool,\n  };\n\n  static defaultProps = {\n    classNameBottomLeftGrid: '',\n    classNameBottomRightGrid: '',\n    classNameTopLeftGrid: '',\n    classNameTopRightGrid: '',\n    enableFixedColumnScroll: false,\n    enableFixedRowScroll: false,\n    fixedColumnCount: 0,\n    fixedRowCount: 0,\n    scrollToColumn: -1,\n    scrollToRow: -1,\n    style: {},\n    styleBottomLeftGrid: {},\n    styleBottomRightGrid: {},\n    styleTopLeftGrid: {},\n    styleTopRightGrid: {},\n    hideTopRightGridScrollbar: false,\n    hideBottomLeftGridScrollbar: false,\n  };\n\n  state = {\n    scrollLeft: 0,\n    scrollTop: 0,\n    scrollbarSize: 0,\n    showHorizontalScrollbar: false,\n    showVerticalScrollbar: false,\n  };\n\n  _deferredInvalidateColumnIndex = null;\n  _deferredInvalidateRowIndex = null;\n\n  constructor(props, context) {\n    super(props, context);\n\n    const {deferredMeasurementCache, fixedColumnCount, fixedRowCount} = props;\n\n    this._maybeCalculateCachedStyles(true);\n\n    if (deferredMeasurementCache) {\n      this._deferredMeasurementCacheBottomLeftGrid =\n        fixedRowCount > 0\n          ? new CellMeasurerCacheDecorator({\n              cellMeasurerCache: deferredMeasurementCache,\n              columnIndexOffset: 0,\n              rowIndexOffset: fixedRowCount,\n            })\n          : deferredMeasurementCache;\n\n      this._deferredMeasurementCacheBottomRightGrid =\n        fixedColumnCount > 0 || fixedRowCount > 0\n          ? new CellMeasurerCacheDecorator({\n              cellMeasurerCache: deferredMeasurementCache,\n              columnIndexOffset: fixedColumnCount,\n              rowIndexOffset: fixedRowCount,\n            })\n          : deferredMeasurementCache;\n\n      this._deferredMeasurementCacheTopRightGrid =\n        fixedColumnCount > 0\n          ? new CellMeasurerCacheDecorator({\n              cellMeasurerCache: deferredMeasurementCache,\n              columnIndexOffset: fixedColumnCount,\n              rowIndexOffset: 0,\n            })\n          : deferredMeasurementCache;\n    }\n  }\n\n  forceUpdateGrids() {\n    this._bottomLeftGrid && this._bottomLeftGrid.forceUpdate();\n    this._bottomRightGrid && this._bottomRightGrid.forceUpdate();\n    this._topLeftGrid && this._topLeftGrid.forceUpdate();\n    this._topRightGrid && this._topRightGrid.forceUpdate();\n  }\n\n  /** See Grid#invalidateCellSizeAfterRender */\n  invalidateCellSizeAfterRender({columnIndex = 0, rowIndex = 0} = {}) {\n    this._deferredInvalidateColumnIndex =\n      typeof this._deferredInvalidateColumnIndex === 'number'\n        ? Math.min(this._deferredInvalidateColumnIndex, columnIndex)\n        : columnIndex;\n    this._deferredInvalidateRowIndex =\n      typeof this._deferredInvalidateRowIndex === 'number'\n        ? Math.min(this._deferredInvalidateRowIndex, rowIndex)\n        : rowIndex;\n  }\n\n  /** See Grid#measureAllCells */\n  measureAllCells() {\n    this._bottomLeftGrid && this._bottomLeftGrid.measureAllCells();\n    this._bottomRightGrid && this._bottomRightGrid.measureAllCells();\n    this._topLeftGrid && this._topLeftGrid.measureAllCells();\n    this._topRightGrid && this._topRightGrid.measureAllCells();\n  }\n\n  /** See Grid#recomputeGridSize */\n  recomputeGridSize({columnIndex = 0, rowIndex = 0} = {}) {\n    const {fixedColumnCount, fixedRowCount} = this.props;\n\n    const adjustedColumnIndex = Math.max(0, columnIndex - fixedColumnCount);\n    const adjustedRowIndex = Math.max(0, rowIndex - fixedRowCount);\n\n    this._bottomLeftGrid &&\n      this._bottomLeftGrid.recomputeGridSize({\n        columnIndex,\n        rowIndex: adjustedRowIndex,\n      });\n    this._bottomRightGrid &&\n      this._bottomRightGrid.recomputeGridSize({\n        columnIndex: adjustedColumnIndex,\n        rowIndex: adjustedRowIndex,\n      });\n    this._topLeftGrid &&\n      this._topLeftGrid.recomputeGridSize({\n        columnIndex,\n        rowIndex,\n      });\n    this._topRightGrid &&\n      this._topRightGrid.recomputeGridSize({\n        columnIndex: adjustedColumnIndex,\n        rowIndex,\n      });\n\n    this._leftGridWidth = null;\n    this._topGridHeight = null;\n    this._maybeCalculateCachedStyles(true);\n  }\n\n  static getDerivedStateFromProps(nextProps, prevState) {\n    if (\n      nextProps.scrollLeft !== prevState.scrollLeft ||\n      nextProps.scrollTop !== prevState.scrollTop\n    ) {\n      return {\n        scrollLeft:\n          nextProps.scrollLeft != null && nextProps.scrollLeft >= 0\n            ? nextProps.scrollLeft\n            : prevState.scrollLeft,\n        scrollTop:\n          nextProps.scrollTop != null && nextProps.scrollTop >= 0\n            ? nextProps.scrollTop\n            : prevState.scrollTop,\n      };\n    }\n\n    return null;\n  }\n\n  componentDidMount() {\n    const {scrollLeft, scrollTop} = this.props;\n\n    if (scrollLeft > 0 || scrollTop > 0) {\n      const newState = {};\n\n      if (scrollLeft > 0) {\n        newState.scrollLeft = scrollLeft;\n      }\n\n      if (scrollTop > 0) {\n        newState.scrollTop = scrollTop;\n      }\n\n      this.setState(newState);\n    }\n    this._handleInvalidatedGridSize();\n  }\n\n  componentDidUpdate() {\n    this._handleInvalidatedGridSize();\n  }\n\n  render() {\n    const {\n      onScroll,\n      onSectionRendered,\n      onScrollbarPresenceChange, // eslint-disable-line no-unused-vars\n      scrollLeft: scrollLeftProp, // eslint-disable-line no-unused-vars\n      scrollToColumn,\n      scrollTop: scrollTopProp, // eslint-disable-line no-unused-vars\n      scrollToRow,\n      ...rest\n    } = this.props;\n\n    this._prepareForRender();\n\n    // Don't render any of our Grids if there are no cells.\n    // This mirrors what Grid does,\n    // And prevents us from recording inaccurage measurements when used with CellMeasurer.\n    if (this.props.width === 0 || this.props.height === 0) {\n      return null;\n    }\n\n    // scrollTop and scrollLeft props are explicitly filtered out and ignored\n\n    const {scrollLeft, scrollTop} = this.state;\n\n    return (\n      <div style={this._containerOuterStyle}>\n        <div style={this._containerTopStyle}>\n          {this._renderTopLeftGrid(rest)}\n          {this._renderTopRightGrid({\n            ...rest,\n            onScroll,\n            scrollLeft,\n          })}\n        </div>\n        <div style={this._containerBottomStyle}>\n          {this._renderBottomLeftGrid({\n            ...rest,\n            onScroll,\n            scrollTop,\n          })}\n          {this._renderBottomRightGrid({\n            ...rest,\n            onScroll,\n            onSectionRendered,\n            scrollLeft,\n            scrollToColumn,\n            scrollToRow,\n            scrollTop,\n          })}\n        </div>\n      </div>\n    );\n  }\n\n  _bottomLeftGridRef = ref => {\n    this._bottomLeftGrid = ref;\n  };\n\n  _bottomRightGridRef = ref => {\n    this._bottomRightGrid = ref;\n  };\n\n  _cellRendererBottomLeftGrid = ({rowIndex, ...rest}) => {\n    const {cellRenderer, fixedRowCount, rowCount} = this.props;\n\n    if (rowIndex === rowCount - fixedRowCount) {\n      return (\n        <div\n          key={rest.key}\n          style={{\n            ...rest.style,\n            height: SCROLLBAR_SIZE_BUFFER,\n          }}\n        />\n      );\n    } else {\n      return cellRenderer({\n        ...rest,\n        parent: this,\n        rowIndex: rowIndex + fixedRowCount,\n      });\n    }\n  };\n\n  _cellRendererBottomRightGrid = ({columnIndex, rowIndex, ...rest}) => {\n    const {cellRenderer, fixedColumnCount, fixedRowCount} = this.props;\n\n    return cellRenderer({\n      ...rest,\n      columnIndex: columnIndex + fixedColumnCount,\n      parent: this,\n      rowIndex: rowIndex + fixedRowCount,\n    });\n  };\n\n  _cellRendererTopRightGrid = ({columnIndex, ...rest}) => {\n    const {cellRenderer, columnCount, fixedColumnCount} = this.props;\n\n    if (columnIndex === columnCount - fixedColumnCount) {\n      return (\n        <div\n          key={rest.key}\n          style={{\n            ...rest.style,\n            width: SCROLLBAR_SIZE_BUFFER,\n          }}\n        />\n      );\n    } else {\n      return cellRenderer({\n        ...rest,\n        columnIndex: columnIndex + fixedColumnCount,\n        parent: this,\n      });\n    }\n  };\n\n  _columnWidthRightGrid = ({index}) => {\n    const {columnCount, fixedColumnCount, columnWidth} = this.props;\n    const {scrollbarSize, showHorizontalScrollbar} = this.state;\n\n    // An extra cell is added to the count\n    // This gives the smaller Grid extra room for offset,\n    // In case the main (bottom right) Grid has a scrollbar\n    // If no scrollbar, the extra space is overflow:hidden anyway\n    if (showHorizontalScrollbar && index === columnCount - fixedColumnCount) {\n      return scrollbarSize;\n    }\n\n    return typeof columnWidth === 'function'\n      ? columnWidth({index: index + fixedColumnCount})\n      : columnWidth;\n  };\n\n  _getBottomGridHeight(props) {\n    const {height} = props;\n\n    let topGridHeight = this._getTopGridHeight(props);\n\n    return height - topGridHeight;\n  }\n\n  _getLeftGridWidth(props) {\n    const {fixedColumnCount, columnWidth} = props;\n\n    if (this._leftGridWidth == null) {\n      if (typeof columnWidth === 'function') {\n        let leftGridWidth = 0;\n\n        for (let index = 0; index < fixedColumnCount; index++) {\n          leftGridWidth += columnWidth({index});\n        }\n\n        this._leftGridWidth = leftGridWidth;\n      } else {\n        this._leftGridWidth = columnWidth * fixedColumnCount;\n      }\n    }\n\n    return this._leftGridWidth;\n  }\n\n  _getRightGridWidth(props) {\n    const {width} = props;\n\n    let leftGridWidth = this._getLeftGridWidth(props);\n\n    return width - leftGridWidth;\n  }\n\n  _getTopGridHeight(props) {\n    const {fixedRowCount, rowHeight} = props;\n\n    if (this._topGridHeight == null) {\n      if (typeof rowHeight === 'function') {\n        let topGridHeight = 0;\n\n        for (let index = 0; index < fixedRowCount; index++) {\n          topGridHeight += rowHeight({index});\n        }\n\n        this._topGridHeight = topGridHeight;\n      } else {\n        this._topGridHeight = rowHeight * fixedRowCount;\n      }\n    }\n\n    return this._topGridHeight;\n  }\n\n  _handleInvalidatedGridSize() {\n    if (typeof this._deferredInvalidateColumnIndex === 'number') {\n      const columnIndex = this._deferredInvalidateColumnIndex;\n      const rowIndex = this._deferredInvalidateRowIndex;\n\n      this._deferredInvalidateColumnIndex = null;\n      this._deferredInvalidateRowIndex = null;\n\n      this.recomputeGridSize({\n        columnIndex,\n        rowIndex,\n      });\n      this.forceUpdate();\n    }\n  }\n\n  /**\n   * Avoid recreating inline styles each render; this bypasses Grid's shallowCompare.\n   * This method recalculates styles only when specific props change.\n   */\n  _maybeCalculateCachedStyles(resetAll) {\n    const {\n      columnWidth,\n      enableFixedColumnScroll,\n      enableFixedRowScroll,\n      height,\n      fixedColumnCount,\n      fixedRowCount,\n      rowHeight,\n      style,\n      styleBottomLeftGrid,\n      styleBottomRightGrid,\n      styleTopLeftGrid,\n      styleTopRightGrid,\n      width,\n    } = this.props;\n\n    const sizeChange =\n      resetAll ||\n      height !== this._lastRenderedHeight ||\n      width !== this._lastRenderedWidth;\n    const leftSizeChange =\n      resetAll ||\n      columnWidth !== this._lastRenderedColumnWidth ||\n      fixedColumnCount !== this._lastRenderedFixedColumnCount;\n    const topSizeChange =\n      resetAll ||\n      fixedRowCount !== this._lastRenderedFixedRowCount ||\n      rowHeight !== this._lastRenderedRowHeight;\n\n    if (resetAll || sizeChange || style !== this._lastRenderedStyle) {\n      this._containerOuterStyle = {\n        height,\n        overflow: 'visible', // Let :focus outline show through\n        width,\n        ...style,\n      };\n    }\n\n    if (resetAll || sizeChange || topSizeChange) {\n      this._containerTopStyle = {\n        height: this._getTopGridHeight(this.props),\n        position: 'relative',\n        width,\n      };\n\n      this._containerBottomStyle = {\n        height: height - this._getTopGridHeight(this.props),\n        overflow: 'visible', // Let :focus outline show through\n        position: 'relative',\n        width,\n      };\n    }\n\n    if (\n      resetAll ||\n      styleBottomLeftGrid !== this._lastRenderedStyleBottomLeftGrid\n    ) {\n      this._bottomLeftGridStyle = {\n        left: 0,\n        overflowX: 'hidden',\n        overflowY: enableFixedColumnScroll ? 'auto' : 'hidden',\n        position: 'absolute',\n        ...styleBottomLeftGrid,\n      };\n    }\n\n    if (\n      resetAll ||\n      leftSizeChange ||\n      styleBottomRightGrid !== this._lastRenderedStyleBottomRightGrid\n    ) {\n      this._bottomRightGridStyle = {\n        left: this._getLeftGridWidth(this.props),\n        position: 'absolute',\n        ...styleBottomRightGrid,\n      };\n    }\n\n    if (resetAll || styleTopLeftGrid !== this._lastRenderedStyleTopLeftGrid) {\n      this._topLeftGridStyle = {\n        left: 0,\n        overflowX: 'hidden',\n        overflowY: 'hidden',\n        position: 'absolute',\n        top: 0,\n        ...styleTopLeftGrid,\n      };\n    }\n\n    if (\n      resetAll ||\n      leftSizeChange ||\n      styleTopRightGrid !== this._lastRenderedStyleTopRightGrid\n    ) {\n      this._topRightGridStyle = {\n        left: this._getLeftGridWidth(this.props),\n        overflowX: enableFixedRowScroll ? 'auto' : 'hidden',\n        overflowY: 'hidden',\n        position: 'absolute',\n        top: 0,\n        ...styleTopRightGrid,\n      };\n    }\n\n    this._lastRenderedColumnWidth = columnWidth;\n    this._lastRenderedFixedColumnCount = fixedColumnCount;\n    this._lastRenderedFixedRowCount = fixedRowCount;\n    this._lastRenderedHeight = height;\n    this._lastRenderedRowHeight = rowHeight;\n    this._lastRenderedStyle = style;\n    this._lastRenderedStyleBottomLeftGrid = styleBottomLeftGrid;\n    this._lastRenderedStyleBottomRightGrid = styleBottomRightGrid;\n    this._lastRenderedStyleTopLeftGrid = styleTopLeftGrid;\n    this._lastRenderedStyleTopRightGrid = styleTopRightGrid;\n    this._lastRenderedWidth = width;\n  }\n\n  _prepareForRender() {\n    if (\n      this._lastRenderedColumnWidth !== this.props.columnWidth ||\n      this._lastRenderedFixedColumnCount !== this.props.fixedColumnCount\n    ) {\n      this._leftGridWidth = null;\n    }\n\n    if (\n      this._lastRenderedFixedRowCount !== this.props.fixedRowCount ||\n      this._lastRenderedRowHeight !== this.props.rowHeight\n    ) {\n      this._topGridHeight = null;\n    }\n\n    this._maybeCalculateCachedStyles();\n\n    this._lastRenderedColumnWidth = this.props.columnWidth;\n    this._lastRenderedFixedColumnCount = this.props.fixedColumnCount;\n    this._lastRenderedFixedRowCount = this.props.fixedRowCount;\n    this._lastRenderedRowHeight = this.props.rowHeight;\n  }\n\n  _onScroll = scrollInfo => {\n    const {scrollLeft, scrollTop} = scrollInfo;\n    this.setState({\n      scrollLeft,\n      scrollTop,\n    });\n    const onScroll = this.props.onScroll;\n    if (onScroll) {\n      onScroll(scrollInfo);\n    }\n  };\n\n  _onScrollbarPresenceChange = ({horizontal, size, vertical}) => {\n    const {showHorizontalScrollbar, showVerticalScrollbar} = this.state;\n\n    if (\n      horizontal !== showHorizontalScrollbar ||\n      vertical !== showVerticalScrollbar\n    ) {\n      this.setState({\n        scrollbarSize: size,\n        showHorizontalScrollbar: horizontal,\n        showVerticalScrollbar: vertical,\n      });\n\n      const {onScrollbarPresenceChange} = this.props;\n      if (typeof onScrollbarPresenceChange === 'function') {\n        onScrollbarPresenceChange({\n          horizontal,\n          size,\n          vertical,\n        });\n      }\n    }\n  };\n\n  _onScrollLeft = scrollInfo => {\n    const {scrollLeft} = scrollInfo;\n    this._onScroll({\n      scrollLeft,\n      scrollTop: this.state.scrollTop,\n    });\n  };\n\n  _onScrollTop = scrollInfo => {\n    const {scrollTop} = scrollInfo;\n    this._onScroll({\n      scrollTop,\n      scrollLeft: this.state.scrollLeft,\n    });\n  };\n\n  _renderBottomLeftGrid(props) {\n    const {\n      enableFixedColumnScroll,\n      fixedColumnCount,\n      fixedRowCount,\n      rowCount,\n      hideBottomLeftGridScrollbar,\n    } = props;\n    const {showVerticalScrollbar} = this.state;\n\n    if (!fixedColumnCount) {\n      return null;\n    }\n\n    const additionalRowCount = showVerticalScrollbar ? 1 : 0,\n      height = this._getBottomGridHeight(props),\n      width = this._getLeftGridWidth(props),\n      scrollbarSize = this.state.showVerticalScrollbar\n        ? this.state.scrollbarSize\n        : 0,\n      gridWidth = hideBottomLeftGridScrollbar ? width + scrollbarSize : width;\n\n    const bottomLeftGrid = (\n      <Grid\n        {...props}\n        cellRenderer={this._cellRendererBottomLeftGrid}\n        className={this.props.classNameBottomLeftGrid}\n        columnCount={fixedColumnCount}\n        deferredMeasurementCache={this._deferredMeasurementCacheBottomLeftGrid}\n        height={height}\n        onScroll={enableFixedColumnScroll ? this._onScrollTop : undefined}\n        ref={this._bottomLeftGridRef}\n        rowCount={Math.max(0, rowCount - fixedRowCount) + additionalRowCount}\n        rowHeight={this._rowHeightBottomGrid}\n        style={this._bottomLeftGridStyle}\n        tabIndex={null}\n        width={gridWidth}\n      />\n    );\n\n    if (hideBottomLeftGridScrollbar) {\n      return (\n        <div\n          className=\"BottomLeftGrid_ScrollWrapper\"\n          style={{\n            ...this._bottomLeftGridStyle,\n            height,\n            width,\n            overflowY: 'hidden',\n          }}>\n          {bottomLeftGrid}\n        </div>\n      );\n    }\n    return bottomLeftGrid;\n  }\n\n  _renderBottomRightGrid(props) {\n    const {\n      columnCount,\n      fixedColumnCount,\n      fixedRowCount,\n      rowCount,\n      scrollToColumn,\n      scrollToRow,\n    } = props;\n\n    return (\n      <Grid\n        {...props}\n        cellRenderer={this._cellRendererBottomRightGrid}\n        className={this.props.classNameBottomRightGrid}\n        columnCount={Math.max(0, columnCount - fixedColumnCount)}\n        columnWidth={this._columnWidthRightGrid}\n        deferredMeasurementCache={this._deferredMeasurementCacheBottomRightGrid}\n        height={this._getBottomGridHeight(props)}\n        onScroll={this._onScroll}\n        onScrollbarPresenceChange={this._onScrollbarPresenceChange}\n        ref={this._bottomRightGridRef}\n        rowCount={Math.max(0, rowCount - fixedRowCount)}\n        rowHeight={this._rowHeightBottomGrid}\n        scrollToColumn={scrollToColumn - fixedColumnCount}\n        scrollToRow={scrollToRow - fixedRowCount}\n        style={this._bottomRightGridStyle}\n        width={this._getRightGridWidth(props)}\n      />\n    );\n  }\n\n  _renderTopLeftGrid(props) {\n    const {fixedColumnCount, fixedRowCount} = props;\n\n    if (!fixedColumnCount || !fixedRowCount) {\n      return null;\n    }\n\n    return (\n      <Grid\n        {...props}\n        className={this.props.classNameTopLeftGrid}\n        columnCount={fixedColumnCount}\n        height={this._getTopGridHeight(props)}\n        ref={this._topLeftGridRef}\n        rowCount={fixedRowCount}\n        style={this._topLeftGridStyle}\n        tabIndex={null}\n        width={this._getLeftGridWidth(props)}\n      />\n    );\n  }\n\n  _renderTopRightGrid(props) {\n    const {\n      columnCount,\n      enableFixedRowScroll,\n      fixedColumnCount,\n      fixedRowCount,\n      scrollLeft,\n      hideTopRightGridScrollbar,\n    } = props;\n    const {showHorizontalScrollbar, scrollbarSize} = this.state;\n\n    if (!fixedRowCount) {\n      return null;\n    }\n\n    const additionalColumnCount = showHorizontalScrollbar ? 1 : 0,\n      height = this._getTopGridHeight(props),\n      width = this._getRightGridWidth(props),\n      additionalHeight = showHorizontalScrollbar ? scrollbarSize : 0;\n\n    let gridHeight = height,\n      style = this._topRightGridStyle;\n\n    if (hideTopRightGridScrollbar) {\n      gridHeight = height + additionalHeight;\n      style = {\n        ...this._topRightGridStyle,\n        left: 0,\n      };\n    }\n\n    const topRightGrid = (\n      <Grid\n        {...props}\n        cellRenderer={this._cellRendererTopRightGrid}\n        className={this.props.classNameTopRightGrid}\n        columnCount={\n          Math.max(0, columnCount - fixedColumnCount) + additionalColumnCount\n        }\n        columnWidth={this._columnWidthRightGrid}\n        deferredMeasurementCache={this._deferredMeasurementCacheTopRightGrid}\n        height={gridHeight}\n        onScroll={enableFixedRowScroll ? this._onScrollLeft : undefined}\n        ref={this._topRightGridRef}\n        rowCount={fixedRowCount}\n        scrollLeft={scrollLeft}\n        style={style}\n        tabIndex={null}\n        width={width}\n      />\n    );\n\n    if (hideTopRightGridScrollbar) {\n      return (\n        <div\n          className=\"TopRightGrid_ScrollWrapper\"\n          style={{\n            ...this._topRightGridStyle,\n            height,\n            width,\n            overflowX: 'hidden',\n          }}>\n          {topRightGrid}\n        </div>\n      );\n    }\n    return topRightGrid;\n  }\n\n  _rowHeightBottomGrid = ({index}) => {\n    const {fixedRowCount, rowCount, rowHeight} = this.props;\n    const {scrollbarSize, showVerticalScrollbar} = this.state;\n\n    // An extra cell is added to the count\n    // This gives the smaller Grid extra room for offset,\n    // In case the main (bottom right) Grid has a scrollbar\n    // If no scrollbar, the extra space is overflow:hidden anyway\n    if (showVerticalScrollbar && index === rowCount - fixedRowCount) {\n      return scrollbarSize;\n    }\n\n    return typeof rowHeight === 'function'\n      ? rowHeight({index: index + fixedRowCount})\n      : rowHeight;\n  };\n\n  _topLeftGridRef = ref => {\n    this._topLeftGrid = ref;\n  };\n\n  _topRightGridRef = ref => {\n    this._topRightGrid = ref;\n  };\n}\n\npolyfill(MultiGrid);\n\nexport default MultiGrid;\n"
  },
  {
    "path": "source/MultiGrid/index.js",
    "content": "/** @flow */\nimport MultiGrid from './MultiGrid';\n\nexport default MultiGrid;\nexport {MultiGrid};\n"
  },
  {
    "path": "source/ScrollSync/ScrollSync.example.css",
    "content": ".GridRow {\n  position: relative;\n  display: flex;\n  flex-direction: row;\n}\n.GridColumn {\n  display: flex;\n  flex-direction: column;\n  flex: 1 1 auto;\n}\n.LeftSideGridContainer {\n  flex: 0 0 75px;\n  z-index: 10;\n}\n\n.LeftSideGrid {\n  overflow: hidden !important;\n}\n.HeaderGrid {\n  width: 100%;\n  overflow: hidden !important;\n}\n.BodyGrid {\n  width: 100%;\n}\n\n.evenRow {\n}\n.oddRow {\n  background-color: rgba(0, 0, 0, .1);\n}\n\n.cell,\n.headerCell,\n.leftCell {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  text-align: center;\n  padding: 0 .5em;\n}\n.headerCell,\n.leftCell {\n  font-weight: bold;\n}\n"
  },
  {
    "path": "source/ScrollSync/ScrollSync.example.js",
    "content": "/** @flow */\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport AutoSizer from '../AutoSizer';\nimport Grid from '../Grid';\nimport ScrollSync from './ScrollSync';\nimport clsx from 'clsx';\nimport styles from './ScrollSync.example.css';\nimport scrollbarSize from 'dom-helpers/scrollbarSize';\n\nconst LEFT_COLOR_FROM = hexToRgb('#471061');\nconst LEFT_COLOR_TO = hexToRgb('#BC3959');\nconst TOP_COLOR_FROM = hexToRgb('#000000');\nconst TOP_COLOR_TO = hexToRgb('#333333');\n\nexport default class GridExample extends React.PureComponent {\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      columnWidth: 75,\n      columnCount: 50,\n      height: 300,\n      overscanColumnCount: 0,\n      overscanRowCount: 5,\n      rowHeight: 40,\n      rowCount: 100,\n    };\n\n    this._renderBodyCell = this._renderBodyCell.bind(this);\n    this._renderHeaderCell = this._renderHeaderCell.bind(this);\n    this._renderLeftSideCell = this._renderLeftSideCell.bind(this);\n  }\n\n  render() {\n    const {\n      columnCount,\n      columnWidth,\n      height,\n      overscanColumnCount,\n      overscanRowCount,\n      rowHeight,\n      rowCount,\n    } = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"ScrollSync\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/ScrollSync/ScrollSync.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/ScrollSync.md\"\n        />\n\n        <ContentBoxParagraph>\n          High order component that simplifies the process of synchronizing\n          scrolling between two or more virtualized components.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          This example shows two <code>Grid</code>s and one <code>List</code>{' '}\n          configured to mimic a spreadsheet with a fixed header and first\n          column. It also shows how a scroll callback can be used to control UI\n          properties such as background color.\n        </ContentBoxParagraph>\n\n        <ScrollSync>\n          {({\n            clientHeight,\n            clientWidth,\n            onScroll,\n            scrollHeight,\n            scrollLeft,\n            scrollTop,\n            scrollWidth,\n          }) => {\n            const x = scrollLeft / (scrollWidth - clientWidth);\n            const y = scrollTop / (scrollHeight - clientHeight);\n\n            const leftBackgroundColor = mixColors(\n              LEFT_COLOR_FROM,\n              LEFT_COLOR_TO,\n              y,\n            );\n            const leftColor = '#ffffff';\n            const topBackgroundColor = mixColors(\n              TOP_COLOR_FROM,\n              TOP_COLOR_TO,\n              x,\n            );\n            const topColor = '#ffffff';\n            const middleBackgroundColor = mixColors(\n              leftBackgroundColor,\n              topBackgroundColor,\n              0.5,\n            );\n            const middleColor = '#ffffff';\n\n            return (\n              <div className={styles.GridRow}>\n                <div\n                  className={styles.LeftSideGridContainer}\n                  style={{\n                    position: 'absolute',\n                    left: 0,\n                    top: 0,\n                    color: leftColor,\n                    backgroundColor: `rgb(${topBackgroundColor.r},${topBackgroundColor.g},${topBackgroundColor.b})`,\n                  }}>\n                  <Grid\n                    cellRenderer={this._renderLeftHeaderCell}\n                    className={styles.HeaderGrid}\n                    width={columnWidth}\n                    height={rowHeight}\n                    rowHeight={rowHeight}\n                    columnWidth={columnWidth}\n                    rowCount={1}\n                    columnCount={1}\n                  />\n                </div>\n                <div\n                  className={styles.LeftSideGridContainer}\n                  style={{\n                    position: 'absolute',\n                    left: 0,\n                    top: rowHeight,\n                    color: leftColor,\n                    backgroundColor: `rgb(${leftBackgroundColor.r},${leftBackgroundColor.g},${leftBackgroundColor.b})`,\n                  }}>\n                  <Grid\n                    overscanColumnCount={overscanColumnCount}\n                    overscanRowCount={overscanRowCount}\n                    cellRenderer={this._renderLeftSideCell}\n                    columnWidth={columnWidth}\n                    columnCount={1}\n                    className={styles.LeftSideGrid}\n                    height={height - scrollbarSize()}\n                    rowHeight={rowHeight}\n                    rowCount={rowCount}\n                    scrollTop={scrollTop}\n                    width={columnWidth}\n                  />\n                </div>\n                <div className={styles.GridColumn}>\n                  <AutoSizer disableHeight>\n                    {({width}) => (\n                      <div>\n                        <div\n                          style={{\n                            backgroundColor: `rgb(${topBackgroundColor.r},${topBackgroundColor.g},${topBackgroundColor.b})`,\n                            color: topColor,\n                            height: rowHeight,\n                            width: width - scrollbarSize(),\n                          }}>\n                          <Grid\n                            className={styles.HeaderGrid}\n                            columnWidth={columnWidth}\n                            columnCount={columnCount}\n                            height={rowHeight}\n                            overscanColumnCount={overscanColumnCount}\n                            cellRenderer={this._renderHeaderCell}\n                            rowHeight={rowHeight}\n                            rowCount={1}\n                            scrollLeft={scrollLeft}\n                            width={width - scrollbarSize()}\n                          />\n                        </div>\n                        <div\n                          style={{\n                            backgroundColor: `rgb(${middleBackgroundColor.r},${middleBackgroundColor.g},${middleBackgroundColor.b})`,\n                            color: middleColor,\n                            height,\n                            width,\n                          }}>\n                          <Grid\n                            className={styles.BodyGrid}\n                            columnWidth={columnWidth}\n                            columnCount={columnCount}\n                            height={height}\n                            onScroll={onScroll}\n                            overscanColumnCount={overscanColumnCount}\n                            overscanRowCount={overscanRowCount}\n                            cellRenderer={this._renderBodyCell}\n                            rowHeight={rowHeight}\n                            rowCount={rowCount}\n                            width={width}\n                          />\n                        </div>\n                      </div>\n                    )}\n                  </AutoSizer>\n                </div>\n              </div>\n            );\n          }}\n        </ScrollSync>\n      </ContentBox>\n    );\n  }\n\n  _renderBodyCell({columnIndex, key, rowIndex, style}) {\n    if (columnIndex < 1) {\n      return;\n    }\n\n    return this._renderLeftSideCell({columnIndex, key, rowIndex, style});\n  }\n\n  _renderHeaderCell({columnIndex, key, rowIndex, style}) {\n    if (columnIndex < 1) {\n      return;\n    }\n\n    return this._renderLeftHeaderCell({columnIndex, key, rowIndex, style});\n  }\n\n  _renderLeftHeaderCell({columnIndex, key, style}) {\n    return (\n      <div className={styles.headerCell} key={key} style={style}>\n        {`C${columnIndex}`}\n      </div>\n    );\n  }\n\n  _renderLeftSideCell({columnIndex, key, rowIndex, style}) {\n    const rowClass =\n      rowIndex % 2 === 0\n        ? columnIndex % 2 === 0\n          ? styles.evenRow\n          : styles.oddRow\n        : columnIndex % 2 !== 0\n        ? styles.evenRow\n        : styles.oddRow;\n    const classNames = clsx(rowClass, styles.cell);\n\n    return (\n      <div className={classNames} key={key} style={style}>\n        {`R${rowIndex}, C${columnIndex}`}\n      </div>\n    );\n  }\n}\n\nfunction hexToRgb(hex) {\n  const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n  return result\n    ? {\n        r: parseInt(result[1], 16),\n        g: parseInt(result[2], 16),\n        b: parseInt(result[3], 16),\n      }\n    : null;\n}\n\n/**\n * Ported from sass implementation in C\n * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209\n */\nfunction mixColors(color1, color2, amount) {\n  const weight1 = amount;\n  const weight2 = 1 - amount;\n\n  const r = Math.round(weight1 * color1.r + weight2 * color2.r);\n  const g = Math.round(weight1 * color1.g + weight2 * color2.g);\n  const b = Math.round(weight1 * color1.b + weight2 * color2.b);\n\n  return {r, g, b};\n}\n"
  },
  {
    "path": "source/ScrollSync/ScrollSync.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport ScrollSync from './ScrollSync';\n\nfunction ChildComponent({\n  clientHeight,\n  clientWidth,\n  scrollHeight,\n  scrollLeft,\n  scrollTop,\n  scrollWidth,\n}) {\n  return (\n    <div>\n      {`clientHeight:${clientHeight}`}\n      {`clientWidth:${clientWidth}`}\n      {`scrollHeight:${scrollHeight}`}\n      {`scrollLeft:${scrollLeft}`}\n      {`scrollTop:${scrollTop}`}\n      {`scrollWidth:${scrollWidth}`}\n    </div>\n  );\n}\n\ndescribe('ScrollSync', () => {\n  it('should pass through an initial value of 0 for :scrollLeft and :scrollTop', () => {\n    const component = render(\n      <ScrollSync>\n        {({\n          clientHeight,\n          clientWidth,\n          scrollHeight,\n          scrollLeft,\n          scrollTop,\n          scrollWidth,\n        }) => (\n          <ChildComponent\n            clientHeight={clientHeight}\n            clientWidth={clientWidth}\n            scrollHeight={scrollHeight}\n            scrollLeft={scrollLeft}\n            scrollTop={scrollTop}\n            scrollWidth={scrollWidth}\n          />\n        )}\n      </ScrollSync>,\n    );\n    expect(findDOMNode(component).textContent).toContain('clientHeight:0');\n    expect(findDOMNode(component).textContent).toContain('clientWidth:0');\n    expect(findDOMNode(component).textContent).toContain('scrollHeight:0');\n    expect(findDOMNode(component).textContent).toContain('scrollLeft:0');\n    expect(findDOMNode(component).textContent).toContain('scrollTop:0');\n    expect(findDOMNode(component).textContent).toContain('scrollWidth:0');\n  });\n\n  it('should update :scrollLeft and :scrollTop when :onScroll is called', () => {\n    let onScroll;\n    const component = render(\n      <ScrollSync>\n        {params => {\n          onScroll = params.onScroll;\n          return <ChildComponent {...params} />;\n        }}\n      </ScrollSync>,\n    );\n    onScroll({\n      clientHeight: 400,\n      clientWidth: 200,\n      scrollHeight: 1000,\n      scrollLeft: 50,\n      scrollTop: 100,\n      scrollWidth: 500,\n    });\n    expect(findDOMNode(component).textContent).toContain('clientHeight:400');\n    expect(findDOMNode(component).textContent).toContain('clientWidth:200');\n    expect(findDOMNode(component).textContent).toContain('scrollHeight:1000');\n    expect(findDOMNode(component).textContent).toContain('scrollLeft:50');\n    expect(findDOMNode(component).textContent).toContain('scrollTop:100');\n    expect(findDOMNode(component).textContent).toContain('scrollWidth:500');\n  });\n});\n"
  },
  {
    "path": "source/ScrollSync/ScrollSync.js",
    "content": "import PropTypes from 'prop-types';\nimport * as React from 'react';\n\n/**\n * HOC that simplifies the process of synchronizing scrolling between two or more virtualized components.\n */\nexport default class ScrollSync extends React.PureComponent {\n  static propTypes = {\n    /**\n     * Function responsible for rendering 2 or more virtualized components.\n     * This function should implement the following signature:\n     * ({ onScroll, scrollLeft, scrollTop }) => PropTypes.element\n     */\n    children: PropTypes.func.isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    this.state = {\n      clientHeight: 0,\n      clientWidth: 0,\n      scrollHeight: 0,\n      scrollLeft: 0,\n      scrollTop: 0,\n      scrollWidth: 0,\n    };\n\n    this._onScroll = this._onScroll.bind(this);\n  }\n\n  render() {\n    const {children} = this.props;\n    const {\n      clientHeight,\n      clientWidth,\n      scrollHeight,\n      scrollLeft,\n      scrollTop,\n      scrollWidth,\n    } = this.state;\n\n    return children({\n      clientHeight,\n      clientWidth,\n      onScroll: this._onScroll,\n      scrollHeight,\n      scrollLeft,\n      scrollTop,\n      scrollWidth,\n    });\n  }\n\n  _onScroll({\n    clientHeight,\n    clientWidth,\n    scrollHeight,\n    scrollLeft,\n    scrollTop,\n    scrollWidth,\n  }) {\n    this.setState({\n      clientHeight,\n      clientWidth,\n      scrollHeight,\n      scrollLeft,\n      scrollTop,\n      scrollWidth,\n    });\n  }\n}\n"
  },
  {
    "path": "source/ScrollSync/index.js",
    "content": "/** @flow */\nimport ScrollSync from './ScrollSync';\n\nexport default ScrollSync;\nexport {ScrollSync};\n"
  },
  {
    "path": "source/Table/Column.jest.js",
    "content": "import * as React from 'react';\nimport Immutable from 'immutable';\nimport defaultCellDataGetter from './defaultCellDataGetter';\nimport defaultCellRenderer from './defaultCellRenderer';\nimport defaultHeaderRenderer from './defaultHeaderRenderer';\n\ndescribe('Column', () => {\n  const rowData = Immutable.Map({\n    foo: 'Foo',\n    bar: 1,\n  });\n\n  describe('defaultCellDataGetter', () => {\n    it('should return a value for specified attributes', () => {\n      expect(\n        defaultCellDataGetter({\n          dataKey: 'foo',\n          rowData,\n        }),\n      ).toEqual('Foo');\n      expect(\n        defaultCellDataGetter({\n          dataKey: 'bar',\n          rowData,\n        }),\n      ).toEqual(1);\n    });\n\n    it('should return undefined for missing attributes', () => {\n      expect(\n        defaultCellDataGetter({\n          dataKey: 'baz',\n          rowData,\n        }),\n      ).toEqual(undefined);\n    });\n  });\n\n  describe('defaultCellRenderer', () => {\n    it('should render a value for specified attributes', () => {\n      expect(\n        defaultCellRenderer({\n          cellData: 'Foo',\n          dataKey: 'foo',\n          rowData,\n          rowIndex: 0,\n        }),\n      ).toEqual('Foo');\n      expect(\n        defaultCellRenderer({\n          cellData: 1,\n          dataKey: 'bar',\n          rowData,\n          rowIndex: 0,\n        }),\n      ).toEqual('1');\n    });\n\n    it('should render empty string for null or missing attributes', () => {\n      expect(\n        defaultCellRenderer({\n          cellData: null,\n          dataKey: 'baz',\n          rowData,\n          rowIndex: 0,\n        }),\n      ).toEqual('');\n      expect(\n        defaultCellRenderer({\n          cellData: undefined,\n          dataKey: 'baz',\n          rowData,\n          rowIndex: 0,\n        }),\n      ).toEqual('');\n    });\n  });\n\n  describe('defaultHeaderRenderer', () => {\n    it('should render a value for specified attributes', () => {\n      expect(\n        defaultHeaderRenderer({\n          dataKey: 'foo',\n          label: 'squirrel',\n        })[0].props.children,\n      ).toEqual('squirrel');\n\n      const label = <div className=\"rabbit\">Rabbit</div>;\n      expect(\n        defaultHeaderRenderer({\n          dataKey: 'bar',\n          label: label,\n        })[0].props.children,\n      ).toEqual(label);\n    });\n\n    it('should render empty string for null or missing attributes', () => {\n      expect(\n        defaultHeaderRenderer({\n          dataKey: 'foo',\n          label: null,\n        })[0].props.children,\n      ).toBeNull();\n      expect(\n        defaultHeaderRenderer({\n          dataKey: 'bar',\n          label: undefined,\n        })[0].props.children,\n      ).toBeUndefined();\n    });\n  });\n});\n"
  },
  {
    "path": "source/Table/Column.js",
    "content": "/** @flow */\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport defaultHeaderRenderer from './defaultHeaderRenderer';\nimport defaultCellRenderer from './defaultCellRenderer';\nimport defaultCellDataGetter from './defaultCellDataGetter';\nimport SortDirection from './SortDirection';\n\n/**\n * Describes the header and cell contents of a table column.\n */\nexport default class Column extends React.Component {\n  static propTypes = {\n    /** Optional aria-label value to set on the column header */\n    'aria-label': PropTypes.string,\n\n    /**\n     * Callback responsible for returning a cell's data, given its :dataKey\n     * ({ columnData: any, dataKey: string, rowData: any }): any\n     */\n    cellDataGetter: PropTypes.func,\n\n    /**\n     * Callback responsible for rendering a cell's contents.\n     * ({ cellData: any, columnData: any, dataKey: string, rowData: any, rowIndex: number }): node\n     */\n    cellRenderer: PropTypes.func,\n\n    /** Optional CSS class to apply to cell */\n    className: PropTypes.string,\n\n    /** Optional additional data passed to this column's :cellDataGetter */\n    columnData: PropTypes.object,\n\n    /** Uniquely identifies the row-data attribute corresponding to this cell */\n    dataKey: PropTypes.any.isRequired,\n\n    /** Optional direction to be used when clicked the first time */\n    defaultSortDirection: PropTypes.oneOf([\n      SortDirection.ASC,\n      SortDirection.DESC,\n    ]),\n\n    /** If sort is enabled for the table at large, disable it for this column */\n    disableSort: PropTypes.bool,\n\n    /** Flex grow style; defaults to 0 */\n    flexGrow: PropTypes.number,\n\n    /** Flex shrink style; defaults to 1 */\n    flexShrink: PropTypes.number,\n\n    /** Optional CSS class to apply to this column's header */\n    headerClassName: PropTypes.string,\n\n    /**\n     * Optional callback responsible for rendering a column header contents.\n     * ({ columnData: object, dataKey: string, disableSort: boolean, label: node, sortBy: string, sortDirection: string }): PropTypes.node\n     */\n    headerRenderer: PropTypes.func.isRequired,\n\n    /** Optional inline style to apply to this column's header */\n    headerStyle: PropTypes.object,\n\n    /** Optional id to set on the column header */\n    id: PropTypes.string,\n\n    /** Header label for this column */\n    label: PropTypes.node,\n\n    /** Maximum width of column; this property will only be used if :flexGrow is > 0. */\n    maxWidth: PropTypes.number,\n\n    /** Minimum width of column. */\n    minWidth: PropTypes.number,\n\n    /** Optional inline style to apply to cell */\n    style: PropTypes.object,\n\n    /** Flex basis (width) for this column; This value can grow or shrink based on :flexGrow and :flexShrink properties. */\n    width: PropTypes.number.isRequired,\n  };\n\n  static defaultProps = {\n    cellDataGetter: defaultCellDataGetter,\n    cellRenderer: defaultCellRenderer,\n    defaultSortDirection: SortDirection.ASC,\n    flexGrow: 0,\n    flexShrink: 1,\n    headerRenderer: defaultHeaderRenderer,\n    style: {},\n  };\n}\n"
  },
  {
    "path": "source/Table/SortDirection.js",
    "content": "const SortDirection = {\n  /**\n   * Sort items in ascending order.\n   * This means arranging from the lowest value to the highest (e.g. a-z, 0-9).\n   */\n  ASC: 'ASC',\n\n  /**\n   * Sort items in descending order.\n   * This means arranging from the highest value to the lowest (e.g. z-a, 9-0).\n   */\n  DESC: 'DESC',\n};\n\nexport default SortDirection;\n"
  },
  {
    "path": "source/Table/SortIndicator.js",
    "content": "import clsx from 'clsx';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport SortDirection from './SortDirection';\n\n/**\n * Displayed beside a header to indicate that a Table is currently sorted by this column.\n */\nexport default function SortIndicator({sortDirection}) {\n  const classNames = clsx('ReactVirtualized__Table__sortableHeaderIcon', {\n    'ReactVirtualized__Table__sortableHeaderIcon--ASC':\n      sortDirection === SortDirection.ASC,\n    'ReactVirtualized__Table__sortableHeaderIcon--DESC':\n      sortDirection === SortDirection.DESC,\n  });\n\n  return (\n    <svg className={classNames} width={18} height={18} viewBox=\"0 0 24 24\">\n      {sortDirection === SortDirection.ASC ? (\n        <path d=\"M7 14l5-5 5 5z\" />\n      ) : (\n        <path d=\"M7 10l5 5 5-5z\" />\n      )}\n      <path d=\"M0 0h24v24H0z\" fill=\"none\" />\n    </svg>\n  );\n}\n\nSortIndicator.propTypes = {\n  sortDirection: PropTypes.oneOf([SortDirection.ASC, SortDirection.DESC]),\n};\n"
  },
  {
    "path": "source/Table/Table.example.css",
    "content": ".Table {\n  width: 100%;\n  margin-top: 15px;\n}\n.headerRow,\n.evenRow,\n.oddRow {\n  border-bottom: 1px solid #e0e0e0;\n}\n.oddRow {\n  background-color: #fafafa;\n}\n.headerColumn {\n  text-transform: none;\n}\n.exampleColumn {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.checkboxLabel {\n  margin-left: .5rem;\n}\n.checkboxLabel:first-of-type {\n  margin-left: 0;\n}\n\n.noRows {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 1em;\n  color: #bdbdbd;\n}\n"
  },
  {
    "path": "source/Table/Table.example.js",
    "content": "/** @flow */\nimport Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\nimport AutoSizer from '../AutoSizer';\nimport Column from './Column';\nimport Table from './Table';\nimport SortDirection from './SortDirection';\nimport SortIndicator from './SortIndicator';\nimport styles from './Table.example.css';\n\nexport default class TableExample extends React.PureComponent {\n  static contextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n  };\n\n  constructor(props, context) {\n    super(props, context);\n\n    const sortBy = 'index';\n    const sortDirection = SortDirection.ASC;\n    const sortedList = this._sortList({sortBy, sortDirection});\n\n    this.state = {\n      disableHeader: false,\n      headerHeight: 30,\n      height: 270,\n      hideIndexRow: false,\n      overscanRowCount: 10,\n      rowHeight: 40,\n      rowCount: 1000,\n      scrollToIndex: undefined,\n      sortBy,\n      sortDirection,\n      sortedList,\n      useDynamicRowHeight: false,\n    };\n\n    this._getRowHeight = this._getRowHeight.bind(this);\n    this._headerRenderer = this._headerRenderer.bind(this);\n    this._noRowsRenderer = this._noRowsRenderer.bind(this);\n    this._onRowCountChange = this._onRowCountChange.bind(this);\n    this._onScrollToRowChange = this._onScrollToRowChange.bind(this);\n    this._rowClassName = this._rowClassName.bind(this);\n    this._sort = this._sort.bind(this);\n  }\n\n  render() {\n    const {\n      disableHeader,\n      headerHeight,\n      height,\n      hideIndexRow,\n      overscanRowCount,\n      rowHeight,\n      rowCount,\n      scrollToIndex,\n      sortBy,\n      sortDirection,\n      sortedList,\n      useDynamicRowHeight,\n    } = this.state;\n\n    const rowGetter = ({index}) => this._getDatum(sortedList, index);\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"Table\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/Table/Table.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/Table.md\"\n        />\n\n        <ContentBoxParagraph>\n          The table layout below is created with flexboxes. This allows it to\n          have a fixed header and scrollable body content. It also makes use of{' '}\n          <code>Grid</code> for windowing table content so that large lists are\n          rendered efficiently. Adjust its configurable properties below to see\n          how it reacts.\n        </ContentBoxParagraph>\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Use dynamic row heights?\"\n              checked={useDynamicRowHeight}\n              className={styles.checkbox}\n              type=\"checkbox\"\n              onChange={event =>\n                this._updateUseDynamicRowHeight(event.target.checked)\n              }\n            />\n            Use dynamic row heights?\n          </label>\n\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Hide index?\"\n              checked={hideIndexRow}\n              className={styles.checkbox}\n              type=\"checkbox\"\n              onChange={event =>\n                this.setState({hideIndexRow: event.target.checked})\n              }\n            />\n            Hide index?\n          </label>\n\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Hide header?\"\n              checked={disableHeader}\n              className={styles.checkbox}\n              type=\"checkbox\"\n              onChange={event =>\n                this.setState({disableHeader: event.target.checked})\n              }\n            />\n            Hide header?\n          </label>\n        </ContentBoxParagraph>\n\n        <InputRow>\n          <LabeledInput\n            label=\"Num rows\"\n            name=\"rowCount\"\n            onChange={this._onRowCountChange}\n            value={rowCount}\n          />\n          <LabeledInput\n            label=\"Scroll to\"\n            name=\"onScrollToRow\"\n            placeholder=\"Index...\"\n            onChange={this._onScrollToRowChange}\n            value={scrollToIndex || ''}\n          />\n          <LabeledInput\n            label=\"List height\"\n            name=\"height\"\n            onChange={event =>\n              this.setState({height: parseInt(event.target.value, 10) || 1})\n            }\n            value={height}\n          />\n          <LabeledInput\n            disabled={useDynamicRowHeight}\n            label=\"Row height\"\n            name=\"rowHeight\"\n            onChange={event =>\n              this.setState({\n                rowHeight: parseInt(event.target.value, 10) || 1,\n              })\n            }\n            value={rowHeight}\n          />\n          <LabeledInput\n            label=\"Header height\"\n            name=\"headerHeight\"\n            onChange={event =>\n              this.setState({\n                headerHeight: parseInt(event.target.value, 10) || 1,\n              })\n            }\n            value={headerHeight}\n          />\n          <LabeledInput\n            label=\"Overscan\"\n            name=\"overscanRowCount\"\n            onChange={event =>\n              this.setState({\n                overscanRowCount: parseInt(event.target.value, 10) || 0,\n              })\n            }\n            value={overscanRowCount}\n          />\n        </InputRow>\n\n        <div>\n          <AutoSizer disableHeight>\n            {({width}) => (\n              <Table\n                ref=\"Table\"\n                disableHeader={disableHeader}\n                headerClassName={styles.headerColumn}\n                headerHeight={headerHeight}\n                height={height}\n                noRowsRenderer={this._noRowsRenderer}\n                overscanRowCount={overscanRowCount}\n                rowClassName={this._rowClassName}\n                rowHeight={useDynamicRowHeight ? this._getRowHeight : rowHeight}\n                rowGetter={rowGetter}\n                rowCount={rowCount}\n                scrollToIndex={scrollToIndex}\n                sort={this._sort}\n                sortBy={sortBy}\n                sortDirection={sortDirection}\n                width={width}>\n                {!hideIndexRow && (\n                  <Column\n                    label=\"Index\"\n                    cellDataGetter={({rowData}) => rowData.index}\n                    dataKey=\"index\"\n                    disableSort={!this._isSortEnabled()}\n                    width={60}\n                  />\n                )}\n                <Column\n                  dataKey=\"name\"\n                  disableSort={!this._isSortEnabled()}\n                  headerRenderer={this._headerRenderer}\n                  width={90}\n                />\n                <Column\n                  width={210}\n                  disableSort\n                  label=\"The description label is really long so that it will be truncated\"\n                  dataKey=\"random\"\n                  className={styles.exampleColumn}\n                  cellRenderer={({cellData}) => cellData}\n                  flexGrow={1}\n                />\n              </Table>\n            )}\n          </AutoSizer>\n        </div>\n      </ContentBox>\n    );\n  }\n\n  _getDatum(list, index) {\n    return list.get(index % list.size);\n  }\n\n  _getRowHeight({index}) {\n    const {list} = this.context;\n\n    return this._getDatum(list, index).size;\n  }\n\n  _headerRenderer({dataKey, sortBy, sortDirection}) {\n    return (\n      <div>\n        Full Name\n        {sortBy === dataKey && <SortIndicator sortDirection={sortDirection} />}\n      </div>\n    );\n  }\n\n  _isSortEnabled() {\n    const {list} = this.context;\n    const {rowCount} = this.state;\n\n    return rowCount <= list.size;\n  }\n\n  _noRowsRenderer() {\n    return <div className={styles.noRows}>No rows</div>;\n  }\n\n  _onRowCountChange(event) {\n    const rowCount = parseInt(event.target.value, 10) || 0;\n\n    this.setState({rowCount});\n  }\n\n  _onScrollToRowChange(event) {\n    const {rowCount} = this.state;\n    let scrollToIndex = Math.min(\n      rowCount - 1,\n      parseInt(event.target.value, 10),\n    );\n\n    if (isNaN(scrollToIndex)) {\n      scrollToIndex = undefined;\n    }\n\n    this.setState({scrollToIndex});\n  }\n\n  _rowClassName({index}) {\n    if (index < 0) {\n      return styles.headerRow;\n    } else {\n      return index % 2 === 0 ? styles.evenRow : styles.oddRow;\n    }\n  }\n\n  _sort({sortBy, sortDirection}) {\n    const sortedList = this._sortList({sortBy, sortDirection});\n\n    this.setState({sortBy, sortDirection, sortedList});\n  }\n\n  _sortList({sortBy, sortDirection}) {\n    const {list} = this.context;\n\n    return list\n      .sortBy(item => item[sortBy])\n      .update(list =>\n        sortDirection === SortDirection.DESC ? list.reverse() : list,\n      );\n  }\n\n  _updateUseDynamicRowHeight(value) {\n    this.setState({\n      useDynamicRowHeight: value,\n    });\n  }\n}\n"
  },
  {
    "path": "source/Table/Table.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport {Simulate} from 'react-dom/test-utils';\nimport Immutable from 'immutable';\nimport Column from './Column';\nimport Table from './Table';\nimport SortDirection from './SortDirection';\n\ndescribe('Table', () => {\n  const array = [];\n  for (var i = 0; i < 100; i++) {\n    array.push({\n      id: i,\n      name: `Name ${i}`,\n      email: `user-${i}@treasure-data.com`,\n    });\n  }\n  const list = Immutable.fromJS(array);\n\n  // Works with an Immutable List of Maps\n  function immutableRowGetter({index}) {\n    return list.get(index);\n  }\n\n  // Works with an Array of Objects\n  function vanillaRowGetter({index}) {\n    return array[index];\n  }\n\n  // Override default behavior of overscanning by at least 1 (for accessibility)\n  // Because it makes for simple tests below\n  function overscanIndicesGetter({startIndex, stopIndex}) {\n    return {\n      overscanStartIndex: startIndex,\n      overscanStopIndex: stopIndex,\n    };\n  }\n\n  function getMarkup({\n    cellDataGetter,\n    cellRenderer,\n    columnData = {data: 123},\n    columnID,\n    columnStyle,\n    columnHeaderStyle,\n    disableSort = false,\n    headerRenderer,\n    maxWidth,\n    minWidth,\n    defaultSortDirection,\n    label,\n    ...flexTableProps\n  } = {}) {\n    return (\n      <Table\n        headerHeight={20}\n        height={100}\n        overscanRowCount={0}\n        overscanIndicesGetter={overscanIndicesGetter}\n        rowCount={list.size}\n        rowGetter={immutableRowGetter}\n        rowHeight={10}\n        width={100}\n        {...flexTableProps}>\n        <Column\n          label={label || 'Name'}\n          dataKey=\"name\"\n          columnData={columnData}\n          width={50}\n          cellRenderer={cellRenderer}\n          cellDataGetter={cellDataGetter}\n          headerRenderer={headerRenderer}\n          disableSort={disableSort}\n          defaultSortDirection={defaultSortDirection}\n          style={columnStyle}\n          headerStyle={columnHeaderStyle}\n          id={columnID}\n        />\n        <Column\n          label=\"Email\"\n          dataKey=\"email\"\n          maxWidth={maxWidth}\n          minWidth={minWidth}\n          width={50}\n        />\n        {false}\n        {true}\n        {null}\n        {undefined}\n      </Table>\n    );\n  }\n\n  beforeEach(() => jest.resetModules());\n\n  describe('children', () => {\n    it('should accept Column children', () => {\n      const children = [<Column dataKey=\"foo\" width={100} />];\n      const result = Table.propTypes.children({children}, 'children', 'Table');\n      expect(result instanceof Error).toEqual(false);\n    });\n\n    it('should accept subclasses of Column as children', () => {\n      class AnotherColumn extends Column {}\n\n      const children = [<AnotherColumn dataKey=\"foo\" width={100} />];\n      const result = Table.propTypes.children({children}, 'children', 'Table');\n      expect(result instanceof Error).toEqual(false);\n    });\n\n    it('should not accept non-Column children', () => {\n      const children = [<div />];\n      const result = Table.propTypes.children({children}, 'children', 'Table');\n      expect(result instanceof Error).toEqual(true);\n    });\n\n    it('should accept falsy children to allow easier dynamic showing/hiding of columns', () => {\n      const children = [false, <Column dataKey=\"foo\" width={100} />, null];\n      const result = Table.propTypes.children({children}, 'children', 'Table');\n      expect(result instanceof Error).toEqual(false);\n    });\n  });\n\n  describe('height', () => {\n    it('should subtract header row height from the inner Grid height if headers are enabled', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            headerHeight: 10,\n            overscanRowCount: 0,\n            rowHeight: 20,\n            height: 50,\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      expect(rows.length).toEqual(2);\n    });\n\n    it('should not subtract header row height from the inner Grid height if headers are disabled', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            disableHeader: true,\n            headerHeight: 10,\n            overscanRowCount: 0,\n            rowHeight: 20,\n            height: 50,\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      expect(rows.length).toEqual(3);\n    });\n  });\n\n  describe('initial rendering', () => {\n    // Ensure that both Immutable Lists of Maps and Arrays of Objects are supported\n    const useImmutable = [true, false];\n    useImmutable.forEach(useImmutable => {\n      it('should render the correct number of rows', () => {\n        const rendered = findDOMNode(\n          render(\n            getMarkup({\n              rowGetter: useImmutable ? immutableRowGetter : vanillaRowGetter,\n            }),\n          ),\n        );\n        // 100px height should fit 1 header (20px) and 8 rows (10px each) -\n        expect(\n          rendered.querySelectorAll('.ReactVirtualized__Table__headerRow')\n            .length,\n        ).toEqual(1);\n        expect(\n          rendered.querySelectorAll('.ReactVirtualized__Table__row').length,\n        ).toEqual(8);\n      });\n\n      it('should render the expected headers', () => {\n        const rendered = findDOMNode(\n          render(\n            getMarkup({\n              rowGetter: useImmutable ? immutableRowGetter : vanillaRowGetter,\n            }),\n          ),\n        );\n        const columns = rendered.querySelectorAll(\n          '.ReactVirtualized__Table__headerColumn',\n        );\n        expect(columns.length).toEqual(2);\n        expect(columns[0].textContent).toEqual('Name');\n        expect(columns[1].textContent).toEqual('Email');\n      });\n\n      it('should render the expected rows and columns', () => {\n        const rendered = findDOMNode(\n          render(\n            getMarkup({\n              rowGetter: useImmutable ? immutableRowGetter : vanillaRowGetter,\n              headerHeight: 10,\n              rowHeight: 20,\n              height: 50,\n            }),\n          ),\n        );\n        const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n        expect(rows.length).toEqual(2);\n        Array.from(rows).forEach((row, index) => {\n          let rowData = list.get(index);\n          let columns = row.querySelectorAll(\n            '.ReactVirtualized__Table__rowColumn',\n          );\n          expect(columns.length).toEqual(2);\n          expect(columns[0].textContent).toEqual(rowData.get('name'));\n          expect(columns[1].textContent).toEqual(rowData.get('email'));\n        });\n      });\n    });\n\n    it('should support a :rowHeight function', () => {\n      const rowHeight = ({index}) => 10 + index * 10;\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            rowHeight,\n            rowCount: 3,\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      Array.from(rows).forEach((row, index) => {\n        expect(Number.parseInt(row.style.height, 10)).toEqual(\n          rowHeight({index}),\n        );\n      });\n    });\n\n    it('should support :minWidth and :maxWidth values for a column', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            maxWidth: 75,\n            minWidth: 25,\n            rowCount: 1,\n          }),\n        ),\n      );\n      const columns = rendered.querySelectorAll(\n        '.ReactVirtualized__Table__rowColumn',\n      );\n      const emailColumn = columns[1];\n      expect(Number.parseInt(emailColumn.style.maxWidth, 10)).toEqual(75);\n      expect(Number.parseInt(emailColumn.style.minWidth, 10)).toEqual(25);\n    });\n  });\n\n  describe('measureAllRows', () => {\n    it('should measure any unmeasured rows', () => {\n      const rendered = render(\n        getMarkup({\n          estimatedRowSize: 15,\n          height: 0,\n          rowCount: 10,\n          rowHeight: () => 20,\n          width: 0,\n        }),\n      );\n      expect(\n        rendered.Grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize(),\n      ).toEqual(150);\n      rendered.measureAllRows();\n      expect(\n        rendered.Grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize(),\n      ).toEqual(200);\n    });\n  });\n\n  describe('recomputeRowHeights', () => {\n    it('should recompute row heights and other values when called', () => {\n      const indices = [];\n      const rowHeight = ({index}) => {\n        indices.push(index);\n        return 10;\n      };\n      const component = render(\n        getMarkup({\n          rowHeight,\n          rowCount: 50,\n        }),\n      );\n\n      indices.splice(0);\n      component.recomputeRowHeights();\n\n      // Only the rows required to fill the current viewport will be rendered\n      expect(indices[0]).toEqual(0);\n      expect(indices[indices.length - 1]).toEqual(7);\n\n      indices.splice(0);\n      component.recomputeRowHeights(4);\n\n      expect(indices[0]).toEqual(4);\n      expect(indices[indices.length - 1]).toEqual(7);\n    });\n  });\n\n  describe('forceUpdateGrid', () => {\n    it('should refresh inner Grid content when called', () => {\n      let marker = 'a';\n      function cellRenderer({rowIndex}) {\n        return `${rowIndex}${marker}`;\n      }\n      const component = render(getMarkup({cellRenderer}));\n      const node = findDOMNode(component);\n      expect(node.textContent).toContain('1a');\n      marker = 'b';\n      component.forceUpdateGrid();\n      expect(node.textContent).toContain('1b');\n    });\n  });\n\n  describe('custom getter functions', () => {\n    it('should use a custom cellDataGetter if specified', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellDataGetter: ({dataKey, rowData}) =>\n              `Custom ${dataKey} for row ${rowData.get('id')}`,\n          }),\n        ),\n      );\n      const nameColumns = rendered.querySelectorAll(\n        '.ReactVirtualized__Table__rowColumn:first-of-type',\n      );\n      Array.from(nameColumns).forEach((nameColumn, index) => {\n        expect(nameColumn.textContent).toEqual(`Custom name for row ${index}`);\n      });\n    });\n\n    it('should use a custom cellRenderer if specified', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellRenderer: ({cellData}) => `Custom ${cellData}`,\n          }),\n        ),\n      );\n      const nameColumns = rendered.querySelectorAll(\n        '.ReactVirtualized__Table__rowColumn:first-of-type',\n      );\n      Array.from(nameColumns).forEach((nameColumn, index) => {\n        let rowData = list.get(index);\n        expect(nameColumn.textContent).toEqual(`Custom ${rowData.get('name')}`);\n      });\n    });\n\n    it('should set the rendered cell content as the cell :title if it is a string', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellRenderer: () => 'Custom',\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__rowColumn:first-of-type',\n      );\n      expect(nameColumn.getAttribute('title')).toContain('Custom');\n    });\n\n    it('should not set a cell :title if the rendered cell content is not a string', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            cellRenderer: () => <div>Custom</div>,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__rowColumn:first-of-type',\n      );\n      expect(nameColumn.getAttribute('title')).toEqual(null);\n    });\n\n    it('should set the rendered header label as header :title if it is a string', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            label: 'Custom',\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerTruncatedText:first-of-type',\n      );\n      expect(nameColumn.getAttribute('title')).toContain('Custom');\n    });\n\n    it('should not set a header :title if the rendered header label is not a string', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            label: <div>Custom</div>,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerTruncatedText:first-of-type',\n      );\n      expect(nameColumn.getAttribute('title')).toEqual(null);\n    });\n  });\n\n  describe('sorting', () => {\n    it('should not render sort indicators if no sort function is provided', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const nameColumn = rendered.querySelectorAll(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      expect(nameColumn.className || '').not.toContain(\n        'ReactVirtualized__Table__sortableHeaderColumn',\n      );\n    });\n\n    it('should not render sort indicators for non-sortable columns', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            disableSort: true,\n            sort: () => {},\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelectorAll(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      expect(nameColumn.className || '').not.toContain(\n        'ReactVirtualized__Table__sortableHeaderColumn',\n      );\n      expect(\n        rendered.querySelectorAll(\n          '.ReactVirtualized__Table__sortableHeaderColumn',\n        ).length,\n      ).toEqual(1); // Email only\n    });\n\n    it('should render sortable column headers as sortable', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            sort: () => {},\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      expect(nameColumn.className).toContain(\n        'ReactVirtualized__Table__sortableHeaderColumn',\n      );\n      expect(\n        rendered.querySelectorAll(\n          '.ReactVirtualized__Table__sortableHeaderColumn',\n        ).length,\n      ).toEqual(2); // Email and Name\n    });\n\n    it('should render the correct sort indicator by the current sort-by column', () => {\n      const sortDirections = [SortDirection.ASC, SortDirection.DESC];\n      sortDirections.forEach(sortDirection => {\n        const rendered = findDOMNode(\n          render(\n            getMarkup({\n              sort: () => {},\n              sortBy: 'name',\n              sortDirection,\n            }),\n          ),\n        );\n        const nameColumn = rendered.querySelector(\n          '.ReactVirtualized__Table__headerColumn:first-of-type',\n        );\n\n        expect(\n          nameColumn.querySelector(\n            '.ReactVirtualized__Table__sortableHeaderIcon',\n          ),\n        ).not.toEqual(null);\n        expect(\n          nameColumn.querySelector(\n            `.ReactVirtualized__Table__sortableHeaderIcon--${sortDirection}`,\n          ),\n        ).not.toEqual(null);\n      });\n    });\n\n    it('should call sort with the correct arguments when the current sort-by column header is clicked', () => {\n      const sortDirections = [SortDirection.ASC, SortDirection.DESC];\n      sortDirections.forEach(sortDirection => {\n        const sortCalls = [];\n        const rendered = findDOMNode(\n          render(\n            getMarkup({\n              sort: ({sortBy, sortDirection}) =>\n                sortCalls.push({sortBy, sortDirection}),\n              sortBy: 'name',\n              sortDirection,\n            }),\n          ),\n        );\n        const nameColumn = rendered.querySelector(\n          '.ReactVirtualized__Table__headerColumn:first-of-type',\n        );\n\n        Simulate.click(nameColumn);\n        expect(sortCalls.length).toEqual(1);\n\n        const {sortBy, sortDirection: newSortDirection} = sortCalls[0];\n        const expectedSortDirection =\n          sortDirection === SortDirection.ASC\n            ? SortDirection.DESC\n            : SortDirection.ASC;\n        expect(sortBy).toEqual('name');\n        expect(newSortDirection).toEqual(expectedSortDirection);\n      });\n    });\n\n    it('should call sort with the correct arguments when a new sort-by column header is clicked', () => {\n      const sortCalls = [];\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            sort: ({sortBy, sortDirection}) =>\n              sortCalls.push({sortBy, sortDirection}),\n            sortBy: 'email',\n            sortDirection: SortDirection.ASC,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      Simulate.click(nameColumn);\n      expect(sortCalls.length).toEqual(1);\n\n      const {sortBy, sortDirection} = sortCalls[0];\n      expect(sortBy).toEqual('name');\n      expect(sortDirection).toEqual(SortDirection.ASC);\n    });\n\n    it('should call sort when a column header is activated via ENTER or SPACE key', () => {\n      const sortCalls = [];\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            sort: ({sortBy, sortDirection}) =>\n              sortCalls.push({sortBy, sortDirection}),\n            sortBy: 'name',\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n      expect(sortCalls.length).toEqual(0);\n      Simulate.keyDown(nameColumn, {key: ' '});\n      expect(sortCalls.length).toEqual(1);\n      Simulate.keyDown(nameColumn, {key: 'Enter'});\n      expect(sortCalls.length).toEqual(2);\n      Simulate.keyDown(nameColumn, {key: 'F'});\n      expect(sortCalls.length).toEqual(2);\n    });\n\n    it('should honor the default sort order on first click of the column', () => {\n      const sortDirections = [SortDirection.ASC, SortDirection.DESC];\n      sortDirections.forEach(sortDirection => {\n        const sortCalls = [];\n        const rendered = findDOMNode(\n          render(\n            getMarkup({\n              sort: ({sortBy, sortDirection}) =>\n                sortCalls.push({sortBy, sortDirection}),\n              defaultSortDirection: sortDirection,\n            }),\n          ),\n        );\n        const nameColumn = rendered.querySelector(\n          '.ReactVirtualized__Table__headerColumn:first-of-type',\n        );\n\n        Simulate.click(nameColumn);\n        expect(sortCalls.length).toEqual(1);\n\n        const {sortBy, sortDirection: newSortDirection} = sortCalls[0];\n        expect(sortBy).toEqual('name');\n        expect(newSortDirection).toEqual(sortDirection);\n      });\n    });\n  });\n\n  describe('headerRowRenderer', () => {\n    it('should render a custom header row if one is provided', () => {\n      const headerRowRenderer = jest.fn().mockReturnValue(<div>foo bar</div>);\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            headerHeight: 33,\n            headerRowRenderer,\n            rowClassName: 'someRowClass',\n          }),\n        ),\n      );\n      expect(rendered.textContent).toContain('foo bar');\n      expect(headerRowRenderer).toHaveBeenCalled();\n      const params = headerRowRenderer.mock.calls[0][0];\n      expect(params.className).toContain('someRowClass');\n      expect(params.columns).toHaveLength(2);\n      expect(params.style.height).toBe(33);\n    });\n  });\n\n  describe('headerRenderer', () => {\n    it('should render a custom header if one is provided', () => {\n      const columnData = {foo: 'foo', bar: 'bar'};\n      const headerRendererCalls = [];\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnData,\n            headerRenderer: params => {\n              headerRendererCalls.push(params);\n              return 'custom header';\n            },\n            sortBy: 'name',\n            sortDirection: SortDirection.ASC,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      expect(nameColumn.textContent).toContain('custom header');\n      expect(headerRendererCalls.length).toBeTruthy();\n\n      const headerRendererCall = headerRendererCalls[0];\n      expect(headerRendererCall.columnData).toEqual(columnData);\n      expect(headerRendererCall.dataKey).toEqual('name');\n      expect(headerRendererCall.disableSort).toEqual(false);\n      expect(headerRendererCall.label).toEqual('Name');\n      expect(headerRendererCall.sortBy).toEqual('name');\n      expect(headerRendererCall.sortDirection).toEqual(SortDirection.ASC);\n    });\n\n    it('should honor sort for custom headers', () => {\n      const sortCalls = [];\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            headerRenderer: () => 'custom header',\n            sort: ({sortBy, sortDirection}) =>\n              sortCalls.push([sortBy, sortDirection]),\n            sortBy: 'name',\n            sortDirection: SortDirection.ASC,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      Simulate.click(nameColumn);\n\n      expect(sortCalls.length).toEqual(1);\n      const sortCall = sortCalls[0];\n      expect(sortCall[0]).toEqual('name');\n      expect(sortCall[1]).toEqual(SortDirection.DESC);\n    });\n\n    it('should honor :onHeaderClick for custom header', () => {\n      const columnData = {foo: 'foo', bar: 'bar'};\n      const onHeaderClick = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnData,\n            headerRenderer: () => 'custom header',\n            onHeaderClick,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      Simulate.click(nameColumn);\n\n      expect(onHeaderClick).toHaveBeenCalledTimes(1);\n      const params = onHeaderClick.mock.calls[0][0];\n      expect(params.dataKey).toEqual('name');\n      expect(params.columnData).toEqual(columnData);\n      expect(params.event.type).toEqual('click');\n    });\n  });\n\n  describe('noRowsRenderer', () => {\n    it('should call :noRowsRenderer if :rowCount is 0', () => {\n      const rendered = render(\n        getMarkup({\n          noRowsRenderer: () => <div>No rows!</div>,\n          rowCount: 0,\n        }),\n      );\n      const bodyDOMNode = findDOMNode(rendered.Grid);\n      expect(bodyDOMNode.textContent).toEqual('No rows!');\n    });\n\n    it('should render an empty body if :rowCount is 0 and there is no :noRowsRenderer', () => {\n      const rendered = render(\n        getMarkup({\n          rowCount: 0,\n        }),\n      );\n      const bodyDOMNode = findDOMNode(rendered.Grid);\n      expect(bodyDOMNode.textContent).toEqual('');\n    });\n  });\n\n  describe('onColumnClick', () => {\n    it('should call :onColumnClick with the correct arguments when a column is clicked', () => {\n      const onColumnClick = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            onColumnClick,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__rowColumn:first-of-type',\n      );\n\n      Simulate.click(nameColumn);\n\n      expect(onColumnClick).toHaveBeenCalledTimes(1);\n      const params = onColumnClick.mock.calls[0][0];\n      expect(params.dataKey).toEqual('name');\n      expect(params.columnData.data).toEqual(123);\n      expect(params.event.type).toEqual('click');\n    });\n  });\n\n  describe('onHeaderClick', () => {\n    it('should call :onHeaderClick with the correct arguments when a column header is clicked and sorting is disabled', () => {\n      const onHeaderClick = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            disableSort: true,\n            onHeaderClick,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      Simulate.click(nameColumn);\n\n      expect(onHeaderClick).toHaveBeenCalledTimes(1);\n      const params = onHeaderClick.mock.calls[0][0];\n      expect(params.dataKey).toEqual('name');\n      expect(params.columnData.data).toEqual(123);\n      expect(params.event.type).toEqual('click');\n    });\n\n    it('should call :onHeaderClick with the correct arguments when a column header is clicked and sorting is enabled', () => {\n      const onHeaderClick = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            disableSort: false,\n            onHeaderClick,\n          }),\n        ),\n      );\n      const nameColumn = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn:first-of-type',\n      );\n\n      Simulate.click(nameColumn);\n\n      expect(onHeaderClick).toHaveBeenCalledTimes(1);\n      const params = onHeaderClick.mock.calls[0][0];\n      expect(params.dataKey).toEqual('name');\n      expect(params.columnData.data).toEqual(123);\n      expect(params.event.type).toEqual('click');\n    });\n  });\n\n  describe('onRowClick', () => {\n    it('should call :onRowClick with the correct :rowIndex when a row is clicked', () => {\n      const onRowClick = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            onRowClick,\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      Simulate.click(rows[0]);\n      Simulate.click(rows[3]);\n      expect(onRowClick).toHaveBeenCalledTimes(2);\n      expect(onRowClick.mock.calls.map(call => call[0].index)).toEqual([0, 3]);\n    });\n  });\n\n  describe('onRowDoubleClick', () => {\n    it('should call :onRowDoubleClick with the correct :rowIndex when a row is clicked', () => {\n      const onRowDoubleClick = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            onRowDoubleClick,\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      Simulate.doubleClick(rows[0]);\n      Simulate.doubleClick(rows[3]);\n      expect(onRowDoubleClick).toHaveBeenCalledTimes(2);\n      expect(onRowDoubleClick.mock.calls.map(call => call[0].index)).toEqual([\n        0,\n        3,\n      ]);\n    });\n  });\n\n  describe('onRowRightClick', () => {\n    it('should call :onRowRightClick with the correct :rowIndex when a row is right-clicked', () => {\n      const onRowRightClick = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            onRowRightClick,\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      Simulate.contextMenu(rows[0]);\n      Simulate.contextMenu(rows[3]);\n      expect(onRowRightClick).toHaveBeenCalledTimes(2);\n      expect(onRowRightClick.mock.calls.map(call => call[0].index)).toEqual([\n        0,\n        3,\n      ]);\n    });\n  });\n\n  describe('onRowMouseOver/Out', () => {\n    it('should call :onRowMouseOver and :onRowMouseOut with the correct :rowIndex when the mouse is moved over rows', () => {\n      let onRowMouseOver = jest.fn();\n      let onRowMouseOut = jest.fn();\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            onRowMouseOver,\n            onRowMouseOut,\n          }),\n        ),\n      );\n\n      const simulateMouseOver = (from, to) => {\n        Simulate.mouseOut(from, {relatedTarget: to});\n        Simulate.mouseOver(to, {relatedTarget: from});\n      };\n\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n\n      simulateMouseOver(rows[0], rows[1]);\n      simulateMouseOver(rows[1], rows[2]);\n      simulateMouseOver(rows[2], rows[3]);\n\n      expect(onRowMouseOver).toHaveBeenCalled();\n      expect(onRowMouseOut).toHaveBeenCalled();\n      expect(onRowMouseOver.mock.calls.map(call => call[0].index)).toEqual([\n        1,\n        2,\n        3,\n      ]);\n      expect(onRowMouseOut.mock.calls.map(call => call[0].index)).toEqual([\n        0,\n        1,\n        2,\n      ]);\n    });\n  });\n\n  describe('rowClassName', () => {\n    it('should render a static classname given :rowClassName as a string', () => {\n      const staticClassName = 'staticClass';\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            rowClassName: staticClassName,\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      Array.from(rows).forEach(row => {\n        expect(row.className).toContain(staticClassName);\n      });\n    });\n\n    it('should render dynamic classname given :rowClassName as a function', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            rowClassName: ({index}) => (index % 2 === 0 ? 'even' : 'odd'),\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      Array.from(rows).forEach((row, index) => {\n        if (index % 2 === 0) {\n          expect(row.className).toContain('even');\n          expect(row.className).not.toContain('odd');\n        } else {\n          expect(row.className).toContain('odd');\n          expect(row.className).not.toContain('even');\n        }\n      });\n    });\n  });\n\n  describe('onRowsRendered', () => {\n    it('should call :onRowsRendered at least one row is rendered', () => {\n      let startIndex, stopIndex;\n      render(\n        getMarkup({\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n        }),\n      );\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(7);\n    });\n\n    it('should not call :onRowsRendered unless the start or stop indices have changed', () => {\n      let numCalls = 0;\n      let startIndex;\n      let stopIndex;\n      const onRowsRendered = params => {\n        startIndex = params.startIndex;\n        stopIndex = params.stopIndex;\n        numCalls++;\n      };\n      render(getMarkup({onRowsRendered}));\n      expect(numCalls).toEqual(1);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(7);\n      render(getMarkup({onRowsRendered}));\n      expect(numCalls).toEqual(1);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(7);\n    });\n\n    it('should call :onRowsRendered if the start or stop indices have changed', () => {\n      let numCalls = 0;\n      let startIndex;\n      let stopIndex;\n      const onRowsRendered = params => {\n        startIndex = params.startIndex;\n        stopIndex = params.stopIndex;\n        numCalls++;\n      };\n      render(getMarkup({onRowsRendered}));\n      expect(numCalls).toEqual(1);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(7);\n      render(\n        getMarkup({\n          height: 50,\n          onRowsRendered,\n        }),\n      );\n      expect(numCalls).toEqual(2);\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(2);\n    });\n\n    it('should not call :onRowsRendered if no rows are rendered', () => {\n      let startIndex, stopIndex;\n      render(\n        getMarkup({\n          height: 0,\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n        }),\n      );\n      expect(startIndex).toEqual(undefined);\n      expect(stopIndex).toEqual(undefined);\n    });\n  });\n\n  describe(':scrollTop property', () => {\n    it('should render correctly when an initial :scrollTop property is specified', () => {\n      let startIndex, stopIndex;\n      render(\n        getMarkup({\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n          scrollTop: 80,\n        }),\n      );\n      expect(startIndex).toEqual(8);\n      expect(stopIndex).toEqual(15);\n    });\n\n    it('should render correctly when :scrollTop property is updated', () => {\n      let startIndex, stopIndex;\n\n      render(\n        getMarkup({\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n        }),\n      );\n      expect(startIndex).toEqual(0);\n      expect(stopIndex).toEqual(7);\n\n      render(\n        getMarkup({\n          onRowsRendered: params => ({startIndex, stopIndex} = params),\n          scrollTop: 80,\n        }),\n      );\n      expect(startIndex).toEqual(8);\n      expect(stopIndex).toEqual(15);\n    });\n  });\n\n  describe('styles, classNames, and ids', () => {\n    it('should use the expected global CSS classNames', () => {\n      const node = findDOMNode(\n        render(\n          getMarkup({\n            sort: () => {},\n            sortBy: 'name',\n            sortDirection: SortDirection.ASC,\n          }),\n        ),\n      );\n      expect(node.className).toEqual('ReactVirtualized__Table');\n      expect(\n        node.querySelector('.ReactVirtualized__Table__headerRow'),\n      ).toBeTruthy();\n      expect(\n        node.querySelector('.ReactVirtualized__Table__rowColumn'),\n      ).toBeTruthy();\n      expect(\n        node.querySelector('.ReactVirtualized__Table__headerColumn'),\n      ).toBeTruthy();\n      expect(node.querySelector('.ReactVirtualized__Table__row')).toBeTruthy();\n      expect(\n        node.querySelector('.ReactVirtualized__Table__sortableHeaderColumn'),\n      ).toBeTruthy();\n      expect(\n        node.querySelector('.ReactVirtualized__Table__sortableHeaderIcon'),\n      ).toBeTruthy();\n    });\n\n    it('should use a custom :className if specified', () => {\n      const node = findDOMNode(\n        render(\n          getMarkup({\n            className: 'foo',\n            headerClassName: 'bar',\n            rowClassName: 'baz',\n          }),\n        ),\n      );\n      expect(node.className).toContain('foo');\n      expect(node.querySelectorAll('.bar').length).toEqual(2);\n      expect(node.querySelectorAll('.baz').length).toEqual(9);\n    });\n\n    it('should use a custom :id if specified', () => {\n      const node = findDOMNode(render(getMarkup({id: 'bar'})));\n      expect(node.getAttribute('id')).toEqual('bar');\n    });\n\n    it('should not set :id on the inner Grid', () => {\n      const node = findDOMNode(render(getMarkup({id: 'bar'})));\n      const grid = node.querySelector('.ReactVirtualized__Grid');\n      expect(grid.getAttribute('id')).not.toEqual('bar');\n    });\n\n    it('should use custom :styles if specified', () => {\n      const columnStyle = {backgroundColor: 'red', overflow: 'visible'};\n      const headerStyle = {backgroundColor: 'blue'};\n      const columnHeaderStyle = {color: 'yellow'};\n      const rowStyle = {backgroundColor: 'green'};\n      const style = {backgroundColor: 'orange'};\n      const node = findDOMNode(\n        render(\n          getMarkup({\n            columnStyle,\n            headerStyle,\n            columnHeaderStyle,\n            rowStyle,\n            style,\n          }),\n        ),\n      );\n      expect(\n        node.querySelector('.ReactVirtualized__Table__rowColumn').style\n          .backgroundColor,\n      ).toEqual('red');\n      expect(\n        node.querySelector('.ReactVirtualized__Table__rowColumn').style\n          .overflow,\n      ).toEqual('visible');\n      expect(\n        node.querySelector('.ReactVirtualized__Table__headerColumn').style\n          .backgroundColor,\n      ).toEqual('blue');\n      expect(\n        node.querySelector('.ReactVirtualized__Table__headerColumn').style\n          .color,\n      ).toEqual('yellow');\n      expect(\n        node.querySelector('.ReactVirtualized__Table__row').style\n          .backgroundColor,\n      ).toEqual('green');\n      expect(node.style.backgroundColor).toEqual('orange');\n    });\n\n    it('should render dynamic style given :rowStyle as a function', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            rowStyle: ({index}) =>\n              index % 2 === 0\n                ? {backgroundColor: 'red'}\n                : {backgroundColor: 'green'},\n          }),\n        ),\n      );\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      Array.from(rows).forEach((row, index) => {\n        if (index % 2 === 0) {\n          expect(row.style.backgroundColor).toEqual('red');\n        } else {\n          expect(row.style.backgroundColor).toEqual('green');\n        }\n      });\n    });\n\n    it('should pass :gridClassName and :gridStyle to the inner Grid', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            gridClassName: 'foo',\n            gridStyle: {backgroundColor: 'red'},\n          }),\n        ),\n      );\n      const grid = rendered.querySelector('.ReactVirtualized__Grid');\n      expect(grid.className).toContain('foo');\n      expect(grid.style.backgroundColor).toEqual('red');\n    });\n  });\n\n  describe('overscanRowCount', () => {\n    it('should not overscan by default', () => {\n      const mock = jest.fn();\n      mock.mockImplementation(overscanIndicesGetter);\n\n      render(\n        getMarkup({\n          overscanIndicesGetter: mock,\n        }),\n      );\n      expect(mock.mock.calls[0][0].overscanCellsCount).toEqual(0);\n      expect(mock.mock.calls[1][0].overscanCellsCount).toEqual(0);\n    });\n\n    it('should overscan the specified amount', () => {\n      const mock = jest.fn();\n      mock.mockImplementation(overscanIndicesGetter);\n\n      render(\n        getMarkup({\n          overscanIndicesGetter: mock,\n          overscanRowCount: 10,\n        }),\n      );\n      expect(mock.mock.calls[0][0].overscanCellsCount).toEqual(0);\n      expect(mock.mock.calls[1][0].overscanCellsCount).toEqual(10);\n    });\n  });\n\n  describe('onScroll', () => {\n    it('should trigger callback when component initially mounts', () => {\n      const onScrollCalls = [];\n      render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      expect(onScrollCalls).toEqual([\n        {\n          clientHeight: 80,\n          scrollHeight: 1000,\n          scrollTop: 0,\n        },\n      ]);\n    });\n\n    it('should trigger callback when component scrolls', () => {\n      const onScrollCalls = [];\n      const rendered = render(\n        getMarkup({\n          onScroll: params => onScrollCalls.push(params),\n        }),\n      );\n      const target = {\n        scrollLeft: 0,\n        scrollTop: 100,\n      };\n      rendered.Grid._scrollingContainer = target; // HACK to work around _onScroll target check\n      Simulate.scroll(findDOMNode(rendered.Grid), {target});\n      expect(onScrollCalls.length).toEqual(2);\n      expect(onScrollCalls[1]).toEqual({\n        clientHeight: 80,\n        scrollHeight: 1000,\n        scrollTop: 100,\n      });\n    });\n  });\n\n  describe('a11y properties', () => {\n    it('should set aria role on the table', () => {\n      const node = findDOMNode(render(getMarkup()));\n      expect(node.getAttribute('role')).toEqual('grid');\n    });\n\n    it('should set aria col/row count on the table', () => {\n      const node = findDOMNode(render(getMarkup()));\n      expect(node.getAttribute('aria-colcount')).toEqual('2');\n      expect(node.getAttribute('aria-rowcount')).toEqual(`${list.size}`);\n    });\n\n    it('should pass down aria labels on the table', () => {\n      const node = findDOMNode(\n        render(\n          getMarkup({\n            'aria-label': 'my-table-label',\n            'aria-labelledby': 'my-table-label-id',\n          }),\n        ),\n      );\n      expect(node.getAttribute('aria-label')).toEqual('my-table-label');\n      expect(node.getAttribute('aria-labelledby')).toEqual('my-table-label-id');\n    });\n\n    it('should set aria role on the header row', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const row = rendered.querySelector('.ReactVirtualized__Table__headerRow');\n      expect(row.getAttribute('role')).toEqual('row');\n    });\n\n    it('should set appropriate aria role on the grid', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const grid = rendered.querySelector('.ReactVirtualized__Table__Grid');\n      expect(grid.getAttribute('role')).toEqual('rowgroup');\n    });\n\n    it('should set aria role on a row', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const row = rendered.querySelector('.ReactVirtualized__Table__row');\n      expect(row.getAttribute('role')).toEqual('row');\n    });\n\n    it('should set aria rowindex on a row', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');\n      expect(rows[0].getAttribute('aria-rowindex')).toEqual('1');\n      expect(rows[1].getAttribute('aria-rowindex')).toEqual('2');\n    });\n\n    it('should set aria role on a cell', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const cell = rendered.querySelector(\n        '.ReactVirtualized__Table__rowColumn',\n      );\n      expect(cell.getAttribute('role')).toEqual('gridcell');\n    });\n\n    it('should set aria colindex on a cell', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const cells = rendered.querySelectorAll(\n        '.ReactVirtualized__Table__rowColumn',\n      );\n      expect(cells[0].getAttribute('aria-colindex')).toEqual('1');\n      expect(cells[1].getAttribute('aria-colindex')).toEqual('2');\n    });\n\n    it('should set aria-describedby on a cell when the column has an id', () => {\n      const columnID = 'column-header-test';\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnID,\n          }),\n        ),\n      );\n      const cell = rendered.querySelector(\n        '.ReactVirtualized__Table__rowColumn',\n      );\n      expect(cell.getAttribute('aria-describedby')).toEqual(columnID);\n    });\n\n    it('should attach a11y properties to a row if :onRowClick is specified', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            onRowClick: () => {},\n          }),\n        ),\n      );\n      const row = rendered.querySelector('.ReactVirtualized__Table__row');\n      expect(row.getAttribute('aria-label')).toEqual('row');\n      expect(row.tabIndex).toEqual(0);\n    });\n\n    it('should not attach a11y properties to a row if no :onRowClick is specified', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            onRowClick: null,\n          }),\n        ),\n      );\n      const row = rendered.querySelector('.ReactVirtualized__Table__row');\n      expect(row.getAttribute('aria-label')).toEqual(null);\n      expect(row.tabIndex).toEqual(-1);\n    });\n\n    it('should set aria role on a header column', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      const header = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn',\n      );\n      expect(header.getAttribute('role')).toEqual('columnheader');\n    });\n\n    it('should set aria-sort ascending on a header column if the column is sorted ascending', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            sortBy: 'name',\n            sortDirection: SortDirection.ASC,\n          }),\n        ),\n      );\n      const header = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn',\n      );\n      expect(header.getAttribute('aria-sort')).toEqual('ascending');\n    });\n\n    it('should set aria-sort descending on a header column if the column is sorted descending', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            sortBy: 'name',\n            sortDirection: SortDirection.DESC,\n          }),\n        ),\n      );\n      const header = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn',\n      );\n      expect(header.getAttribute('aria-sort')).toEqual('descending');\n    });\n\n    it('should set aria-sort to \"none\" if the column is sortable but not the current sort', () => {\n      const rendered = findDOMNode(\n        render(getMarkup({disableSort: true, sort: jest.fn()})),\n      );\n      const headers = rendered.querySelectorAll(\n        '.ReactVirtualized__Table__headerColumn',\n      );\n      // the first column is not sortable\n      expect(headers[0].getAttribute('aria-sort')).toBe(null);\n      // the second column is sortable\n      expect(headers[1].getAttribute('aria-sort')).toEqual('none');\n    });\n\n    it('should set id on a header column when the column has an id', () => {\n      const columnID = 'column-header-test';\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            columnID,\n          }),\n        ),\n      );\n      const header = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn',\n      );\n      expect(header.getAttribute('id')).toEqual(columnID);\n    });\n\n    it('should attach a11y properties to a header column if sort is enabled', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            disableSort: false,\n            sort: () => {},\n          }),\n        ),\n      );\n      const row = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn',\n      );\n      expect(row.getAttribute('aria-label')).toEqual('Name');\n      expect(row.tabIndex).toEqual(0);\n    });\n\n    it('should not attach a11y properties to a header column if sort is not enabled', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            disableSort: true,\n          }),\n        ),\n      );\n      const row = rendered.querySelector(\n        '.ReactVirtualized__Table__headerColumn',\n      );\n      expect(row.getAttribute('aria-label')).toEqual(null);\n      expect(row.tabIndex).toEqual(-1);\n    });\n  });\n\n  describe('tabIndex', () => {\n    it('should be focusable by default', () => {\n      const rendered = findDOMNode(render(getMarkup()));\n      expect(\n        rendered.querySelector('.ReactVirtualized__Grid').tabIndex,\n      ).toEqual(0);\n    });\n\n    it('should allow tabIndex to be overridden', () => {\n      const rendered = findDOMNode(\n        render(\n          getMarkup({\n            tabIndex: -1,\n          }),\n        ),\n      );\n      expect(\n        rendered.querySelector('.ReactVirtualized__Grid').tabIndex,\n      ).toEqual(-1);\n    });\n  });\n\n  describe('pure', () => {\n    it('should not re-render unless props have changed', () => {\n      let headerRendererCalled = false;\n      let cellRendererCalled = false;\n      function headerRenderer() {\n        headerRendererCalled = true;\n        return 'foo';\n      }\n      function cellRenderer() {\n        cellRendererCalled = true;\n        return 'foo';\n      }\n      const markup = getMarkup({\n        headerRenderer,\n        cellRenderer,\n      });\n      render(markup);\n      expect(headerRendererCalled).toEqual(true);\n      expect(cellRendererCalled).toEqual(true);\n      headerRendererCalled = false;\n      cellRendererCalled = false;\n      render(markup);\n      expect(headerRendererCalled).toEqual(false);\n      expect(cellRendererCalled).toEqual(false);\n    });\n\n    it('should re-render both the Table and the inner Grid whenever an external property changes', () => {\n      let headerRendererCalled = false;\n      let cellRendererCalled = false;\n      function headerRenderer() {\n        headerRendererCalled = true;\n        return 'foo';\n      }\n      function cellRenderer() {\n        cellRendererCalled = true;\n        return 'foo';\n      }\n      const initialProperties = {\n        autoHeight: false,\n        cellRenderer,\n        estimatedRowSize: 15,\n        headerRenderer,\n        overscanRowCount: 1,\n        rowHeight: 15,\n        rowCount: 20,\n        scrollToAlignment: 'auto',\n        scrollTop: 0,\n        sortBy: 'name',\n        sortDirection: SortDirection.ASC,\n        tabIndex: null,\n      };\n      const changedProperties = {\n        autoHeight: true,\n        estimatedRowSize: 10,\n        overscanRowCount: 0,\n        rowHeight: 10,\n        rowCount: 10,\n        scrollToAlignment: 'center',\n        scrollTop: 1,\n        sortBy: 'email',\n        sortDirection: SortDirection.DESC,\n        tabIndex: 1,\n      };\n      Object.entries(changedProperties).forEach(([key, value]) => {\n        render.unmount(); // Reset\n        render(getMarkup(initialProperties));\n        headerRendererCalled = true;\n        cellRendererCalled = false;\n        render(\n          getMarkup({\n            ...initialProperties,\n            [key]: value,\n          }),\n        );\n        expect(headerRendererCalled).toEqual(true);\n        expect(cellRendererCalled).toEqual(true);\n      });\n    });\n  });\n\n  it('should set the width of the single-column inner Grid to auto', () => {\n    const rendered = findDOMNode(render(getMarkup()));\n    expect(\n      rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer')\n        .style.width,\n    ).toEqual('auto');\n  });\n\n  it('should relay the Grid :parent param to the Column :cellRenderer', () => {\n    const cellRenderer = jest.fn().mockReturnValue(null);\n    findDOMNode(render(getMarkup({cellRenderer})));\n    expect(cellRenderer.mock.calls[0][0].parent).not.toBeUndefined();\n  });\n});\n"
  },
  {
    "path": "source/Table/Table.js",
    "content": "/** @flow */\n\nimport type {CellPosition} from '../Grid';\n\nimport clsx from 'clsx';\nimport Column from './Column';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport Grid, {accessibilityOverscanIndicesGetter} from '../Grid';\n\nimport defaultRowRenderer from './defaultRowRenderer';\nimport defaultHeaderRowRenderer from './defaultHeaderRowRenderer';\nimport SortDirection from './SortDirection';\n\n/**\n * Table component with fixed headers and virtualized rows for improved performance with large data sets.\n * This component expects explicit width, height, and padding parameters.\n */\nexport default class Table extends React.PureComponent {\n  static propTypes = {\n    /** This is just set on the grid top element. */\n    'aria-label': PropTypes.string,\n\n    /** This is just set on the grid top element. */\n    'aria-labelledby': PropTypes.string,\n\n    /**\n     * Removes fixed height from the scrollingContainer so that the total height\n     * of rows can stretch the window. Intended for use with WindowScroller\n     */\n    autoHeight: PropTypes.bool,\n\n    /** One or more Columns describing the data displayed in this row */\n    children: props => {\n      const children = React.Children.toArray(props.children);\n      for (let i = 0; i < children.length; i++) {\n        const childType = children[i].type;\n        if (childType !== Column && !(childType.prototype instanceof Column)) {\n          return new Error('Table only accepts children of type Column');\n        }\n      }\n    },\n\n    /** Optional CSS class name */\n    className: PropTypes.string,\n\n    /** Disable rendering the header at all */\n    disableHeader: PropTypes.bool,\n\n    /**\n     * Used to estimate the total height of a Table before all of its rows have actually been measured.\n     * The estimated total height is adjusted as rows are rendered.\n     */\n    estimatedRowSize: PropTypes.number.isRequired,\n\n    /** Optional custom CSS class name to attach to inner Grid element. */\n    gridClassName: PropTypes.string,\n\n    /** Optional inline style to attach to inner Grid element. */\n    gridStyle: PropTypes.object,\n\n    /** Optional CSS class to apply to all column headers */\n    headerClassName: PropTypes.string,\n\n    /** Fixed height of header row */\n    headerHeight: PropTypes.number.isRequired,\n\n    /**\n     * Responsible for rendering a table row given an array of columns:\n     * Should implement the following interface: ({\n     *   className: string,\n     *   columns: any[],\n     *   style: any\n     * }): PropTypes.node\n     */\n    headerRowRenderer: PropTypes.func,\n\n    /** Optional custom inline style to attach to table header columns. */\n    headerStyle: PropTypes.object,\n\n    /** Fixed/available height for out DOM element */\n    height: PropTypes.number.isRequired,\n\n    /** Optional id */\n    id: PropTypes.string,\n\n    /** Optional renderer to be used in place of table body rows when rowCount is 0 */\n    noRowsRenderer: PropTypes.func,\n\n    /**\n     * Optional callback when a column is clicked.\n     * ({ columnData: any, dataKey: string }): void\n     */\n    onColumnClick: PropTypes.func,\n\n    /**\n     * Optional callback when a column's header is clicked.\n     * ({ columnData: any, dataKey: string }): void\n     */\n    onHeaderClick: PropTypes.func,\n\n    /**\n     * Callback invoked when a user clicks on a table row.\n     * ({ index: number }): void\n     */\n    onRowClick: PropTypes.func,\n\n    /**\n     * Callback invoked when a user double-clicks on a table row.\n     * ({ index: number }): void\n     */\n    onRowDoubleClick: PropTypes.func,\n\n    /**\n     * Callback invoked when the mouse leaves a table row.\n     * ({ index: number }): void\n     */\n    onRowMouseOut: PropTypes.func,\n\n    /**\n     * Callback invoked when a user moves the mouse over a table row.\n     * ({ index: number }): void\n     */\n    onRowMouseOver: PropTypes.func,\n\n    /**\n     * Callback invoked when a user right-clicks on a table row.\n     * ({ index: number }): void\n     */\n    onRowRightClick: PropTypes.func,\n\n    /**\n     * Callback invoked with information about the slice of rows that were just rendered.\n     * ({ startIndex, stopIndex }): void\n     */\n    onRowsRendered: PropTypes.func,\n\n    /**\n     * Callback invoked whenever the scroll offset changes within the inner scrollable region.\n     * This callback can be used to sync scrolling between lists, tables, or grids.\n     * ({ clientHeight, scrollHeight, scrollTop }): void\n     */\n    onScroll: PropTypes.func.isRequired,\n\n    /** See Grid#overscanIndicesGetter */\n    overscanIndicesGetter: PropTypes.func.isRequired,\n\n    /**\n     * Number of rows to render above/below the visible bounds of the list.\n     * These rows can help for smoother scrolling on touch devices.\n     */\n    overscanRowCount: PropTypes.number.isRequired,\n\n    /**\n     * Optional CSS class to apply to all table rows (including the header row).\n     * This property can be a CSS class name (string) or a function that returns a class name.\n     * If a function is provided its signature should be: ({ index: number }): string\n     */\n    rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),\n\n    /**\n     * Callback responsible for returning a data row given an index.\n     * ({ index: number }): any\n     */\n    rowGetter: PropTypes.func.isRequired,\n\n    /**\n     * Either a fixed row height (number) or a function that returns the height of a row given its index.\n     * ({ index: number }): number\n     */\n    rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func])\n      .isRequired,\n\n    /** Number of rows in table. */\n    rowCount: PropTypes.number.isRequired,\n\n    /**\n     * Responsible for rendering a table row given an array of columns:\n     * Should implement the following interface: ({\n     *   className: string,\n     *   columns: Array,\n     *   index: number,\n     *   isScrolling: boolean,\n     *   onRowClick: ?Function,\n     *   onRowDoubleClick: ?Function,\n     *   onRowMouseOver: ?Function,\n     *   onRowMouseOut: ?Function,\n     *   rowData: any,\n     *   style: any\n     * }): PropTypes.node\n     */\n    rowRenderer: PropTypes.func,\n\n    /** Optional custom inline style to attach to table rows. */\n    rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func])\n      .isRequired,\n\n    /** See Grid#scrollToAlignment */\n    scrollToAlignment: PropTypes.oneOf(['auto', 'end', 'start', 'center'])\n      .isRequired,\n\n    /** Row index to ensure visible (by forcefully scrolling if necessary) */\n    scrollToIndex: PropTypes.number.isRequired,\n\n    /** Vertical offset. */\n    scrollTop: PropTypes.number,\n\n    /**\n     * Sort function to be called if a sortable header is clicked.\n     * Should implement the following interface: ({\n     *   defaultSortDirection: 'ASC' | 'DESC',\n     *   event: MouseEvent,\n     *   sortBy: string,\n     *   sortDirection: SortDirection\n     * }): void\n     */\n    sort: PropTypes.func,\n\n    /** Table data is currently sorted by this :dataKey (if it is sorted at all) */\n    sortBy: PropTypes.string,\n\n    /** Table data is currently sorted in this direction (if it is sorted at all) */\n    sortDirection: PropTypes.oneOf([SortDirection.ASC, SortDirection.DESC]),\n\n    /** Optional inline style */\n    style: PropTypes.object,\n\n    /** Tab index for focus */\n    tabIndex: PropTypes.number,\n\n    /** Width of list */\n    width: PropTypes.number.isRequired,\n  };\n\n  static defaultProps = {\n    disableHeader: false,\n    estimatedRowSize: 30,\n    headerHeight: 0,\n    headerStyle: {},\n    noRowsRenderer: () => null,\n    onRowsRendered: () => null,\n    onScroll: () => null,\n    overscanIndicesGetter: accessibilityOverscanIndicesGetter,\n    overscanRowCount: 10,\n    rowRenderer: defaultRowRenderer,\n    headerRowRenderer: defaultHeaderRowRenderer,\n    rowStyle: {},\n    scrollToAlignment: 'auto',\n    scrollToIndex: -1,\n    style: {},\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      scrollbarWidth: 0,\n    };\n\n    this._createColumn = this._createColumn.bind(this);\n    this._createRow = this._createRow.bind(this);\n    this._onScroll = this._onScroll.bind(this);\n    this._onSectionRendered = this._onSectionRendered.bind(this);\n    this._setRef = this._setRef.bind(this);\n    this._setGridElementRef = this._setGridElementRef.bind(this);\n  }\n\n  forceUpdateGrid() {\n    if (this.Grid) {\n      this.Grid.forceUpdate();\n    }\n  }\n\n  /** See Grid#getOffsetForCell */\n  getOffsetForRow({alignment, index}) {\n    if (this.Grid) {\n      const {scrollTop} = this.Grid.getOffsetForCell({\n        alignment,\n        rowIndex: index,\n      });\n\n      return scrollTop;\n    }\n    return 0;\n  }\n\n  /** CellMeasurer compatibility */\n  invalidateCellSizeAfterRender({columnIndex, rowIndex}: CellPosition) {\n    if (this.Grid) {\n      this.Grid.invalidateCellSizeAfterRender({\n        rowIndex,\n        columnIndex,\n      });\n    }\n  }\n\n  /** See Grid#measureAllCells */\n  measureAllRows() {\n    if (this.Grid) {\n      this.Grid.measureAllCells();\n    }\n  }\n\n  /** CellMeasurer compatibility */\n  recomputeGridSize({columnIndex = 0, rowIndex = 0}: CellPosition = {}) {\n    if (this.Grid) {\n      this.Grid.recomputeGridSize({\n        rowIndex,\n        columnIndex,\n      });\n    }\n  }\n\n  /** See Grid#recomputeGridSize */\n  recomputeRowHeights(index = 0) {\n    if (this.Grid) {\n      this.Grid.recomputeGridSize({\n        rowIndex: index,\n      });\n    }\n  }\n\n  /** See Grid#scrollToPosition */\n  scrollToPosition(scrollTop = 0) {\n    if (this.Grid) {\n      this.Grid.scrollToPosition({scrollTop});\n    }\n  }\n\n  /** See Grid#scrollToCell */\n  scrollToRow(index = 0) {\n    if (this.Grid) {\n      this.Grid.scrollToCell({\n        columnIndex: 0,\n        rowIndex: index,\n      });\n    }\n  }\n\n  getScrollbarWidth() {\n    if (this.GridElement) {\n      const Grid = this.GridElement;\n      const clientWidth = Grid.clientWidth || 0;\n      const offsetWidth = Grid.offsetWidth || 0;\n      return offsetWidth - clientWidth;\n    }\n\n    return 0;\n  }\n\n  componentDidMount() {\n    this._setScrollbarWidth();\n  }\n\n  componentDidUpdate() {\n    this._setScrollbarWidth();\n  }\n\n  render() {\n    const {\n      children,\n      className,\n      disableHeader,\n      gridClassName,\n      gridStyle,\n      headerHeight,\n      headerRowRenderer,\n      height,\n      id,\n      noRowsRenderer,\n      rowClassName,\n      rowStyle,\n      scrollToIndex,\n      style,\n      width,\n    } = this.props;\n    const {scrollbarWidth} = this.state;\n\n    const availableRowsHeight = disableHeader ? height : height - headerHeight;\n\n    const rowClass =\n      typeof rowClassName === 'function'\n        ? rowClassName({index: -1})\n        : rowClassName;\n    const rowStyleObject =\n      typeof rowStyle === 'function' ? rowStyle({index: -1}) : rowStyle;\n\n    // Precompute and cache column styles before rendering rows and columns to speed things up\n    this._cachedColumnStyles = [];\n    React.Children.toArray(children).forEach((column, index) => {\n      const flexStyles = this._getFlexStyleForColumn(\n        column,\n        column.props.style || Column.defaultProps.style,\n      );\n\n      this._cachedColumnStyles[index] = {\n        overflow: 'hidden',\n        ...flexStyles,\n      };\n    });\n\n    // Note that we specify :rowCount, :scrollbarWidth, :sortBy, and :sortDirection as properties on Grid even though these have nothing to do with Grid.\n    // This is done because Grid is a pure component and won't update unless its properties or state has changed.\n    // Any property that should trigger a re-render of Grid then is specified here to avoid a stale display.\n    return (\n      <div\n        aria-label={this.props['aria-label']}\n        aria-labelledby={this.props['aria-labelledby']}\n        aria-colcount={React.Children.toArray(children).length}\n        aria-rowcount={this.props.rowCount}\n        className={clsx('ReactVirtualized__Table', className)}\n        id={id}\n        role=\"grid\"\n        style={style}>\n        {!disableHeader &&\n          headerRowRenderer({\n            className: clsx('ReactVirtualized__Table__headerRow', rowClass),\n            columns: this._getHeaderColumns(),\n            style: {\n              height: headerHeight,\n              overflow: 'hidden',\n              paddingRight: scrollbarWidth,\n              width: width,\n              ...rowStyleObject,\n            },\n          })}\n\n        <Grid\n          {...this.props}\n          elementRef={this._setGridElementRef}\n          aria-readonly={null}\n          autoContainerWidth\n          className={clsx('ReactVirtualized__Table__Grid', gridClassName)}\n          cellRenderer={this._createRow}\n          columnWidth={width}\n          columnCount={1}\n          height={availableRowsHeight}\n          id={undefined}\n          noContentRenderer={noRowsRenderer}\n          onScroll={this._onScroll}\n          onSectionRendered={this._onSectionRendered}\n          ref={this._setRef}\n          role=\"rowgroup\"\n          scrollbarWidth={scrollbarWidth}\n          scrollToRow={scrollToIndex}\n          style={{\n            ...gridStyle,\n            overflowX: 'hidden',\n          }}\n        />\n      </div>\n    );\n  }\n\n  _createColumn({column, columnIndex, isScrolling, parent, rowData, rowIndex}) {\n    const {onColumnClick} = this.props;\n    const {\n      cellDataGetter,\n      cellRenderer,\n      className,\n      columnData,\n      dataKey,\n      id,\n    } = column.props;\n\n    const cellData = cellDataGetter({columnData, dataKey, rowData});\n    const renderedCell = cellRenderer({\n      cellData,\n      columnData,\n      columnIndex,\n      dataKey,\n      isScrolling,\n      parent,\n      rowData,\n      rowIndex,\n    });\n\n    const onClick = event => {\n      onColumnClick && onColumnClick({columnData, dataKey, event});\n    };\n\n    const style = this._cachedColumnStyles[columnIndex];\n\n    const title = typeof renderedCell === 'string' ? renderedCell : null;\n\n    // Avoid using object-spread syntax with multiple objects here,\n    // Since it results in an extra method call to 'babel-runtime/helpers/extends'\n    // See PR https://github.com/bvaughn/react-virtualized/pull/942\n    return (\n      <div\n        aria-colindex={columnIndex + 1}\n        aria-describedby={id}\n        className={clsx('ReactVirtualized__Table__rowColumn', className)}\n        key={'Row' + rowIndex + '-' + 'Col' + columnIndex}\n        onClick={onClick}\n        role=\"gridcell\"\n        style={style}\n        title={title}>\n        {renderedCell}\n      </div>\n    );\n  }\n\n  _createHeader({column, index}) {\n    const {\n      headerClassName,\n      headerStyle,\n      onHeaderClick,\n      sort,\n      sortBy,\n      sortDirection,\n    } = this.props;\n    const {\n      columnData,\n      dataKey,\n      defaultSortDirection,\n      disableSort,\n      headerRenderer,\n      id,\n      label,\n    } = column.props;\n    const sortEnabled = !disableSort && sort;\n\n    const classNames = clsx(\n      'ReactVirtualized__Table__headerColumn',\n      headerClassName,\n      column.props.headerClassName,\n      {\n        ReactVirtualized__Table__sortableHeaderColumn: sortEnabled,\n      },\n    );\n    const style = this._getFlexStyleForColumn(column, {\n      ...headerStyle,\n      ...column.props.headerStyle,\n    });\n\n    const renderedHeader = headerRenderer({\n      columnData,\n      dataKey,\n      disableSort,\n      label,\n      sortBy,\n      sortDirection,\n    });\n\n    let headerOnClick,\n      headerOnKeyDown,\n      headerTabIndex,\n      headerAriaSort,\n      headerAriaLabel;\n\n    if (sortEnabled || onHeaderClick) {\n      // If this is a sortable header, clicking it should update the table data's sorting.\n      const isFirstTimeSort = sortBy !== dataKey;\n\n      // If this is the firstTime sort of this column, use the column default sort order.\n      // Otherwise, invert the direction of the sort.\n      const newSortDirection = isFirstTimeSort\n        ? defaultSortDirection\n        : sortDirection === SortDirection.DESC\n        ? SortDirection.ASC\n        : SortDirection.DESC;\n\n      const onClick = event => {\n        sortEnabled &&\n          sort({\n            defaultSortDirection,\n            event,\n            sortBy: dataKey,\n            sortDirection: newSortDirection,\n          });\n        onHeaderClick && onHeaderClick({columnData, dataKey, event});\n      };\n\n      const onKeyDown = event => {\n        if (event.key === 'Enter' || event.key === ' ') {\n          onClick(event);\n        }\n      };\n\n      headerAriaLabel = column.props['aria-label'] || label || dataKey;\n      headerAriaSort = 'none';\n      headerTabIndex = 0;\n      headerOnClick = onClick;\n      headerOnKeyDown = onKeyDown;\n    }\n\n    if (sortBy === dataKey) {\n      headerAriaSort =\n        sortDirection === SortDirection.ASC ? 'ascending' : 'descending';\n    }\n\n    // Avoid using object-spread syntax with multiple objects here,\n    // Since it results in an extra method call to 'babel-runtime/helpers/extends'\n    // See PR https://github.com/bvaughn/react-virtualized/pull/942\n    return (\n      <div\n        aria-label={headerAriaLabel}\n        aria-sort={headerAriaSort}\n        className={classNames}\n        id={id}\n        key={'Header-Col' + index}\n        onClick={headerOnClick}\n        onKeyDown={headerOnKeyDown}\n        role=\"columnheader\"\n        style={style}\n        tabIndex={headerTabIndex}>\n        {renderedHeader}\n      </div>\n    );\n  }\n\n  _createRow({rowIndex: index, isScrolling, key, parent, style}) {\n    const {\n      children,\n      onRowClick,\n      onRowDoubleClick,\n      onRowRightClick,\n      onRowMouseOver,\n      onRowMouseOut,\n      rowClassName,\n      rowGetter,\n      rowRenderer,\n      rowStyle,\n    } = this.props;\n\n    const {scrollbarWidth} = this.state;\n\n    const rowClass =\n      typeof rowClassName === 'function' ? rowClassName({index}) : rowClassName;\n    const rowStyleObject =\n      typeof rowStyle === 'function' ? rowStyle({index}) : rowStyle;\n    const rowData = rowGetter({index});\n\n    const columns = React.Children.toArray(children).map(\n      (column, columnIndex) =>\n        this._createColumn({\n          column,\n          columnIndex,\n          isScrolling,\n          parent,\n          rowData,\n          rowIndex: index,\n          scrollbarWidth,\n        }),\n    );\n\n    const className = clsx('ReactVirtualized__Table__row', rowClass);\n    const flattenedStyle = {\n      ...style,\n      height: this._getRowHeight(index),\n      overflow: 'hidden',\n      paddingRight: scrollbarWidth,\n      ...rowStyleObject,\n    };\n\n    return rowRenderer({\n      className,\n      columns,\n      index,\n      isScrolling,\n      key,\n      onRowClick,\n      onRowDoubleClick,\n      onRowRightClick,\n      onRowMouseOver,\n      onRowMouseOut,\n      rowData,\n      style: flattenedStyle,\n    });\n  }\n\n  /**\n   * Determines the flex-shrink, flex-grow, and width values for a cell (header or column).\n   */\n  _getFlexStyleForColumn(column, customStyle = {}) {\n    const flexValue = `${column.props.flexGrow} ${column.props.flexShrink} ${column.props.width}px`;\n\n    const style = {\n      ...customStyle,\n      flex: flexValue,\n      msFlex: flexValue,\n      WebkitFlex: flexValue,\n    };\n\n    if (column.props.maxWidth) {\n      style.maxWidth = column.props.maxWidth;\n    }\n\n    if (column.props.minWidth) {\n      style.minWidth = column.props.minWidth;\n    }\n\n    return style;\n  }\n\n  _getHeaderColumns() {\n    const {children, disableHeader} = this.props;\n    const items = disableHeader ? [] : React.Children.toArray(children);\n\n    return items.map((column, index) => this._createHeader({column, index}));\n  }\n\n  _getRowHeight(rowIndex) {\n    const {rowHeight} = this.props;\n\n    return typeof rowHeight === 'function'\n      ? rowHeight({index: rowIndex})\n      : rowHeight;\n  }\n\n  _onScroll({clientHeight, scrollHeight, scrollTop}) {\n    const {onScroll} = this.props;\n\n    onScroll({clientHeight, scrollHeight, scrollTop});\n  }\n\n  _onSectionRendered({\n    rowOverscanStartIndex,\n    rowOverscanStopIndex,\n    rowStartIndex,\n    rowStopIndex,\n  }) {\n    const {onRowsRendered} = this.props;\n\n    onRowsRendered({\n      overscanStartIndex: rowOverscanStartIndex,\n      overscanStopIndex: rowOverscanStopIndex,\n      startIndex: rowStartIndex,\n      stopIndex: rowStopIndex,\n    });\n  }\n\n  _setRef(ref) {\n    this.Grid = ref;\n  }\n\n  _setGridElementRef(ref) {\n    this.GridElement = ref;\n  }\n\n  _setScrollbarWidth() {\n    const scrollbarWidth = this.getScrollbarWidth();\n\n    this.setState({scrollbarWidth});\n  }\n}\n"
  },
  {
    "path": "source/Table/createMultiSort.jest.js",
    "content": "import createMultiSort from './createMultiSort';\n\ndescribe('createMultiSort', () => {\n  function simulate(\n    sort,\n    dataKey,\n    eventModifier = '',\n    defaultSortDirection = 'ASC',\n  ) {\n    sort({\n      defaultSortDirection,\n      event: {\n        ctrlKey: eventModifier === 'control',\n        metaKey: eventModifier === 'meta',\n        shiftKey: eventModifier === 'shift',\n      },\n      sortBy: dataKey,\n    });\n  }\n\n  it('errors if the user did not specify a sort callback', () => {\n    expect(createMultiSort).toThrow();\n  });\n\n  it('sets the correct default values', () => {\n    const multiSort = createMultiSort(jest.fn(), {\n      defaultSortBy: ['a', 'b'],\n      defaultSortDirection: {\n        a: 'ASC',\n        b: 'DESC',\n      },\n    });\n    expect(multiSort.sortBy).toEqual(['a', 'b']);\n    expect(multiSort.sortDirection.a).toBe('ASC');\n    expect(multiSort.sortDirection.b).toBe('DESC');\n  });\n\n  it('sets the correct default sparse values', () => {\n    const multiSort = createMultiSort(jest.fn(), {\n      defaultSortBy: ['a', 'b'],\n    });\n    expect(multiSort.sortBy).toEqual(['a', 'b']);\n    expect(multiSort.sortDirection.a).toBe('ASC');\n    expect(multiSort.sortDirection.b).toBe('ASC');\n  });\n\n  describe('on click', () => {\n    it('sets the correct default value for a field', () => {\n      const multiSort = createMultiSort(jest.fn());\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n\n      simulate(multiSort.sort, 'b', '', 'DESC');\n      expect(multiSort.sortBy).toEqual(['b']);\n      expect(multiSort.sortDirection.b).toBe('DESC');\n    });\n\n    it('toggles a field value', () => {\n      const multiSort = createMultiSort(jest.fn());\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n      expect(multiSort.sortDirection.a).toBe('DESC');\n\n      simulate(multiSort.sort, 'b', '', 'DESC');\n      expect(multiSort.sortBy).toEqual(['b']);\n      expect(multiSort.sortDirection.b).toBe('DESC');\n\n      simulate(multiSort.sort, 'b', '', 'DESC');\n      expect(multiSort.sortBy).toEqual(['b']);\n      expect(multiSort.sortDirection.b).toBe('ASC');\n    });\n\n    it('resets sort-by fields', () => {\n      const multiSort = createMultiSort(jest.fn(), {\n        defaultSortBy: ['a', 'b'],\n      });\n      expect(multiSort.sortBy).toEqual(['a', 'b']);\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n    });\n\n    it('resets sort-direction fields', () => {\n      const multiSort = createMultiSort(jest.fn(), {\n        defaultSortBy: ['a', 'b'],\n        defaultSortDirection: {\n          a: 'DESC',\n          b: 'ASC',\n        },\n      });\n      expect(multiSort.sortBy).toEqual(['a', 'b']);\n      expect(multiSort.sortDirection.a).toEqual('DESC');\n      expect(multiSort.sortDirection.b).toEqual('ASC');\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n      expect(multiSort.sortDirection.a).toEqual('ASC');\n      expect(multiSort.sortDirection.b).toEqual(undefined);\n\n      simulate(multiSort.sort, 'b');\n      expect(multiSort.sortBy).toEqual(['b']);\n      expect(multiSort.sortDirection.a).toEqual(undefined);\n      expect(multiSort.sortDirection.b).toEqual('ASC');\n    });\n  });\n\n  describe('on shift click', () => {\n    it('appends a field to the sort by list', () => {\n      const multiSort = createMultiSort(jest.fn());\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n\n      simulate(multiSort.sort, 'b', 'shift');\n      expect(multiSort.sortBy).toEqual(['a', 'b']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n      expect(multiSort.sortDirection.b).toBe('ASC');\n    });\n\n    it('toggles an appended field value', () => {\n      const multiSort = createMultiSort(jest.fn());\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n\n      simulate(multiSort.sort, 'b', 'shift');\n      expect(multiSort.sortBy).toEqual(['a', 'b']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n      expect(multiSort.sortDirection.b).toBe('ASC');\n\n      simulate(multiSort.sort, 'a', 'shift');\n      expect(multiSort.sortBy).toEqual(['a', 'b']);\n      expect(multiSort.sortDirection.a).toBe('DESC');\n      expect(multiSort.sortDirection.b).toBe('ASC');\n\n      simulate(multiSort.sort, 'a', 'shift');\n      expect(multiSort.sortBy).toEqual(['a', 'b']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n      expect(multiSort.sortDirection.b).toBe('ASC');\n    });\n\n    it('able to shift+click more than once', () => {\n      const multiSort = createMultiSort(jest.fn());\n\n      simulate(multiSort.sort, 'a');\n      expect(multiSort.sortBy).toEqual(['a']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n\n      simulate(multiSort.sort, 'b', 'shift');\n      expect(multiSort.sortBy).toEqual(['a', 'b']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n      expect(multiSort.sortDirection.b).toBe('ASC');\n\n      simulate(multiSort.sort, 'b');\n      expect(multiSort.sortBy).toEqual(['b']);\n      expect(multiSort.sortDirection.b).toBe('DESC');\n\n      simulate(multiSort.sort, 'a', 'shift');\n      expect(multiSort.sortBy).toEqual(['b', 'a']);\n      expect(multiSort.sortDirection.a).toBe('ASC');\n      expect(multiSort.sortDirection.b).toBe('DESC');\n    });\n  });\n\n  ['control', 'meta'].forEach(modifier => {\n    describe(`${modifier} click`, () => {\n      it('removes a field from the sort by list', () => {\n        const multiSort = createMultiSort(jest.fn(), {\n          defaultSortBy: ['a', 'b'],\n        });\n        expect(multiSort.sortBy).toEqual(['a', 'b']);\n\n        simulate(multiSort.sort, 'a', modifier);\n        expect(multiSort.sortBy).toEqual(['b']);\n\n        simulate(multiSort.sort, 'b', modifier);\n        expect(multiSort.sortBy).toEqual([]);\n      });\n\n      it('ignores fields not in the list on control click', () => {\n        const multiSort = createMultiSort(jest.fn(), {\n          defaultSortBy: ['a', 'b'],\n        });\n        expect(multiSort.sortBy).toEqual(['a', 'b']);\n\n        simulate(multiSort.sort, 'c', modifier);\n        expect(multiSort.sortBy).toEqual(['a', 'b']);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "source/Table/createMultiSort.js",
    "content": "/** @flow */\n\ntype SortDirection = 'ASC' | 'DESC';\n\ntype SortParams = {\n  defaultSortDirection: SortDirection,\n  event: MouseEvent,\n  sortBy: string,\n};\n\ntype SortDirectionMap = {[string]: SortDirection};\n\ntype MultiSortOptions = {\n  defaultSortBy: ?Array<string>,\n  defaultSortDirection: ?SortDirectionMap,\n};\n\ntype MultiSortReturn = {\n  /**\n   * Sort property to be passed to the `Table` component.\n   * This function updates `sortBy` and `sortDirection` values.\n   */\n  sort: (params: SortParams) => void,\n\n  /**\n   * Specifies the fields currently responsible for sorting data,\n   * In order of importance.\n   */\n  sortBy: Array<string>,\n\n  /**\n   * Specifies the direction a specific field is being sorted in.\n   */\n  sortDirection: SortDirectionMap,\n};\n\nexport default function createMultiSort(\n  sortCallback: Function,\n  {defaultSortBy, defaultSortDirection = {}}: MultiSortOptions = {},\n): MultiSortReturn {\n  if (!sortCallback) {\n    throw Error(`Required parameter \"sortCallback\" not specified`);\n  }\n\n  const sortBy = defaultSortBy || [];\n  const sortDirection = {};\n\n  sortBy.forEach(dataKey => {\n    sortDirection[dataKey] =\n      defaultSortDirection[dataKey] !== undefined\n        ? defaultSortDirection[dataKey]\n        : 'ASC';\n  });\n\n  function sort({\n    defaultSortDirection,\n    event,\n    sortBy: dataKey,\n  }: SortParams): void {\n    if (event.shiftKey) {\n      // Shift + click appends a column to existing criteria\n      if (sortDirection[dataKey] !== undefined) {\n        sortDirection[dataKey] =\n          sortDirection[dataKey] === 'ASC' ? 'DESC' : 'ASC';\n      } else {\n        sortDirection[dataKey] = defaultSortDirection;\n        sortBy.push(dataKey);\n      }\n    } else if (event.ctrlKey || event.metaKey) {\n      // Control + click removes column from sort (if pressent)\n      const index = sortBy.indexOf(dataKey);\n      if (index >= 0) {\n        sortBy.splice(index, 1);\n        delete sortDirection[dataKey];\n      }\n    } else {\n      // Clear sortBy array of all non-selected keys\n      sortBy.length = 0;\n      sortBy.push(dataKey);\n\n      // Clear sortDirection object of all non-selected keys\n      const sortDirectionKeys = Object.keys(sortDirection);\n      sortDirectionKeys.forEach(key => {\n        if (key !== dataKey) delete sortDirection[key];\n      });\n\n      // If key is already selected, reverse sort direction.\n      // Else, set sort direction to default direction.\n      if (sortDirection[dataKey] !== undefined) {\n        sortDirection[dataKey] =\n          sortDirection[dataKey] === 'ASC' ? 'DESC' : 'ASC';\n      } else {\n        sortDirection[dataKey] = defaultSortDirection;\n      }\n    }\n\n    // Notify application code\n    sortCallback({\n      sortBy,\n      sortDirection,\n    });\n  }\n\n  return {\n    sort,\n    sortBy,\n    sortDirection,\n  };\n}\n"
  },
  {
    "path": "source/Table/defaultCellDataGetter.js",
    "content": "/** @flow */\nimport type {CellDataGetterParams} from './types';\n\n/**\n * Default accessor for returning a cell value for a given attribute.\n * This function expects to operate on either a vanilla Object or an Immutable Map.\n * You should override the column's cellDataGetter if your data is some other type of object.\n */\nexport default function defaultCellDataGetter({\n  dataKey,\n  rowData,\n}: CellDataGetterParams) {\n  if (typeof rowData.get === 'function') {\n    return rowData.get(dataKey);\n  } else {\n    return rowData[dataKey];\n  }\n}\n"
  },
  {
    "path": "source/Table/defaultCellRenderer.js",
    "content": "/** @flow */\nimport type {CellRendererParams} from './types';\n\n/**\n * Default cell renderer that displays an attribute as a simple string\n * You should override the column's cellRenderer if your data is some other type of object.\n */\nexport default function defaultCellRenderer({\n  cellData,\n}: CellRendererParams): string {\n  if (cellData == null) {\n    return '';\n  } else {\n    return String(cellData);\n  }\n}\n"
  },
  {
    "path": "source/Table/defaultHeaderRenderer.js",
    "content": "/** @flow */\nimport * as React from 'react';\nimport SortIndicator from './SortIndicator';\nimport type {HeaderRendererParams} from './types';\n\n/**\n * Default table header renderer.\n */\nexport default function defaultHeaderRenderer({\n  dataKey,\n  label,\n  sortBy,\n  sortDirection,\n}: HeaderRendererParams) {\n  const showSortIndicator = sortBy === dataKey;\n  const children = [\n    <span\n      className=\"ReactVirtualized__Table__headerTruncatedText\"\n      key=\"label\"\n      title={typeof label === 'string' ? label : null}>\n      {label}\n    </span>,\n  ];\n\n  if (showSortIndicator) {\n    children.push(\n      <SortIndicator key=\"SortIndicator\" sortDirection={sortDirection} />,\n    );\n  }\n\n  return children;\n}\n"
  },
  {
    "path": "source/Table/defaultHeaderRowRenderer.js",
    "content": "/** @flow */\nimport * as React from 'react';\nimport type {HeaderRowRendererParams} from './types';\n\nexport default function defaultHeaderRowRenderer({\n  className,\n  columns,\n  style,\n}: HeaderRowRendererParams) {\n  return (\n    <div className={className} role=\"row\" style={style}>\n      {columns}\n    </div>\n  );\n}\n"
  },
  {
    "path": "source/Table/defaultRowRenderer.js",
    "content": "/** @flow */\nimport * as React from 'react';\nimport type {RowRendererParams} from './types';\n\n/**\n * Default row renderer for Table.\n */\nexport default function defaultRowRenderer({\n  className,\n  columns,\n  index,\n  key,\n  onRowClick,\n  onRowDoubleClick,\n  onRowMouseOut,\n  onRowMouseOver,\n  onRowRightClick,\n  rowData,\n  style,\n}: RowRendererParams) {\n  const a11yProps = {'aria-rowindex': index + 1};\n\n  if (\n    onRowClick ||\n    onRowDoubleClick ||\n    onRowMouseOut ||\n    onRowMouseOver ||\n    onRowRightClick\n  ) {\n    a11yProps['aria-label'] = 'row';\n    a11yProps.tabIndex = 0;\n\n    if (onRowClick) {\n      a11yProps.onClick = event => onRowClick({event, index, rowData});\n    }\n    if (onRowDoubleClick) {\n      a11yProps.onDoubleClick = event =>\n        onRowDoubleClick({event, index, rowData});\n    }\n    if (onRowMouseOut) {\n      a11yProps.onMouseOut = event => onRowMouseOut({event, index, rowData});\n    }\n    if (onRowMouseOver) {\n      a11yProps.onMouseOver = event => onRowMouseOver({event, index, rowData});\n    }\n    if (onRowRightClick) {\n      a11yProps.onContextMenu = event =>\n        onRowRightClick({event, index, rowData});\n    }\n  }\n\n  return (\n    <div\n      {...a11yProps}\n      className={className}\n      key={key}\n      role=\"row\"\n      style={style}>\n      {columns}\n    </div>\n  );\n}\n"
  },
  {
    "path": "source/Table/index.js",
    "content": "/* @flow */\nimport createMultiSort from './createMultiSort';\nimport defaultCellDataGetter from './defaultCellDataGetter';\nimport defaultCellRenderer from './defaultCellRenderer';\nimport defaultHeaderRowRenderer from './defaultHeaderRowRenderer.js';\nimport defaultHeaderRenderer from './defaultHeaderRenderer';\nimport defaultRowRenderer from './defaultRowRenderer';\nimport Column from './Column';\nimport SortDirection from './SortDirection';\nimport SortIndicator from './SortIndicator';\nimport Table from './Table';\n\nexport default Table;\nexport {\n  createMultiSort,\n  defaultCellDataGetter,\n  defaultCellRenderer,\n  defaultHeaderRowRenderer,\n  defaultHeaderRenderer,\n  defaultRowRenderer,\n  Column,\n  SortDirection,\n  SortIndicator,\n  Table,\n};\n"
  },
  {
    "path": "source/Table/types.js",
    "content": "/** @flow */\nexport type CellDataGetterParams = {\n  columnData: ?any,\n  dataKey: string,\n  rowData: any,\n};\n\nexport type CellRendererParams = {\n  cellData: ?any,\n  columnData: ?any,\n  dataKey: string,\n  rowData: any,\n  rowIndex: number,\n};\n\nexport type HeaderRowRendererParams = {\n  className: string,\n  columns: Array<any>,\n  style: any,\n};\n\nexport type HeaderRendererParams = {\n  columnData: ?any,\n  dataKey: string,\n  disableSort: ?boolean,\n  label: ?any,\n  sortBy: ?string,\n  sortDirection: ?string,\n};\n\nexport type RowRendererParams = {\n  className: string,\n  columns: Array<any>,\n  index: number,\n  isScrolling: boolean,\n  onRowClick: ?Function,\n  onRowDoubleClick: ?Function,\n  onRowMouseOver: ?Function,\n  onRowMouseOut: ?Function,\n  rowData: any,\n  style: any,\n  key: string,\n};\n"
  },
  {
    "path": "source/TestUtils.js",
    "content": "import * as ReactDOM from 'react-dom';\n\n/**\n * Helper method for testing components that may use Portal and thus require cleanup.\n * This helper method renders components to a transient node that is destroyed after the test completes.\n * Note that rendering twice within the same test method will update the same element (rather than recreate it).\n */\nexport function render(markup) {\n  if (!render._mountNode) {\n    render._mountNode = document.createElement('div');\n\n    // Unless we attach the mount-node to body, getBoundingClientRect() won't work\n    document.body.appendChild(render._mountNode);\n\n    afterEach(render.unmount);\n  }\n\n  return ReactDOM.render(markup, render._mountNode);\n}\n\n/**\n * The render() method auto-unmounts components after each test has completed.\n * Use this method manually to test the componentWillUnmount() lifecycle method.\n */\nrender.unmount = function() {\n  if (render._mountNode) {\n    ReactDOM.unmountComponentAtNode(render._mountNode);\n\n    document.body.removeChild(render._mountNode);\n\n    render._mountNode = null;\n  }\n};\n"
  },
  {
    "path": "source/WindowScroller/WindowScroller.e2e.js",
    "content": "/**\n * @jest-environment jest-environment-puppeteer\n */\n\nconst bootstrap = async () => {\n  const page = await global.browser.newPage();\n  const scripts = [\n    './node_modules/react/umd/react.development.js',\n    './node_modules/react-dom/umd/react-dom.development.js',\n    './dist/umd/react-virtualized.js',\n  ];\n\n  for (const path of scripts) {\n    await page.addScriptTag({path});\n  }\n\n  return page;\n};\n\nconst renderWindowScroller = ({scrollElement}) => {\n  const {render} = window.ReactDOM;\n  const {createElement} = window.React;\n  const {WindowScroller} = window.ReactVirtualized;\n\n  const container = document.createElement('div');\n  container.id = 'container';\n  container.style.margin = '100px';\n  container.style.padding = '50px';\n  document.body.appendChild(container);\n  document.body.style.margin = 0;\n\n  if (scrollElement === 'container') {\n    container.style.width = '100%';\n    container.style.height = '100%';\n    container.style.overflow = 'auto';\n  }\n\n  render(\n    createElement(\n      WindowScroller,\n      {\n        scrollElement: scrollElement === 'container' ? container : window,\n        onScroll: window.scrollFn,\n        onResize: window.resizeFn,\n      },\n      () => createElement('div', {style: {width: 2000, height: 3000}}),\n    ),\n    container,\n  );\n};\n\nconst delay = time => new Promise(resolve => setTimeout(resolve, time));\n\ntest('save position after resize and then scroll in window', async () => {\n  const page = await bootstrap();\n  const scrollFn = jest.fn();\n  const resizeFn = jest.fn();\n  await page.exposeFunction('scrollFn', scrollFn);\n  await page.exposeFunction('resizeFn', resizeFn);\n\n  await page.setViewport({width: 400, height: 600});\n  await page.evaluate(renderWindowScroller, {scrollElement: 'window'});\n\n  // scroll more than viewport\n  await page.evaluate(() => window.scrollTo(610, 830));\n  await delay(100);\n  // resize a bit container/window\n  await page.setViewport({width: 300, height: 500});\n  await delay(100);\n  // scroll again\n  await page.evaluate(() => window.scrollTo(620, 840));\n  await delay(100);\n\n  await page.close();\n\n  expect(scrollFn.mock.calls).toEqual([\n    [{scrollLeft: 610 - 150, scrollTop: 830 - 150}],\n    [{scrollLeft: 620 - 150, scrollTop: 840 - 150}],\n  ]);\n  expect(resizeFn.mock.calls).toEqual([[{width: 300, height: 500}]]);\n});\n\ntest('save position after resize and then scroll in container', async () => {\n  const page = await bootstrap();\n  const scrollFn = jest.fn();\n  const resizeFn = jest.fn();\n  await page.exposeFunction('scrollFn', scrollFn);\n  await page.exposeFunction('resizeFn', resizeFn);\n\n  await page.setViewport({width: 400, height: 600});\n  await page.evaluate(renderWindowScroller, {scrollElement: 'container'});\n\n  // scroll more than viewport\n  await page.$eval('#container', el => el.scrollTo(610, 830));\n  await delay(100);\n  // resize a bit container/window\n  await page.setViewport({width: 300, height: 500});\n  await delay(100);\n  // scroll again\n  await page.$eval('#container', el => el.scrollTo(620, 840));\n  await delay(100);\n\n  await page.close();\n\n  expect(scrollFn.mock.calls).toEqual([\n    [{scrollLeft: 610 - 50, scrollTop: 830 - 50}],\n    [{scrollLeft: 620 - 50, scrollTop: 840 - 50}],\n  ]);\n  expect(resizeFn.mock.calls).toEqual([\n    [{width: 500, height: 700}],\n    [{width: 400, height: 600}],\n  ]);\n});\n\ntest('react on container resize without window changing', async () => {\n  const page = await bootstrap();\n  const resizeFn = jest.fn();\n  await page.exposeFunction('resizeFn', resizeFn);\n\n  await page.evaluate(() => {\n    const {render} = window.ReactDOM;\n    const {createElement} = window.React;\n    const {WindowScroller} = window.ReactVirtualized;\n\n    const wrapper = document.createElement('div');\n    wrapper.id = 'wrapper';\n    Object.assign(wrapper.style, {\n      width: '1000px',\n      height: '800px',\n      display: 'flex',\n    });\n    const container = document.createElement('div');\n    Object.assign(container.style, {\n      flex: '1',\n    });\n    wrapper.appendChild(container);\n    document.body.style.margin = 0;\n    document.body.appendChild(wrapper);\n\n    render(\n      createElement(\n        WindowScroller,\n        {scrollElement: container, onResize: window.resizeFn},\n        () => null,\n      ),\n      container,\n    );\n  });\n\n  await delay(100);\n\n  await page.$eval('#wrapper', el => {\n    el.style.width = '500px';\n    el.style.height = '700px';\n  });\n\n  await delay(100);\n\n  await page.close();\n\n  expect(resizeFn.mock.calls).toEqual([\n    [{width: 1000, height: 800}],\n    [{width: 500, height: 700}],\n  ]);\n});\n"
  },
  {
    "path": "source/WindowScroller/WindowScroller.example.css",
    "content": ".WindowScrollerWrapper {\n  flex: 1 1 auto;\n}\n\n.List {\n  border: 1px solid #e0e0e0;\n}\n\n.row {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  padding: 0 25px;\n  background-color: #fff;\n  border-bottom: 1px solid #e0e0e0;\n}\n.rowScrolling::after {\n  content: ': scrolling';\n  font-size: 0.65rem;\n  color: #aaa;\n}\n\n.checkboxLabel {\n  display: flex;\n  align-items: center;\n}\n.checkbox {\n  margin-right: 5px;\n}\n"
  },
  {
    "path": "source/WindowScroller/WindowScroller.example.js",
    "content": "// @flow\n\nimport clsx from 'clsx';\nimport Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {\n  ContentBox,\n  ContentBoxHeader,\n  ContentBoxParagraph,\n} from '../demo/ContentBox';\nimport {LabeledInput, InputRow} from '../demo/LabeledInput';\nimport WindowScroller from './WindowScroller';\nimport List from '../List';\nimport AutoSizer from '../AutoSizer';\nimport styles from './WindowScroller.example.css';\n\ntype State = {\n  scrollToIndex: number,\n  showHeaderText: boolean,\n};\n\nexport default class WindowScrollerExample extends React.PureComponent<\n  {},\n  State,\n> {\n  static contextTypes = {\n    customElement: PropTypes.any,\n    isScrollingCustomElement: PropTypes.bool.isRequired,\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n    setScrollingCustomElement: PropTypes.func,\n  };\n\n  state = {\n    scrollToIndex: -1,\n    showHeaderText: true,\n  };\n\n  _windowScroller: ?WindowScroller;\n\n  render() {\n    const {customElement, isScrollingCustomElement, list} = this.context;\n    const {scrollToIndex, showHeaderText} = this.state;\n\n    return (\n      <ContentBox>\n        <ContentBoxHeader\n          text=\"WindowScroller\"\n          sourceLink=\"https://github.com/bvaughn/react-virtualized/blob/master/source/WindowScroller/WindowScroller.example.js\"\n          docsLink=\"https://github.com/bvaughn/react-virtualized/blob/master/docs/WindowScroller.md\"\n        />\n\n        {showHeaderText && (\n          <ContentBoxParagraph>\n            This component decorates <code>List</code>, <code>Table</code>, or\n            any other component and manages the window scroll to scroll through\n            the list\n          </ContentBoxParagraph>\n        )}\n\n        {showHeaderText && (\n          <ContentBoxParagraph>\n            <button onClick={this._hideHeader}>Hide header text</button>\n          </ContentBoxParagraph>\n        )}\n\n        <ContentBoxParagraph>\n          <label className={styles.checkboxLabel}>\n            <input\n              aria-label=\"Use custom element for scrolling\"\n              className={styles.checkbox}\n              type=\"checkbox\"\n              checked={isScrollingCustomElement}\n              onChange={this._onCheckboxChange}\n            />\n            Use custom element for scrolling\n          </label>\n        </ContentBoxParagraph>\n        <InputRow>\n          <LabeledInput\n            label=\"Scroll to\"\n            name=\"onScrollToRow\"\n            placeholder=\"Index...\"\n            onChange={this._onScrollToRowChange}\n            value={scrollToIndex || ''}\n          />\n        </InputRow>\n\n        <WindowScroller\n          ref={this._setRef}\n          scrollElement={isScrollingCustomElement ? customElement : window}>\n          {({height, isScrolling, registerChild, onChildScroll, scrollTop}) => (\n            <div className={styles.WindowScrollerWrapper}>\n              <AutoSizer disableHeight>\n                {({width}) => (\n                  <div ref={registerChild}>\n                    <List\n                      ref={el => {\n                        window.listEl = el;\n                      }}\n                      autoHeight\n                      className={styles.List}\n                      height={height}\n                      isScrolling={isScrolling}\n                      onScroll={onChildScroll}\n                      overscanRowCount={2}\n                      rowCount={list.size}\n                      rowHeight={30}\n                      rowRenderer={this._rowRenderer}\n                      scrollToIndex={scrollToIndex}\n                      scrollTop={scrollTop}\n                      width={width}\n                    />\n                  </div>\n                )}\n              </AutoSizer>\n            </div>\n          )}\n        </WindowScroller>\n      </ContentBox>\n    );\n  }\n\n  _hideHeader = () => {\n    const {showHeaderText} = this.state;\n\n    this.setState(\n      {\n        showHeaderText: !showHeaderText,\n      },\n      () => {\n        if (this._windowScroller) {\n          this._windowScroller.updatePosition();\n        }\n      },\n    );\n  };\n\n  _rowRenderer = ({index, isScrolling, isVisible, key, style}) => {\n    const {list} = this.context;\n    const row = list.get(index);\n    const className = clsx(styles.row, {\n      [styles.rowScrolling]: isScrolling,\n      isVisible: isVisible,\n    });\n\n    return (\n      <div key={key} className={className} style={style}>\n        {row.name}\n      </div>\n    );\n  };\n\n  _setRef = windowScroller => {\n    this._windowScroller = windowScroller;\n  };\n\n  _onCheckboxChange = event => {\n    this.context.setScrollingCustomElement(event.target.checked);\n  };\n\n  _onScrollToRowChange = event => {\n    const {list} = this.context;\n    let scrollToIndex = Math.min(\n      list.size - 1,\n      parseInt(event.target.value, 10),\n    );\n\n    if (isNaN(scrollToIndex)) {\n      scrollToIndex = undefined;\n    }\n\n    setTimeout(() => {\n      this.setState({scrollToIndex});\n    }, 0);\n  };\n}\n"
  },
  {
    "path": "source/WindowScroller/WindowScroller.header-resize.e2e.js",
    "content": "/**\n * @jest-environment jest-environment-puppeteer\n */\n\nconst bootstrap = async () => {\n  const page = await global.browser.newPage();\n  const scripts = [\n    './node_modules/react/umd/react.development.js',\n    './node_modules/react-dom/umd/react-dom.development.js',\n    './dist/umd/react-virtualized.js',\n  ];\n\n  for (const path of scripts) {\n    await page.addScriptTag({path});\n  }\n\n  return page;\n};\n\nconst renderWindowScroller = updateScrollTopOnUpdatePosition => {\n  const {render} = window.ReactDOM;\n  const {createElement, useState, useEffect} = window.React;\n  const {WindowScroller} = window.ReactVirtualized;\n\n  const container = document.createElement('div');\n  container.id = 'container';\n  document.body.appendChild(container);\n  document.body.style.margin = 0;\n\n  function Header({height}) {\n    return createElement('div', {style: {height, backgroundColor: 'red'}});\n  }\n\n  function App() {\n    const [height, setHeight] = useState(100);\n    window.setHeaderHeight = setHeight;\n    useEffect(() => () => (window.setHeaderHeight = null));\n\n    return createElement(\n      'div',\n      {},\n      createElement(Header, {height}),\n      createElement(\n        WindowScroller,\n        {\n          updateScrollTopOnUpdatePosition,\n          ref: windowScroller => {\n            window.windowScroller = windowScroller;\n          },\n          onScroll: window.scrollFn,\n          onResize: window.resizeFn,\n        },\n        ({width, scrollTop}) => {\n          console.log({scrollTop});\n          window.windowScrollerScrollTop = scrollTop;\n          return createElement('div', {\n            style: {\n              width,\n              height: 3000,\n              backgroundColor: 'yellow',\n            },\n          });\n        },\n      ),\n    );\n  }\n\n  render(\n    createElement(\n      'div',\n      {'data-test-id': 'main-container'},\n      createElement(App, {}),\n    ),\n    container,\n  );\n};\n\njest.setTimeout(1200000);\n\nconst delay = time => new Promise(resolve => setTimeout(resolve, time));\n\ntest('will react to header height updates if notified through updatePosition', async () => {\n  const page = await bootstrap();\n  const scrollFn = jest.fn();\n  const resizeFn = jest.fn();\n  await page.exposeFunction('scrollFn', scrollFn);\n  await page.exposeFunction('resizeFn', resizeFn);\n\n  await page.setViewport({width: 400, height: 600});\n  await page.evaluate(renderWindowScroller, true);\n\n  const el = await page.$('[data-test-id=\"main-container\"]');\n  expect(el).not.toBeNull();\n\n  await page.evaluate(() => window.scrollTo(0, 200));\n  await delay(500);\n\n  {\n    const scrollTop = await page.evaluate(() => window.windowScrollerScrollTop);\n    expect(scrollTop).toEqual(100);\n  }\n  await delay(500);\n\n  // Update the header height\n  await page.evaluate(() => {\n    console.log('change header height');\n    window.setHeaderHeight(200);\n  });\n  await delay(500);\n\n  await page.evaluate(() => {\n    console.log('update position');\n    window.windowScroller.updatePosition();\n  });\n  await delay(500);\n\n  // Despite header updates, we'd expect the scrollTop to be the same.\n  {\n    const scrollTop = await page.evaluate(() => window.windowScrollerScrollTop);\n    expect(scrollTop).toEqual(100);\n  }\n});\n\ntest('will NOT react to header height updates if notified through updatePosition if `updateScrollTopOnUpdatePosition` is false', async () => {\n  const page = await bootstrap();\n  const scrollFn = jest.fn();\n  const resizeFn = jest.fn();\n  await page.exposeFunction('scrollFn', scrollFn);\n  await page.exposeFunction('resizeFn', resizeFn);\n\n  await page.setViewport({width: 400, height: 600});\n  await page.evaluate(renderWindowScroller, false);\n\n  const el = await page.$('[data-test-id=\"main-container\"]');\n  expect(el).not.toBeNull();\n\n  await page.evaluate(() => window.scrollTo(0, 200));\n  await delay(500);\n\n  {\n    const scrollTop = await page.evaluate(() => window.windowScrollerScrollTop);\n    expect(scrollTop).toEqual(100);\n  }\n  await delay(500);\n\n  // Update the header height\n  await page.evaluate(() => {\n    console.log('change header height');\n    window.setHeaderHeight(200);\n  });\n  await delay(500);\n\n  await page.evaluate(() => {\n    console.log('update position');\n    window.windowScroller.updatePosition();\n  });\n  await delay(500);\n\n  // Despite header updates, we'd expect the scrollTop to be the same.\n  // As the fix is off, this will fail.\n  const scrollTop = await page.evaluate(() => window.windowScrollerScrollTop);\n  expect(() => {\n    expect(scrollTop).toEqual(100);\n  }).toThrow();\n});\n\ntest('will properly process scroll events after header height updates', async () => {\n  const page = await bootstrap();\n  const scrollFn = jest.fn();\n  const resizeFn = jest.fn();\n  await page.exposeFunction('scrollFn', scrollFn);\n  await page.exposeFunction('resizeFn', resizeFn);\n\n  await page.setViewport({width: 400, height: 600});\n  await page.evaluate(renderWindowScroller, true);\n\n  const el = await page.$('[data-test-id=\"main-container\"]');\n  expect(el).not.toBeNull();\n\n  await page.evaluate(() => window.scrollTo(0, 200));\n  await delay(500);\n\n  {\n    const scrollTop = await page.evaluate(() => window.windowScrollerScrollTop);\n    expect(scrollTop).toEqual(100);\n  }\n  await delay(500);\n\n  // Update the header height\n  await page.evaluate(() => {\n    window.setHeaderHeight(200);\n  });\n  await delay(500);\n\n  await page.evaluate(() => {\n    window.windowScroller.updatePosition();\n  });\n  await delay(500);\n  // This is only 50px under the first position\n  await page.evaluate(() => window.scrollTo(0, 350));\n\n  {\n    const scrollTop = await page.evaluate(() => window.windowScrollerScrollTop);\n    expect(scrollTop).toEqual(150);\n  }\n});\n"
  },
  {
    "path": "source/WindowScroller/WindowScroller.jest.js",
    "content": "import * as React from 'react';\nimport {findDOMNode} from 'react-dom';\nimport {render} from '../TestUtils';\nimport WindowScroller, {IS_SCROLLING_TIMEOUT} from './WindowScroller';\n\nfunction mockGetBoundingClientRectForHeader({\n  documentOffset = 0,\n  height,\n  width,\n}) {\n  // Mock the WindowScroller element and window separately\n  // The only way to mock the former (before its created) is globally\n  Element.prototype.getBoundingClientRect = jest.fn(() => ({\n    top: height,\n    left: width,\n  }));\n  document.documentElement.getBoundingClientRect = jest.fn(() => ({\n    top: documentOffset,\n    left: documentOffset,\n  }));\n}\n\nfunction getMarkup({headerElements, documentOffset, renderFn, ...props} = {}) {\n  const windowScroller = (\n    <WindowScroller {...props}>\n      {params => <div>{renderFn && renderFn(params)}</div>}\n    </WindowScroller>\n  );\n\n  // JSDome doesn't implement a working getBoundingClientRect()\n  // But WindowScroller requires it\n  mockGetBoundingClientRectForHeader({\n    documentOffset,\n    height: headerElements ? headerElements.props.style.height : 0,\n    width: headerElements ? headerElements.props.style.width : 0,\n  });\n\n  if (headerElements) {\n    return (\n      <div>\n        {headerElements}\n        {windowScroller}\n      </div>\n    );\n  } else {\n    return windowScroller;\n  }\n}\n\nfunction simulateWindowScroll({scrollX = 0, scrollY = 0}) {\n  document.body.style.height = '10000px';\n  window.scrollX = scrollX;\n  window.scrollY = scrollY;\n  document.dispatchEvent(new window.Event('scroll', {bubbles: true}));\n  document.body.style.height = '';\n}\n\nfunction simulateWindowResize({height = 0, width = 0}) {\n  window.innerHeight = height;\n  window.innerWidth = width;\n  document.dispatchEvent(new window.Event('resize', {bubbles: true}));\n}\n\ndescribe('WindowScroller', () => {\n  // Set default window height and scroll position between tests\n  beforeEach(() => {\n    window.scrollY = 0;\n    window.scrollX = 0;\n    window.innerHeight = 500;\n    window.innerWidth = 500;\n  });\n\n  // Starts updating scrollTop only when the top position is reached\n  it('should have correct top and left properties to be defined on :_positionFromTop and :_positionFromLeft', () => {\n    const component = render(getMarkup());\n    const rendered = findDOMNode(component);\n    const {top, left} = rendered.getBoundingClientRect();\n    expect(component._positionFromTop).toEqual(top);\n    expect(component._positionFromLeft).toEqual(left);\n  });\n\n  it('should allow passing child element with registerChild of children function param', () => {\n    const scrollElement = document.createElement('div');\n    scrollElement.scrollTop = 100;\n    scrollElement.scrollLeft = 150;\n    scrollElement.getBoundingClientRect = () => ({\n      top: 200,\n      left: 250,\n    });\n    const child = document.createElement('div');\n    child.getBoundingClientRect = () => ({\n      top: 300,\n      left: 350,\n    });\n    const renderFn = jest.fn();\n    const component = render(getMarkup({scrollElement, renderFn}));\n    renderFn.mock.calls[0][0].registerChild(child);\n    expect(component._positionFromTop).toEqual(300 + 100 - 200);\n    expect(component._positionFromLeft).toEqual(350 + 150 - 250);\n  });\n\n  it('should warn on passing non-element or not null', () => {\n    const warnFn = jest.spyOn(console, 'warn');\n    const renderFn = jest.fn();\n\n    render(getMarkup({renderFn}));\n\n    renderFn.mock.calls[0][0].registerChild(1);\n    renderFn.mock.calls[0][0].registerChild(document.createElement('div'));\n    renderFn.mock.calls[0][0].registerChild(null);\n\n    expect(warnFn).toHaveBeenCalledTimes(1);\n    warnFn.mockRestore();\n  });\n\n  // Test edge-case reported in bvaughn/react-virtualized/pull/346\n  it('should have correct top and left properties to be defined on :_positionFromTop and :_positionFromLeft if documentElement is scrolled', () => {\n    render.unmount();\n\n    // Simulate scrolled documentElement\n    const component = render(\n      getMarkup({\n        documentOffset: -100,\n      }),\n    );\n    const rendered = findDOMNode(component);\n    const {top, left} = rendered.getBoundingClientRect();\n    expect(component._positionFromTop).toEqual(top + 100);\n    expect(component._positionFromLeft).toEqual(left + 100);\n    // Reset override\n    delete document.documentElement.getBoundingClientRect;\n  });\n\n  it('inherits the window height and passes it to child component', () => {\n    const renderFn = jest.fn();\n    const component = render(getMarkup({renderFn}));\n\n    expect(component.state.height).toEqual(window.innerHeight);\n    expect(component.state.height).toEqual(500);\n    expect(renderFn).lastCalledWith(\n      expect.objectContaining({\n        height: 500,\n      }),\n    );\n  });\n\n  it('should restore pointerEvents on body after IS_SCROLLING_TIMEOUT', async () => {\n    render(getMarkup());\n    document.body.style.pointerEvents = 'all';\n    simulateWindowScroll({scrollY: 5000});\n    expect(document.body.style.pointerEvents).toEqual('none');\n    await new Promise(resolve =>\n      setTimeout(resolve, IS_SCROLLING_TIMEOUT + 100),\n    );\n    expect(document.body.style.pointerEvents).toEqual('all');\n  });\n\n  it('should restore pointerEvents on body after unmount', () => {\n    render(getMarkup());\n    document.body.style.pointerEvents = 'all';\n    simulateWindowScroll({scrollY: 5000});\n    expect(document.body.style.pointerEvents).toEqual('none');\n    render.unmount();\n    expect(document.body.style.pointerEvents).toEqual('all');\n  });\n\n  describe('onScroll', () => {\n    it('should trigger callback when window scrolls', async () => {\n      const onScroll = jest.fn();\n      render(getMarkup({onScroll}));\n\n      simulateWindowScroll({scrollY: 5000});\n\n      // Allow scrolling timeout to complete so that the component computes state\n      await new Promise(resolve => setTimeout(resolve, 150));\n\n      expect(onScroll).toHaveBeenCalledWith({\n        scrollLeft: 0,\n        scrollTop: 5000,\n      });\n\n      simulateWindowScroll({\n        scrollX: 2500,\n        scrollY: 5000,\n      });\n\n      // Allow scrolling timeout to complete so that the component computes state\n      await new Promise(resolve => setTimeout(resolve, 150));\n\n      expect(onScroll).toHaveBeenCalledWith({\n        scrollLeft: 2500,\n        scrollTop: 5000,\n      });\n    });\n\n    it('should update :scrollTop when window is scrolled', async () => {\n      const renderFn = jest.fn();\n      const component = render(getMarkup({renderFn}));\n\n      // Initial load of the component should have 0 scrollTop\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          scrollTop: 0,\n        }),\n      );\n\n      simulateWindowScroll({scrollY: 5000});\n\n      // Allow scrolling timeout to complete so that the component computes state\n      await new Promise(resolve => setTimeout(resolve, 150));\n\n      const componentScrollTop = window.scrollY - component._positionFromTop;\n      expect(component.state.scrollTop).toEqual(componentScrollTop);\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          scrollTop: componentScrollTop,\n        }),\n      );\n    });\n\n    it('should specify :isScrolling when scrolling and reset after scrolling', async () => {\n      const renderFn = jest.fn();\n      render(getMarkup({renderFn}));\n\n      simulateWindowScroll({scrollY: 5000});\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          isScrolling: true,\n        }),\n      );\n\n      await new Promise(resolve => setTimeout(resolve, 250));\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          isScrolling: false,\n        }),\n      );\n    });\n\n    it('should support a custom :scrollingResetTimeInterval prop', async () => {\n      const renderFn = jest.fn();\n      render(\n        getMarkup({\n          scrollingResetTimeInterval: 500,\n          renderFn,\n        }),\n      );\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          isScrolling: false,\n        }),\n      );\n\n      simulateWindowScroll({scrollY: 5000});\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          isScrolling: true,\n        }),\n      );\n\n      await new Promise(resolve => setTimeout(resolve, 100));\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          isScrolling: true,\n        }),\n      );\n\n      await new Promise(resolve => setTimeout(resolve, 100));\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          isScrolling: true,\n        }),\n      );\n\n      await new Promise(resolve => setTimeout(resolve, 400));\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          isScrolling: false,\n        }),\n      );\n    });\n  });\n\n  describe('onResize', () => {\n    it('should trigger callback on init and when window resizes', () => {\n      const resizeFn = jest.fn();\n      render(getMarkup({onResize: resizeFn}));\n\n      simulateWindowResize({height: 1000, width: 1024});\n\n      expect(resizeFn).toHaveBeenCalledTimes(1);\n      expect(resizeFn).lastCalledWith({height: 1000, width: 1024});\n    });\n\n    it('should update height when window resizes', () => {\n      const renderFn = jest.fn();\n      const component = render(getMarkup({renderFn}));\n\n      // Initial load of the component should have the same window height = 500\n      expect(component.state.height).toEqual(window.innerHeight);\n      expect(component.state.height).toEqual(500);\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          height: 500,\n        }),\n      );\n\n      simulateWindowResize({height: 1000});\n\n      expect(component.state.height).toEqual(window.innerHeight);\n      expect(component.state.height).toEqual(1000);\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({\n          height: 1000,\n        }),\n      );\n    });\n  });\n\n  describe('updatePosition', () => {\n    it('should calculate the initial offset from the top of the page when mounted', () => {\n      let windowScroller;\n\n      render(\n        getMarkup({\n          headerElements: <div style={{height: 100}} />,\n          ref: ref => {\n            windowScroller = ref;\n          },\n        }),\n      );\n\n      expect(windowScroller._positionFromTop).toBe(100);\n    });\n\n    it('should recalculate the offset from the top when the window resizes', () => {\n      let windowScroller;\n\n      render(\n        getMarkup({\n          headerElements: <div id=\"header\" style={{height: 100, width: 150}} />,\n          ref: ref => {\n            windowScroller = ref;\n          },\n        }),\n      );\n\n      expect(windowScroller._positionFromTop).toBe(100);\n      expect(windowScroller._positionFromLeft).toBe(150);\n\n      mockGetBoundingClientRectForHeader({\n        height: 200,\n        width: 300,\n      });\n\n      expect(windowScroller._positionFromTop).toBe(100);\n      expect(windowScroller._positionFromLeft).toBe(150);\n\n      simulateWindowResize({height: 1000, width: 1000});\n\n      expect(windowScroller._positionFromTop).toBe(200);\n      expect(windowScroller._positionFromLeft).toBe(300);\n    });\n\n    it('should recalculate the offset from the top if called externally', () => {\n      let windowScroller;\n\n      render(\n        getMarkup({\n          headerElements: <div id=\"header\" style={{height: 100, width: 150}} />,\n          ref: ref => {\n            windowScroller = ref;\n          },\n        }),\n      );\n\n      expect(windowScroller._positionFromTop).toBe(100);\n      expect(windowScroller._positionFromLeft).toBe(150);\n\n      mockGetBoundingClientRectForHeader({\n        height: 200,\n        width: 300,\n      });\n\n      windowScroller.updatePosition();\n\n      expect(windowScroller._positionFromTop).toBe(200);\n      expect(windowScroller._positionFromLeft).toBe(300);\n    });\n  });\n\n  describe('when child scrolls', () => {\n    let originalScrollTo;\n    beforeEach(() => {\n      originalScrollTo = window.scrollTo;\n      window.scrollTo = (scrollX, scrollY) =>\n        simulateWindowScroll({scrollX, scrollY});\n    });\n\n    afterEach(() => {\n      window.scrollTo = originalScrollTo;\n      render.unmount();\n    });\n\n    it('should scroll the scrollElement (when it is window) the desired amount', () => {\n      const renderFn = jest.fn();\n      let windowScroller;\n\n      render(\n        getMarkup({\n          ref: ref => {\n            windowScroller = ref;\n          },\n          renderFn,\n        }),\n      );\n\n      renderFn.mock.calls[0][0].onChildScroll({scrollTop: 200});\n\n      expect(window.scrollY).toEqual(200 + windowScroller._positionFromTop);\n    });\n\n    it('should not scroll the scrollElement if trying to scroll to where we already are', () => {\n      const renderFn = jest.fn();\n\n      render(getMarkup({renderFn}));\n\n      simulateWindowScroll({scrollY: 200});\n\n      window.scrollTo = jest.fn();\n\n      renderFn.mock.calls[0][0].onChildScroll({scrollTop: 200});\n\n      expect(window.scrollTo).not.toHaveBeenCalled();\n    });\n\n    it('should scroll the scrollElement (when it is an element) the desired amount', () => {\n      let windowScroller;\n      const renderFn = jest.fn();\n      const divEl = document.createElement('div');\n\n      render(\n        getMarkup({\n          ref: ref => {\n            windowScroller = ref;\n          },\n          renderFn,\n          scrollElement: divEl,\n        }),\n      );\n\n      renderFn.mock.calls[0][0].onChildScroll({scrollTop: 200});\n\n      expect(divEl.scrollTop).toEqual(200 + windowScroller._positionFromTop);\n    });\n\n    it('should update own scrollTop', () => {\n      const renderFn = jest.fn();\n\n      render(getMarkup({renderFn}));\n\n      renderFn.mock.calls[0][0].onChildScroll({scrollTop: 200});\n\n      expect(renderFn).lastCalledWith(\n        expect.objectContaining({scrollTop: 200}),\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "source/WindowScroller/WindowScroller.js",
    "content": "// @flow\n\nimport * as React from 'react';\nimport {\n  registerScrollListener,\n  unregisterScrollListener,\n} from './utils/onScroll';\nimport {\n  getDimensions,\n  getPositionOffset,\n  getScrollOffset,\n} from './utils/dimensions';\nimport createDetectElementResize from '../vendor/detectElementResize';\n\ntype Props = {\n  /**\n   * Function responsible for rendering children.\n   * This function should implement the following signature:\n   * ({ height, isScrolling, scrollLeft, scrollTop, width }) => PropTypes.element\n   */\n  children: ({\n    onChildScroll: ({scrollTop: number}) => void,\n    registerChild: (?Element) => void,\n    height: number,\n    isScrolling: boolean,\n    scrollLeft: number,\n    scrollTop: number,\n    width: number,\n  }) => React.Node,\n\n  /** Callback to be invoked on-resize: ({ height, width }) */\n  onResize: ({height: number, width: number}) => void,\n\n  /** Callback to be invoked on-scroll: ({ scrollLeft, scrollTop }) */\n  onScroll: ({scrollLeft: number, scrollTop: number}) => void,\n\n  /** Element to attach scroll event listeners. Defaults to window. */\n  scrollElement: ?(typeof window | Element),\n  /**\n   * Wait this amount of time after the last scroll event before resetting child `pointer-events`.\n   */\n  scrollingResetTimeInterval: number,\n\n  /** Height used for server-side rendering */\n  serverHeight: number,\n\n  /** Width used for server-side rendering */\n  serverWidth: number,\n\n  /** Force scrollTop updates when .updatePosition is called, fixing forced header height change updates */\n  updateScrollTopOnUpdatePosition?: boolean,\n};\n\ntype State = {\n  height: number,\n  width: number,\n  isScrolling: boolean,\n  scrollLeft: number,\n  scrollTop: number,\n};\n\ntype ResizeHandler = (element: Element, onResize: () => void) => void;\n\ntype DetectElementResize = {\n  addResizeListener: ResizeHandler,\n  removeResizeListener: ResizeHandler,\n};\n\n/**\n * Specifies the number of milliseconds during which to disable pointer events while a scroll is in progress.\n * This improves performance and makes scrolling smoother.\n */\nexport const IS_SCROLLING_TIMEOUT = 150;\n\nconst getWindow = () => (typeof window !== 'undefined' ? window : undefined);\n\nexport default class WindowScroller extends React.PureComponent<Props, State> {\n  static defaultProps = {\n    onResize: () => {},\n    onScroll: () => {},\n    scrollingResetTimeInterval: IS_SCROLLING_TIMEOUT,\n    scrollElement: getWindow(),\n    serverHeight: 0,\n    serverWidth: 0,\n  };\n\n  _window = getWindow();\n  _isMounted = false;\n  _positionFromTop = 0;\n  _positionFromLeft = 0;\n  _detectElementResize: DetectElementResize;\n  _child: ?Element;\n  _windowScrollerRef: {current: HTMLElement | null} = React.createRef();\n\n  state = {\n    ...getDimensions(this.props.scrollElement, this.props),\n    isScrolling: false,\n    scrollLeft: 0,\n    scrollTop: 0,\n  };\n\n  updatePosition(scrollElement: ?Element = this.props.scrollElement) {\n    const {onResize} = this.props;\n    const {height, width} = this.state;\n\n    const thisNode = this._child || this._windowScrollerRef.current;\n    if (thisNode instanceof Element && scrollElement) {\n      const offset = getPositionOffset(thisNode, scrollElement);\n      this._positionFromTop = offset.top;\n      this._positionFromLeft = offset.left;\n    }\n\n    const dimensions = getDimensions(scrollElement, this.props);\n    if (height !== dimensions.height || width !== dimensions.width) {\n      this.setState({\n        height: dimensions.height,\n        width: dimensions.width,\n      });\n      onResize({\n        height: dimensions.height,\n        width: dimensions.width,\n      });\n    }\n\n    if (this.props.updateScrollTopOnUpdatePosition === true) {\n      this.__handleWindowScrollEvent();\n      this.__resetIsScrolling();\n    }\n  }\n\n  componentDidMount() {\n    const scrollElement = this.props.scrollElement;\n\n    this._detectElementResize = createDetectElementResize();\n\n    this.updatePosition(scrollElement);\n\n    if (scrollElement) {\n      registerScrollListener(this, scrollElement);\n      this._registerResizeListener(scrollElement);\n    }\n\n    this._isMounted = true;\n  }\n\n  componentDidUpdate(prevProps: Props, prevState: State) {\n    const {scrollElement} = this.props;\n    const {scrollElement: prevScrollElement} = prevProps;\n\n    if (\n      prevScrollElement !== scrollElement &&\n      prevScrollElement != null &&\n      scrollElement != null\n    ) {\n      this.updatePosition(scrollElement);\n\n      unregisterScrollListener(this, prevScrollElement);\n      registerScrollListener(this, scrollElement);\n\n      this._unregisterResizeListener(prevScrollElement);\n      this._registerResizeListener(scrollElement);\n    }\n  }\n\n  componentWillUnmount() {\n    const scrollElement = this.props.scrollElement;\n    if (scrollElement) {\n      unregisterScrollListener(this, scrollElement);\n      this._unregisterResizeListener(scrollElement);\n    }\n\n    this._isMounted = false;\n  }\n\n  render() {\n    const {children} = this.props;\n    const {isScrolling, scrollTop, scrollLeft, height, width} = this.state;\n\n    return React.createElement(\n      'div',\n      {\n        ref: this._windowScrollerRef,\n      },\n      children({\n        onChildScroll: this._onChildScroll,\n        registerChild: this._registerChild,\n        height,\n        isScrolling,\n        scrollLeft,\n        scrollTop,\n        width,\n      }),\n    );\n  }\n\n  _registerChild = element => {\n    if (element && !(element instanceof Element)) {\n      console.warn(\n        'WindowScroller registerChild expects to be passed Element or null',\n      );\n    }\n    this._child = element;\n    this.updatePosition();\n  };\n\n  _onChildScroll = ({scrollTop}) => {\n    if (this.state.scrollTop === scrollTop) {\n      return;\n    }\n\n    const scrollElement = this.props.scrollElement;\n    if (scrollElement) {\n      if (typeof scrollElement.scrollTo === 'function') {\n        scrollElement.scrollTo(0, scrollTop + this._positionFromTop);\n      } else {\n        scrollElement.scrollTop = scrollTop + this._positionFromTop;\n      }\n    }\n  };\n\n  _registerResizeListener = element => {\n    if (element === window) {\n      window.addEventListener('resize', this._onResize, false);\n    } else {\n      this._detectElementResize.addResizeListener(element, this._onResize);\n    }\n  };\n\n  _unregisterResizeListener = element => {\n    if (element === window) {\n      window.removeEventListener('resize', this._onResize, false);\n    } else if (element) {\n      this._detectElementResize.removeResizeListener(element, this._onResize);\n    }\n  };\n\n  _onResize = () => {\n    this.updatePosition();\n  };\n\n  // Referenced by utils/onScroll\n  __handleWindowScrollEvent = () => {\n    if (!this._isMounted) {\n      return;\n    }\n\n    const {onScroll} = this.props;\n\n    const scrollElement = this.props.scrollElement;\n    if (scrollElement) {\n      const scrollOffset = getScrollOffset(scrollElement);\n      const scrollLeft = Math.max(\n        0,\n        scrollOffset.left - this._positionFromLeft,\n      );\n      const scrollTop = Math.max(0, scrollOffset.top - this._positionFromTop);\n\n      this.setState({\n        isScrolling: true,\n        scrollLeft,\n        scrollTop,\n      });\n\n      onScroll({\n        scrollLeft,\n        scrollTop,\n      });\n    }\n  };\n\n  // Referenced by utils/onScroll\n  __resetIsScrolling = () => {\n    this.setState({\n      isScrolling: false,\n    });\n  };\n}\n"
  },
  {
    "path": "source/WindowScroller/WindowScroller.ssr.js",
    "content": "/**\n * @jest-environment node\n */\n\nimport * as React from 'react';\nimport * as ReactDOMServer from 'react-dom/server';\nimport WindowScroller from './WindowScroller';\n\ntest('should render content with default widths and heights initially', () => {\n  const rendered = ReactDOMServer.renderToString(\n    <WindowScroller serverHeight={100} serverWidth={200}>\n      {({height, width}) => <div>{`height:${height};width:${width}`}</div>}\n    </WindowScroller>,\n  );\n\n  expect(rendered).toContain('height:100');\n  expect(rendered).toContain('width:200');\n});\n"
  },
  {
    "path": "source/WindowScroller/index.js",
    "content": "// @flow\n\nimport WindowScroller, {IS_SCROLLING_TIMEOUT} from './WindowScroller';\n\nexport default WindowScroller;\nexport {WindowScroller, IS_SCROLLING_TIMEOUT};\n"
  },
  {
    "path": "source/WindowScroller/utils/dimensions.js",
    "content": "// @flow\n\n/**\n * Gets the dimensions of the element, accounting for API differences between\n * `window` and other DOM elements.\n */\n\ntype Dimensions = {\n  height: number,\n  width: number,\n};\n\n// TODO Move this into WindowScroller and import from there\ntype WindowScrollerProps = {\n  serverHeight: number,\n  serverWidth: number,\n};\n\nconst isWindow = element => element === window;\n\nconst getBoundingBox = element => element.getBoundingClientRect();\n\nexport function getDimensions(\n  scrollElement: ?Element,\n  props: WindowScrollerProps,\n): Dimensions {\n  if (!scrollElement) {\n    return {\n      height: props.serverHeight,\n      width: props.serverWidth,\n    };\n  } else if (isWindow(scrollElement)) {\n    const {innerHeight, innerWidth} = window;\n    return {\n      height: typeof innerHeight === 'number' ? innerHeight : 0,\n      width: typeof innerWidth === 'number' ? innerWidth : 0,\n    };\n  } else {\n    return getBoundingBox(scrollElement);\n  }\n}\n\n/**\n * Gets the vertical and horizontal position of an element within its scroll container.\n * Elements that have been “scrolled past” return negative values.\n * Handles edge-case where a user is navigating back (history) from an already-scrolled page.\n * In this case the body’s top or left position will be a negative number and this element’s top or left will be increased (by that amount).\n */\nexport function getPositionOffset(element: Element, container: Element) {\n  if (isWindow(container) && document.documentElement) {\n    const containerElement = document.documentElement;\n    const elementRect = getBoundingBox(element);\n    const containerRect = getBoundingBox(containerElement);\n    return {\n      top: elementRect.top - containerRect.top,\n      left: elementRect.left - containerRect.left,\n    };\n  } else {\n    const scrollOffset = getScrollOffset(container);\n    const elementRect = getBoundingBox(element);\n    const containerRect = getBoundingBox(container);\n    return {\n      top: elementRect.top + scrollOffset.top - containerRect.top,\n      left: elementRect.left + scrollOffset.left - containerRect.left,\n    };\n  }\n}\n\n/**\n * Gets the vertical and horizontal scroll amount of the element, accounting for IE compatibility\n * and API differences between `window` and other DOM elements.\n */\nexport function getScrollOffset(element: Element) {\n  if (isWindow(element) && document.documentElement) {\n    return {\n      top:\n        'scrollY' in window\n          ? window.scrollY\n          : document.documentElement.scrollTop,\n      left:\n        'scrollX' in window\n          ? window.scrollX\n          : document.documentElement.scrollLeft,\n    };\n  } else {\n    return {\n      top: element.scrollTop,\n      left: element.scrollLeft,\n    };\n  }\n}\n"
  },
  {
    "path": "source/WindowScroller/utils/onScroll.js",
    "content": "// @flow\n'no babel-plugin-flow-react-proptypes';\n\nimport {\n  requestAnimationTimeout,\n  cancelAnimationTimeout,\n} from '../../utils/requestAnimationTimeout';\nimport type WindowScroller from '../WindowScroller.js';\n\nlet mountedInstances = [];\nlet originalBodyPointerEvents = null;\nlet disablePointerEventsTimeoutId = null;\n\nfunction enablePointerEventsIfDisabled() {\n  if (disablePointerEventsTimeoutId) {\n    disablePointerEventsTimeoutId = null;\n\n    if (document.body && originalBodyPointerEvents != null) {\n      document.body.style.pointerEvents = originalBodyPointerEvents;\n    }\n\n    originalBodyPointerEvents = null;\n  }\n}\n\nfunction enablePointerEventsAfterDelayCallback() {\n  enablePointerEventsIfDisabled();\n  mountedInstances.forEach(instance => instance.__resetIsScrolling());\n}\n\nfunction enablePointerEventsAfterDelay() {\n  if (disablePointerEventsTimeoutId) {\n    cancelAnimationTimeout(disablePointerEventsTimeoutId);\n  }\n\n  var maximumTimeout = 0;\n  mountedInstances.forEach(instance => {\n    maximumTimeout = Math.max(\n      maximumTimeout,\n      instance.props.scrollingResetTimeInterval,\n    );\n  });\n\n  disablePointerEventsTimeoutId = requestAnimationTimeout(\n    enablePointerEventsAfterDelayCallback,\n    maximumTimeout,\n  );\n}\n\nfunction onScrollWindow(event: Event) {\n  if (\n    event.currentTarget === window &&\n    originalBodyPointerEvents == null &&\n    document.body\n  ) {\n    originalBodyPointerEvents = document.body.style.pointerEvents;\n\n    document.body.style.pointerEvents = 'none';\n  }\n  enablePointerEventsAfterDelay();\n  mountedInstances.forEach(instance => {\n    if (instance.props.scrollElement === event.currentTarget) {\n      instance.__handleWindowScrollEvent();\n    }\n  });\n}\n\nexport function registerScrollListener(\n  component: WindowScroller,\n  element: Element,\n) {\n  if (\n    !mountedInstances.some(instance => instance.props.scrollElement === element)\n  ) {\n    element.addEventListener('scroll', onScrollWindow);\n  }\n  mountedInstances.push(component);\n}\n\nexport function unregisterScrollListener(\n  component: WindowScroller,\n  element: Element,\n) {\n  mountedInstances = mountedInstances.filter(\n    instance => instance !== component,\n  );\n  if (!mountedInstances.length) {\n    element.removeEventListener('scroll', onScrollWindow);\n    if (disablePointerEventsTimeoutId) {\n      cancelAnimationTimeout(disablePointerEventsTimeoutId);\n      enablePointerEventsIfDisabled();\n    }\n  }\n}\n"
  },
  {
    "path": "source/demo/Application.css",
    "content": "html, body, :global(#root) {\n  height: 100%;\n}\n\nbody {\n  padding: 0;\n  margin: 0;\n  font-family: 'Roboto', 'Open Sans', sans-serif;\n  font-size: 12px;\n  color: #272727;\n  box-sizing: border-box;\n}\na {\n  color: #409890;\n}\n* {\n  font-size: inherit;\n  box-sizing: inherit;\n}\n\n.headerRow {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  width: 100%;\n  background-color: #4db6ac;\n  flex-shrink: 0;\n}\n\n.logo {\n  width: 35px;\n  height: 35px;\n}\n\n.logoRow {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.ReactVirtualizedContainer {\n  display: inline-flex;\n  flex-direction: row;\n  align-items: center;\n  justify-content: center;\n  margin: 0.25rem;\n}\n.LogoColumn {\n  display: inline-flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  padding: 0 1rem;\n  text-transform: uppercase;\n}\n.PrimaryLogoText,\n.SecondaryLogoText {\n  text-transform: lowercase;\n  font-size: 2em;\n}\n.PrimaryLogoText {\n  font-weight: bold;\n  color: #272727;\n  margin: 0 .5em;\n}\n.SecondaryLogoText {\n  color: #ffffff;\n}\n\n.NavList {\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  font-size: 0.8rem;\n}\n\n.ComponentList,\n.HighOrderComponentList {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  font-size: 0.7rem;\n}\n.ComponentList {\n  background-color: #272727;\n}\n.HighOrderComponentList {\n  background-color: #111;\n}\n\n.demo {\n  display: flex;\n  flex-direction: column;\n  height: 100vh;\n}\n.Body {\n  flex: 1 0 auto; \n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: center;\n}\n.ScrollingBody {\n  composes: Body;\n  overflow: auto;\n  flex: 1 1 auto;\n}\n.column {\n  display: flex;\n  flex-direction: column;\n  flex: 0 0 100%;\n  width: 100%;\n  max-width: 100%;\n}\n"
  },
  {
    "path": "source/demo/Application.js",
    "content": "import Immutable from 'immutable';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport {Redirect} from 'react-router';\nimport {HashRouter, Route} from 'react-router-dom';\n\nimport ComponentLink from './ComponentLink';\nimport styles from './Application.css';\nimport NavLink from './NavLink';\nimport Wizard from './Wizard';\nimport {TYPES} from './Icon';\nimport {generateRandomList} from './utils';\n\nimport ArrowKeyStepperExample from '../ArrowKeyStepper/ArrowKeyStepper.example';\nimport AutoSizerExample from '../AutoSizer/AutoSizer.example';\nimport CellMeasurerExample from '../CellMeasurer/CellMeasurer.example';\nimport CollectionExample from '../Collection/Collection.example';\nimport ColumnSizerExample from '../ColumnSizer/ColumnSizer.example';\nimport GridExample from '../Grid/Grid.example';\nimport InfiniteLoaderExample from '../InfiniteLoader/InfiniteLoader.example';\nimport ListExample from '../List/List.example';\nimport MasonryExample from '../Masonry/Masonry.example';\nimport MultiGridExample from '../MultiGrid/MultiGrid.example';\nimport ScrollSyncExample from '../ScrollSync/ScrollSync.example';\nimport TableExample from '../Table/Table.example';\nimport WindowScrollerExample from '../WindowScroller/WindowScroller.example';\n\nconst COMPONENT_EXAMPLES_MAP = {\n  '/components/ArrowKeyStepper': ArrowKeyStepperExample,\n  '/components/AutoSizer': AutoSizerExample,\n  '/components/CellMeasurer': CellMeasurerExample,\n  '/components/Collection': CollectionExample,\n  '/components/ColumnSizer': ColumnSizerExample,\n  '/components/Grid': GridExample,\n  '/components/Masonry': MasonryExample,\n  '/components/InfiniteLoader': InfiniteLoaderExample,\n  '/components/List': ListExample,\n  '/components/MultiGrid': MultiGridExample,\n  '/components/ScrollSync': ScrollSyncExample,\n  '/components/Table': TableExample,\n  '/components/WindowScroller': WindowScrollerExample,\n};\n\n// HACK Generate arbitrary data for use in example components :)\nconst list = Immutable.List(generateRandomList());\n\nexport default class Application extends React.PureComponent {\n  static childContextTypes = {\n    list: PropTypes.instanceOf(Immutable.List).isRequired,\n    customElement: PropTypes.any,\n    isScrollingCustomElement: PropTypes.bool.isRequired,\n    setScrollingCustomElement: PropTypes.func,\n  };\n\n  state = {\n    isScrollingCustomElement: false,\n  };\n\n  constructor(props) {\n    super(props);\n    this.setScrollingCustomElement = this.setScrollingCustomElement.bind(this);\n  }\n\n  setScrollingCustomElement(custom) {\n    this.setState({isScrollingCustomElement: custom});\n  }\n\n  getChildContext() {\n    const {customElement, isScrollingCustomElement} = this.state;\n    return {\n      list,\n      customElement,\n      isScrollingCustomElement,\n      setScrollingCustomElement: this.setScrollingCustomElement,\n    };\n  }\n\n  render() {\n    const {isScrollingCustomElement} = this.state;\n    const bodyStyle = isScrollingCustomElement\n      ? styles.ScrollingBody\n      : styles.Body;\n    return (\n      <HashRouter>\n        <div className={styles.demo}>\n          <div className={styles.headerRow}>\n            <div className={styles.logoRow}>\n              <div className={styles.ReactVirtualizedContainer}>\n                <img\n                  alt=\"React virtualized\"\n                  className={styles.logo}\n                  src=\"https://cloud.githubusercontent.com/assets/29597/11736841/c0497158-9f87-11e5-8dfe-9c0be97d4286.png\"\n                />\n                <div className={styles.PrimaryLogoText}>React</div>\n                <div className={styles.SecondaryLogoText}>Virtualized</div>\n              </div>\n\n              <ul className={styles.NavList}>\n                <NavLink to=\"/components/List\" iconType={TYPES.COMPONENTS}>\n                  Components\n                </NavLink>\n                <NavLink to=\"/wizard\" iconType={TYPES.WIZARD}>\n                  Wizard\n                </NavLink>\n                <NavLink\n                  href=\"https://github.com/bvaughn/react-virtualized\"\n                  iconType={TYPES.SOURCE}>\n                  Source\n                </NavLink>\n                <NavLink\n                  href=\"https://github.com/bvaughn/react-virtualized/tree/master/docs#documentation\"\n                  iconType={TYPES.DOCUMENTATION}>\n                  Documentation\n                </NavLink>\n                <NavLink\n                  href=\"https://github.com/bvaughn/react-virtualized/issues\"\n                  iconType={TYPES.ISSUES}>\n                  Issues\n                </NavLink>\n              </ul>\n            </div>\n\n            <div className={styles.ComponentList}>\n              <ComponentLink to=\"/components/Collection\">\n                Collection\n              </ComponentLink>\n              <ComponentLink to=\"/components/Grid\">Grid</ComponentLink>\n              <ComponentLink to=\"/components/List\">List</ComponentLink>\n              <ComponentLink to=\"/components/Masonry\">Masonry</ComponentLink>\n              <ComponentLink to=\"/components/Table\">Table</ComponentLink>\n            </div>\n\n            <div className={styles.HighOrderComponentList}>\n              <ComponentLink to=\"/components/ArrowKeyStepper\">\n                ArrowKeyStepper\n              </ComponentLink>\n              <ComponentLink to=\"/components/AutoSizer\">\n                AutoSizer\n              </ComponentLink>\n              <ComponentLink to=\"/components/CellMeasurer\">\n                CellMeasurer\n              </ComponentLink>\n              <ComponentLink to=\"/components/ColumnSizer\">\n                ColumnSizer\n              </ComponentLink>\n              <ComponentLink to=\"/components/InfiniteLoader\">\n                InfiniteLoader\n              </ComponentLink>\n              <ComponentLink to=\"/components/MultiGrid\">\n                MultiGrid\n              </ComponentLink>\n              <ComponentLink to=\"/components/ScrollSync\">\n                ScrollSync\n              </ComponentLink>\n              <ComponentLink to=\"/components/WindowScroller\">\n                WindowScroller\n              </ComponentLink>\n            </div>\n          </div>\n\n          <div\n            className={bodyStyle}\n            ref={e => this.setState({customElement: e})}>\n            <div className={styles.column}>\n              <Route path=\"/wizard\" component={Wizard} />\n              {Object.keys(COMPONENT_EXAMPLES_MAP).map(route => (\n                <Route\n                  key={route}\n                  path={route}\n                  component={COMPONENT_EXAMPLES_MAP[route]}\n                />\n              ))}\n              <Route\n                exact\n                path=\"/\"\n                render={() => <Redirect to=\"/components/List\" />}\n              />\n            </div>\n          </div>\n        </div>\n      </HashRouter>\n    );\n  }\n}\n"
  },
  {
    "path": "source/demo/ComponentLink.css",
    "content": ".ComponentLink {\n  display: list-item;\n  background: none;\n  border: none;\n  padding: .6rem;\n  color: #bdbdbd;\n  cursor: pointer;\n  text-transform: uppercase;\n  text-decoration: none;\n  transition: .2s all;\n}\n.ComponentLink:hover {\n  color: #fff;\n}\n.ComponentLink:focus {\n  outline: none;\n  background: rgba(255,255,255,.08);\n}\n.ActiveComponentLink {\n  color: #fff;\n}\n.DisabledComponentLink {\n  pointer-events: none;\n  opacity: 0.5;\n}\n"
  },
  {
    "path": "source/demo/ComponentLink.js",
    "content": "import * as React from 'react';\nimport {NavLink} from 'react-router-dom';\nimport styles from './ComponentLink.css';\n\nexport default function ComponentLink({children, to}) {\n  return (\n    <li className={styles.NavListItem}>\n      <NavLink\n        activeClassName={styles.ActiveComponentLink}\n        className={styles.ComponentLink}\n        to={to}>\n        {children}\n      </NavLink>\n    </li>\n  );\n}\n"
  },
  {
    "path": "source/demo/ContentBox.css",
    "content": ".ContentBox {\n  flex: 1 0 auto;\n  display: flex;\n  flex-direction: column;\n  background-color: #FFF;\n  padding: 0 1rem 1rem 1rem;\n  overflow: auto;\n  background: white;\n}\n.ContentBox:first-of-type {\n  padding-top: 1rem;\n}\n\n.Header {\n  flex: 0 0 auto;\n  display: flex;\n  flex-direction: row;\n  font-weight: normal;\n  justify-content: space-between;\n  margin: 0 0 0.5rem;\n  font-size: 1rem;\n  font-weight: normal;\n}\n.Small {\n  font-size: .65em;\n}\n.Link:hover {\n  text-decoration: none;\n}\n\n.Paragraph {\n  flex: 0 0 auto;\n  line-height: 1.2rem;\n  margin: 0 0 1rem;\n}\n"
  },
  {
    "path": "source/demo/ContentBox.js",
    "content": "import * as React from 'react';\nimport clsx from 'clsx';\nimport styles from './ContentBox.css';\n\nexport function ContentBox({className, children, style}) {\n  return (\n    <div className={clsx(styles.ContentBox, className)} style={style}>\n      {children}\n    </div>\n  );\n}\n\nexport function ContentBoxHeader({text, sourceLink, docsLink}) {\n  const links = [];\n\n  if (sourceLink) {\n    links.push(\n      <a className={styles.Link} href={sourceLink} key=\"sourceLink\">\n        Source\n      </a>,\n    );\n  }\n\n  if (sourceLink && docsLink) {\n    links.push(<span key=\"separator\"> | </span>);\n  }\n\n  if (docsLink) {\n    links.push(\n      <a className={styles.Link} href={docsLink} key=\"docsLink\">\n        Docs\n      </a>,\n    );\n  }\n\n  return (\n    <h1 className={styles.Header}>\n      {text}\n\n      {links.length > 0 && <small className={styles.Small}>{links}</small>}\n    </h1>\n  );\n}\n\nexport function ContentBoxParagraph({children}) {\n  return <div className={styles.Paragraph}>{children}</div>;\n}\n"
  },
  {
    "path": "source/demo/Icon.css",
    "content": ".Icon {\n  fill: currentColor;\n}\n"
  },
  {
    "path": "source/demo/Icon.js",
    "content": "import clsx from 'clsx';\nimport PropTypes from 'prop-types';\nimport * as React from 'react';\nimport styles from './Icon.css';\n\n// TODO: Remove unused BURGER and ARROW_LEFT?\n// TODO: Rotate ARROW_UP to create ARROW_DOWN\nexport const TYPES = {\n  COMPONENTS: 'COMPONENTS',\n  DOCUMENTATION: 'DOCUMENTATION',\n  ISSUES: 'ISSUES',\n  SOURCE: 'SOURCE',\n  WIZARD: 'WIZARD',\n};\n\nIcon.propTypes = {\n  className: PropTypes.string,\n  title: PropTypes.string,\n  type: PropTypes.oneOf(Object.keys(TYPES)).isRequired,\n};\n\nexport default function Icon({className, title, type}) {\n  return (\n    <svg\n      className={clsx(styles.Icon, className)}\n      height={12}\n      title={title}\n      viewBox=\"0 0 24 24\"\n      width={12}>\n      <path d=\"M0 0h24v24H0V0z\" fill=\"none\" />\n\n      {getIconData(type)}\n    </svg>\n  );\n}\n\nexport function getIconData(type) {\n  switch (type) {\n    case TYPES.COMPONENTS:\n      return (\n        <path d=\"M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z\" />\n      );\n    case TYPES.DOCUMENTATION:\n      return (\n        <path d=\"M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z\" />\n      );\n    case TYPES.ISSUES:\n      return (\n        <path d=\"M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z\" />\n      );\n    case TYPES.SOURCE:\n      return (\n        <path d=\"M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z\" />\n      );\n    case TYPES.WIZARD:\n      return <path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\" />;\n    default:\n      throw new Error(`Invalid icon type: ${type}`);\n  }\n}\n"
  },
  {
    "path": "source/demo/LabeledInput.css",
    "content": ".LabeledInput {\n  display: flex;\n  flex-direction: column;\n  flex: 1 1 auto;\n  min-width: 0;\n  margin: 0 10px 5px 0;\n}\n.LabeledInput:last-of-type {\n  margin-right: 0;\n}\n\n.Label {\n  display: inline-block;\n  width: 100%;\n  box-sizing: border-box;\n}\n.Input {\n  appearance: none;\n  border: 0;\n  border-bottom: 1px solid #e0e0e0;\n  padding: 5px 0;\n  font-size: 1em;\n  font-weight: 300;\n  background-color: transparent;\n}\n.Input:focus {\n  outline: 0;\n}\n.Input::placeholder {\n  color: #e0e0e0;\n}\n.Input:disabled {\n  opacity: .5;\n}\n\n.Label {\n  color: #bdbdbd;\n  font-size: .75em;\n  margin-bottom: 5px;\n  font-weight: 100;\n}\n.LabelDisabled {\n  opacity: .5;\n}\n\n.InputRow {\n  flex: 0 0 auto;\n  display: flex;\n  flex-direction: row;\n  align-items: flex-end;\n}\n"
  },
  {
    "path": "source/demo/LabeledInput.js",
    "content": "import PropTypes from 'prop-types';\nimport * as React from 'react';\nimport clsx from 'clsx';\nimport styles from './LabeledInput.css';\n\nexport function LabeledInput({\n  disabled,\n  label,\n  name,\n  onChange,\n  placeholder,\n  value,\n}) {\n  const labelClassName = clsx(styles.Label, {\n    [styles.LabelDisabled]: disabled,\n  });\n\n  return (\n    <div className={styles.LabeledInput}>\n      <label className={labelClassName} title={label}>\n        {label}\n      </label>\n      <input\n        aria-label={label}\n        className={styles.Input}\n        name={name}\n        placeholder={placeholder}\n        onChange={onChange}\n        value={value}\n        disabled={disabled}\n      />\n    </div>\n  );\n}\nLabeledInput.propTypes = {\n  disabled: PropTypes.bool,\n  label: PropTypes.string.isRequired,\n  name: PropTypes.string.isRequired,\n  onChange: PropTypes.func.isRequired,\n  placeholder: PropTypes.string,\n  value: PropTypes.any,\n};\n\nexport function InputRow({children}) {\n  return <div className={styles.InputRow}>{children}</div>;\n}\n"
  },
  {
    "path": "source/demo/NavLink.css",
    "content": ".NavListItem {\n  display: inline-flex;\n  flex-direction: row;\n  align-items: center;\n}\n\n@keyframes spin {\n  from {\n    transform: rotate(0deg);\n  }\n  to { \n    transform: rotate(360deg);\n  }\n}\n\n.NavLink {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  padding: 0.5rem;\n  color: rgba(255, 255, 255, 0.8);\n  text-transform: lowercase;\n  text-decoration: none;\n  text-transform: uppercase;\n  cursor: pointer;\n}\n.ActiveNavLink,\n.NavLink:hover {\n  color: #FFF;\n}\n\n.Icon {\n  margin-right: 0.25rem;\n  color: #fff;\n}\n"
  },
  {
    "path": "source/demo/NavLink.js",
    "content": "import * as React from 'react';\nimport {NavLink as RRNavLink} from 'react-router-dom';\nimport Icon from './Icon';\nimport styles from './NavLink.css';\n\nexport default function NavLink({children, href, iconType, to}) {\n  let link;\n  let icon;\n\n  if (iconType) {\n    icon = <Icon className={styles.Icon} type={iconType} />;\n  }\n\n  if (to) {\n    link = (\n      <RRNavLink\n        activeClassName={styles.ActiveNavLink}\n        className={styles.NavLink}\n        to={to}>\n        {icon} {children}\n      </RRNavLink>\n    );\n  } else {\n    link = (\n      <a className={styles.NavLink} href={href}>\n        {icon} {children}\n      </a>\n    );\n  }\n\n  return <li className={styles.NavListItem}>{link}</li>;\n}\n"
  },
  {
    "path": "source/demo/Wizard/Generator.js",
    "content": "export default function Generator({\n  cellsHaveKnownHeight,\n  cellsHaveKnownWidth,\n  cellsHaveUniformHeight,\n  cellsHaveUniformWidth,\n  collectionHasFixedHeight,\n  collectionHasFixedWidth,\n  doNotVirtualizeColumns,\n  hasMultipleColumns,\n  hasMultipleRows,\n  nonCheckerboardPattern,\n}) {\n  if (!hasMultipleColumns && !hasMultipleRows) {\n    return \"<div>Looks like you don't need react-virtualized.</div>\";\n  }\n\n  const baseComponent = getBaseComponent({\n    doNotVirtualizeColumns,\n    hasMultipleColumns,\n    nonCheckerboardPattern,\n  });\n  const useAutoSizer = !collectionHasFixedHeight || !collectionHasFixedWidth;\n  const useCellMeasurer =\n    !doNotVirtualizeColumns &&\n    !nonCheckerboardPattern &&\n    (!cellsHaveKnownHeight || !cellsHaveKnownWidth);\n\n  // TODO Share these with CellMeasurer?\n  const heightValue = collectionHasFixedHeight ? 600 : 'height';\n  const widthValue = collectionHasFixedWidth ? 800 : 'width';\n\n  baseComponent.props.height = heightValue;\n  baseComponent.props.width = widthValue;\n\n  if (baseComponent.columnWidthProp) {\n    baseComponent.props[baseComponent.columnWidthProp] = hasMultipleColumns\n      ? cellsHaveKnownWidth\n        ? cellsHaveUniformWidth\n          ? 100\n          : '({ index }) => 100'\n        : 'getColumnWidth'\n      : widthValue;\n  }\n  if (baseComponent.rowHeightProp) {\n    baseComponent.props[baseComponent.rowHeightProp] = hasMultipleRows\n      ? cellsHaveKnownHeight\n        ? cellsHaveUniformHeight\n          ? 50\n          : '({ index }) => 50'\n        : 'getRowHeight'\n      : heightValue;\n  }\n\n  if (baseComponent.columnCountProp) {\n    baseComponent.props[baseComponent.columnCountProp] = hasMultipleColumns\n      ? 'numColumns'\n      : '1';\n  }\n  if (baseComponent.rowCountProp) {\n    baseComponent.props[baseComponent.rowCountProp] = hasMultipleRows\n      ? hasMultipleColumns\n        ? 'numRows'\n        : 'collection.size'\n      : '1';\n  }\n\n  let component = baseComponent;\n\n  if (useCellMeasurer) {\n    component = getCellMeasurer({\n      cellsHaveKnownHeight,\n      collectionHasFixedHeight,\n      collectionHasFixedWidth,\n      child: component,\n      indentation: useAutoSizer ? 4 : 0,\n    });\n  }\n\n  if (useAutoSizer) {\n    component = getAutoSizer({\n      child: component,\n    });\n  }\n\n  return componentToString({component});\n}\n\nfunction componentToString({component, indentation = 0}) {\n  const spaces = indentationToSpaces(indentation);\n  const hasProps = Object.keys(component.props).length > 0;\n  const markup = [];\n\n  if (hasProps) {\n    markup.push(`${spaces}<${component.name}`);\n  } else {\n    markup.push(`${spaces}<${component.name}>`);\n  }\n\n  if (hasProps) {\n    markup.push(\n      Object.keys(component.props)\n        .sort()\n        .map(key => `${spaces}  ${key}={${component.props[key]}}`)\n        .join(`\\n`),\n    );\n  }\n\n  if (component.children) {\n    if (hasProps) {\n      markup.push(`${spaces}>`);\n    }\n\n    markup.push(component.children);\n  }\n\n  if (component.children) {\n    markup.push(`${spaces}</${component.name}>`);\n  } else if (hasProps) {\n    markup.push(`${spaces}/>`);\n  }\n\n  return markup.join(`\\n`);\n}\n\nfunction getAutoSizer({\n  child,\n  collectionHasFixedHeight,\n  collectionHasFixedWidth,\n}) {\n  const props = {};\n\n  if (collectionHasFixedHeight) {\n    props.disableHeight = true;\n  }\n  if (collectionHasFixedWidth) {\n    props.disableWidth = true;\n  }\n\n  let methodSignature = '{({ height, width })';\n  if (collectionHasFixedWidth) {\n    methodSignature = '{({ height })';\n  } else if (collectionHasFixedHeight) {\n    methodSignature = '{({ width })';\n  }\n\n  const children = [];\n  children.push(`  ${methodSignature} => (`);\n  children.push(\n    componentToString({\n      component: child,\n      indentation: 4,\n    }),\n  );\n  children.push('  )}');\n\n  return {\n    name: 'AutoSizer',\n    props,\n    children: children.join(`\\n`),\n  };\n}\n\nfunction getBaseComponent({\n  doNotVirtualizeColumns,\n  hasMultipleColumns,\n  nonCheckerboardPattern,\n}) {\n  if (nonCheckerboardPattern) {\n    return getCollectionMarkup();\n  } else if (!hasMultipleColumns) {\n    return getListMarkup();\n  } else if (doNotVirtualizeColumns) {\n    return getTableMarkup();\n  } else {\n    return getGridMarkup();\n  }\n}\n\nfunction getCellMeasurer({\n  cellsHaveKnownHeight,\n  collectionHasFixedHeight,\n  collectionHasFixedWidth,\n  child,\n  indentation,\n}) {\n  const spaces = indentationToSpaces(indentation);\n\n  // TODO Share these with render()?\n  const heightValue = collectionHasFixedHeight ? 600 : 'height';\n  const widthValue = collectionHasFixedWidth ? 800 : 'width';\n\n  const props = {\n    cellRenderer: 'yourCellRenderer', // @TODO pass down?\n    columnCount: 'numColumns',\n    rowCount: 'numRows',\n  };\n  let methodSignature;\n\n  // @TODO CellMeasurer doesn't support both dynamic widths and heights. Warn about this.\n  if (cellsHaveKnownHeight) {\n    props.height = heightValue;\n    methodSignature = '{({ getColumnWidth })';\n  } else {\n    props.width = widthValue;\n    methodSignature = '{({ getRowHeight })';\n  }\n\n  const children = [];\n  children.push(`${spaces}  ${methodSignature} => (`);\n  children.push(\n    componentToString({\n      component: child,\n      indentation: indentation + 4,\n    }),\n  );\n  children.push(`${spaces}  )}`);\n\n  return {\n    name: 'CellMeasurer',\n    props,\n    children: children.join(`\\n`),\n  };\n}\n\nfunction getCollectionMarkup() {\n  return {\n    name: 'Collection',\n    props: {\n      cellCount: 'collection.size',\n      cellRenderer: '({ index }) => collection.getIn([index, \"name\"])',\n      cellSizeAndPositionGetter:\n        '({ index, isScrolling  }) => ({ height, width, x, y })',\n    },\n  };\n}\n\nfunction getTableMarkup() {\n  return {\n    name: 'Table',\n    props: {\n      headerHeight: 30,\n      rowGetter: '({ index }) => collection.get(index)',\n    },\n    rowCountProp: 'rowCount',\n    rowHeightProp: 'rowHeight',\n    children: '<!-- Insert Column children here -->', // @TODO\n  };\n}\n\nfunction getGridMarkup() {\n  return {\n    name: 'Grid',\n    props: {\n      cellRenderer:\n        '({ columnIndex, key, rowIndex, style }) => <div key={key} style={style}>...</div>',\n    },\n    columnCountProp: 'columnCount',\n    columnWidthProp: 'columnWidth',\n    rowCountProp: 'rowCount',\n    rowHeightProp: 'rowHeight',\n  };\n}\n\nfunction getListMarkup() {\n  return {\n    name: 'List',\n    props: {\n      rowRenderer:\n        '({ index, key, style }) => <div key={key} style={style}>...</div>',\n    },\n    rowHeightProp: 'rowHeight',\n  };\n}\n\nfunction indentationToSpaces(indentation) {\n  return Array.from(Array(indentation))\n    .map(() => ' ')\n    .join('');\n}\n"
  },
  {
    "path": "source/demo/Wizard/Wizard.css",
    "content": ".Wrapper {\n  flex: 1;\n}\n\n.Option {\n  max-width: 100%;\n  padding: 0.25rem 0;\n}\n\n.OptionDisabled {\n  opacity: .5;\n}\n\n.Label,\n.Input {\n  font-size: 1em;\n}\n\n:global(.CodeMirror) {\n  height: auto;\n  font-size: 0.75rem;\n}\n"
  },
  {
    "path": "source/demo/Wizard/Wizard.js",
    "content": "import clsx from 'clsx';\nimport CodeMirror from 'react-codemirror';\nimport * as React from 'react';\nimport {ContentBox, ContentBoxHeader} from '../ContentBox';\nimport generate from './Generator';\nimport styles from './Wizard.css';\n\nrequire('codemirror/mode/jsx/jsx');\n\nconst codeMirrorOptions = {\n  mode: 'jsx',\n  theme: 'dracula',\n};\n\n// @TODO Clean up this class; it's pretty hacky.\nexport default class Wizard extends React.Component {\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      key: 0,\n      cellsHaveKnownHeight: true,\n      cellsHaveKnownWidth: true,\n      cellsHaveUniformHeight: true,\n      cellsHaveUniformWidth: true,\n      collectionHasFixedHeight: false,\n      collectionHasFixedWidth: false,\n      doNotVirtualizeColumns: false,\n      hasMultipleColumns: true,\n      hasMultipleRows: true,\n      nonCheckerboardPattern: false,\n    };\n  }\n\n  // TODO Remove this key hack once JedWatson/react-codemirror/issues/106 is fixed\n  updateState = obj => this.setState(state => ({...obj, key: state.key + 1}));\n\n  render() {\n    const state = this._sanitizeState();\n    const markup = generate(state);\n\n    const {\n      cellsHaveKnownHeight,\n      cellsHaveKnownWidth,\n      cellsHaveUniformHeight,\n      cellsHaveUniformWidth,\n      collectionHasFixedHeight,\n      collectionHasFixedWidth,\n      doNotVirtualizeColumns,\n      hasMultipleColumns,\n      hasMultipleRows,\n      nonCheckerboardPattern,\n    } = state;\n\n    return (\n      <div className={styles.Wrapper}>\n        <ContentBox>\n          <ContentBoxHeader text=\"Collection Layout and Sizing\" />\n          <Option\n            checked={hasMultipleRows}\n            label=\"Will your collection have more than 1 row of data?\"\n            onChange={hasMultipleRows => this.updateState({hasMultipleRows})}\n          />\n          <Option\n            checked={hasMultipleColumns}\n            label=\"Will your collection have more than 1 column of data?\"\n            onChange={hasMultipleColumns =>\n              this.updateState({hasMultipleColumns})\n            }\n          />\n          <Option\n            checked={doNotVirtualizeColumns}\n            disabled={!hasMultipleColumns}\n            label=\"Should all your columns be visible at once?\"\n            onChange={doNotVirtualizeColumns =>\n              this.updateState({doNotVirtualizeColumns})\n            }\n          />\n          <Option\n            checked={nonCheckerboardPattern}\n            disabled={!hasMultipleColumns || !hasMultipleRows}\n            label=\"Is your data scattered (not in a checkerboard pattern)?\"\n            onChange={nonCheckerboardPattern =>\n              this.updateState({nonCheckerboardPattern})\n            }\n          />\n          <Option\n            disabled={!hasMultipleRows && !hasMultipleColumns}\n            checked={collectionHasFixedHeight}\n            label=\"Does your collection have a fixed height?\"\n            onChange={collectionHasFixedHeight =>\n              this.updateState({collectionHasFixedHeight})\n            }\n          />\n          <Option\n            disabled={!hasMultipleRows && !hasMultipleColumns}\n            checked={collectionHasFixedWidth}\n            label=\"Does your collection have a fixed width?\"\n            onChange={collectionHasFixedWidth =>\n              this.updateState({collectionHasFixedWidth})\n            }\n          />\n        </ContentBox>\n        <ContentBox>\n          <ContentBoxHeader text=\"Cell Sizing\" />\n          <Option\n            disabled={\n              nonCheckerboardPattern ||\n              doNotVirtualizeColumns ||\n              !cellsHaveKnownWidth ||\n              (!hasMultipleRows && !hasMultipleColumns)\n            }\n            checked={\n              cellsHaveKnownHeight &&\n              !nonCheckerboardPattern &&\n              !doNotVirtualizeColumns\n            }\n            label=\"Do you know the height of your rows ahead of time?\"\n            onChange={cellsHaveKnownHeight =>\n              this.updateState({cellsHaveKnownHeight})\n            }\n          />\n          <Option\n            disabled={\n              nonCheckerboardPattern ||\n              doNotVirtualizeColumns ||\n              !cellsHaveKnownHeight ||\n              (!hasMultipleRows && !hasMultipleColumns)\n            }\n            checked={\n              cellsHaveKnownWidth &&\n              !nonCheckerboardPattern &&\n              !doNotVirtualizeColumns\n            }\n            label=\"Do you know the width of your columns ahead of time?\"\n            onChange={cellsHaveKnownWidth =>\n              this.updateState({cellsHaveKnownWidth})\n            }\n          />\n          <Option\n            checked={cellsHaveUniformHeight}\n            disabled={\n              !hasMultipleRows ||\n              nonCheckerboardPattern ||\n              !cellsHaveKnownHeight\n            }\n            label=\"Are all of your rows the same height?\"\n            onChange={cellsHaveUniformHeight =>\n              this.updateState({cellsHaveUniformHeight})\n            }\n          />\n          <Option\n            checked={cellsHaveUniformWidth}\n            disabled={\n              !hasMultipleColumns ||\n              nonCheckerboardPattern ||\n              !cellsHaveKnownWidth\n            }\n            label=\"Are all of your columns the same width?\"\n            onChange={cellsHaveUniformWidth =>\n              this.updateState({cellsHaveUniformWidth})\n            }\n          />\n        </ContentBox>\n        <ContentBox>\n          <ContentBoxHeader text=\"Suggested Starting Point\" />\n          <CodeMirror\n            options={codeMirrorOptions}\n            value={markup}\n            key={this.state.key}\n          />\n        </ContentBox>\n      </div>\n    );\n  }\n\n  _sanitizeState() {\n    const {\n      cellsHaveKnownHeight,\n      cellsHaveKnownWidth,\n      cellsHaveUniformHeight,\n      cellsHaveUniformWidth,\n      collectionHasFixedHeight,\n      collectionHasFixedWidth,\n      doNotVirtualizeColumns,\n      hasMultipleColumns,\n      hasMultipleRows,\n      nonCheckerboardPattern,\n    } = this.state;\n\n    return {\n      cellsHaveKnownHeight,\n      cellsHaveKnownWidth,\n      cellsHaveUniformHeight:\n        cellsHaveUniformHeight &&\n        hasMultipleRows &&\n        !nonCheckerboardPattern &&\n        cellsHaveKnownHeight,\n      cellsHaveUniformWidth:\n        cellsHaveUniformWidth &&\n        hasMultipleColumns &&\n        !nonCheckerboardPattern &&\n        cellsHaveKnownWidth,\n      collectionHasFixedHeight,\n      collectionHasFixedWidth,\n      doNotVirtualizeColumns: doNotVirtualizeColumns && hasMultipleColumns,\n      hasMultipleColumns,\n      hasMultipleRows,\n      nonCheckerboardPattern:\n        nonCheckerboardPattern && hasMultipleColumns && hasMultipleRows,\n    };\n  }\n}\n\nfunction Option({checked, disabled = false, label, onChange}) {\n  return (\n    <div\n      className={clsx(styles.Option, {\n        [styles.OptionDisabled]: disabled,\n      })}>\n      <label className={styles.Label}>\n        <input\n          checked={checked}\n          className={styles.Input}\n          disabled={disabled}\n          onChange={event => onChange(event.target.checked)}\n          type=\"checkbox\"\n        />\n        {label}\n      </label>\n    </div>\n  );\n}\n"
  },
  {
    "path": "source/demo/Wizard/index.js",
    "content": "/** @flow */\nimport Wizard from './Wizard';\n\nexport default Wizard;\nexport {Wizard};\n"
  },
  {
    "path": "source/demo/index.js",
    "content": "// IE 10+ compatibility for demo (must come before other imports)\nimport '@babel/polyfill';\n\n// Import react-virtualized styles as part of bootstrap process\nimport '../styles.css';\n\nimport * as React from 'react';\nimport {render} from 'react-dom';\n\nimport Application from './Application';\n\nrender(<Application />, document.getElementById('root'));\n\n// Import and attach the favicon\ndocument.querySelector(\n  '[rel=\"shortcut icon\"]',\n).href = require('file-loader!./favicon.png');\n"
  },
  {
    "path": "source/demo/utils.js",
    "content": "/**\n * Generate random data for use in examples.\n */\nexport function generateRandomList() {\n  const list = [];\n\n  for (var i = 0; i < 1000; i++) {\n    const random = loremIpsum[i % loremIpsum.length];\n    const randoms = [random];\n\n    for (let j = Math.round(Math.random() * 10); j--; ) {\n      randoms.push(loremIpsum[(i * j) % loremIpsum.length]);\n    }\n\n    list.push({\n      color: BADGE_COLORS[i % BADGE_COLORS.length],\n      index: i,\n      name: NAMES[i % NAMES.length],\n      random,\n      randomLong: randoms.join(' '),\n      size: ROW_HEIGHTS[Math.floor(Math.random() * ROW_HEIGHTS.length)],\n    });\n  }\n\n  return list;\n}\n\nconst BADGE_COLORS = [\n  '#f44336',\n  '#3f51b5',\n  '#4caf50',\n  '#ff9800',\n  '#2196f3',\n  '#374046',\n  '#cddc39',\n  '#2196f3',\n  '#9c27b0',\n  '#ffc107',\n  '#009688',\n  '#673ab7',\n  '#ffeb3b',\n  '#cddc39',\n  '#795548',\n];\nconst NAMES = [\n  'Peter Brimer',\n  'Tera Gaona',\n  'Kandy Liston',\n  'Lonna Wrede',\n  'Kristie Yard',\n  'Raul Host',\n  'Yukiko Binger',\n  'Velvet Natera',\n  'Donette Ponton',\n  'Loraine Grim',\n  'Shyla Mable',\n  'Marhta Sing',\n  'Alene Munden',\n  'Holley Pagel',\n  'Randell Tolman',\n  'Wilfred Juneau',\n  'Naida Madson',\n  'Marine Amison',\n  'Glinda Palazzo',\n  'Lupe Island',\n  'Cordelia Trotta',\n  'Samara Berrier',\n  'Era Stepp',\n  'Malka Spradlin',\n  'Edward Haner',\n  'Clemencia Feather',\n  'Loretta Rasnake',\n  'Dana Hasbrouck',\n  'Sanda Nery',\n  'Soo Reiling',\n  'Apolonia Volk',\n  'Liliana Cacho',\n  'Angel Couchman',\n  'Yvonne Adam',\n  'Jonas Curci',\n  'Tran Cesar',\n  'Buddy Panos',\n  'Rosita Ells',\n  'Rosalind Tavares',\n  'Renae Keehn',\n  'Deandrea Bester',\n  'Kelvin Lemmon',\n  'Guadalupe Mccullar',\n  'Zelma Mayers',\n  'Laurel Stcyr',\n  'Edyth Everette',\n  'Marylin Shevlin',\n  'Hsiu Blackwelder',\n  'Mark Ferguson',\n  'Winford Noggle',\n  'Shizuko Gilchrist',\n  'Roslyn Cress',\n  'Nilsa Lesniak',\n  'Agustin Grant',\n  'Earlie Jester',\n  'Libby Daigle',\n  'Shanna Maloy',\n  'Brendan Wilken',\n  'Windy Knittel',\n  'Alice Curren',\n  'Eden Lumsden',\n  'Klara Morfin',\n  'Sherryl Noack',\n  'Gala Munsey',\n  'Stephani Frew',\n  'Twana Anthony',\n  'Mauro Matlock',\n  'Claudie Meisner',\n  'Adrienne Petrarca',\n  'Pearlene Shurtleff',\n  'Rachelle Piro',\n  'Louis Cocco',\n  'Susann Mcsweeney',\n  'Mandi Kempker',\n  'Ola Moller',\n  'Leif Mcgahan',\n  'Tisha Wurster',\n  'Hector Pinkett',\n  'Benita Jemison',\n  'Kaley Findley',\n  'Jim Torkelson',\n  'Freda Okafor',\n  'Rafaela Markert',\n  'Stasia Carwile',\n  'Evia Kahler',\n  'Rocky Almon',\n  'Sonja Beals',\n  'Dee Fomby',\n  'Damon Eatman',\n  'Alma Grieve',\n  'Linsey Bollig',\n  'Stefan Cloninger',\n  'Giovanna Blind',\n  'Myrtis Remy',\n  'Marguerita Dostal',\n  'Junior Baranowski',\n  'Allene Seto',\n  'Margery Caves',\n  'Nelly Moudy',\n  'Felix Sailer',\n];\nconst ROW_HEIGHTS = [50, 75, 100];\n\nconst loremIpsum = [\n  'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',\n  'Phasellus vulputate odio commodo tortor sodales, et vehicula ipsum viverra.',\n  'In et mollis velit, accumsan volutpat libero.',\n  'Nulla rutrum tellus ipsum, eget fermentum sem dictum quis.',\n  'Suspendisse eget vehicula elit.',\n  'Proin ut lacus lacus.',\n  'Aliquam erat volutpat.',\n  'Vivamus ac suscipit est, et elementum lectus.',\n  'Cras tincidunt nisi in urna molestie varius.',\n  'Integer in magna eu nibh imperdiet tristique.',\n  'Curabitur eu pellentesque nisl.',\n  'Etiam non consequat est.',\n  'Duis mi massa, feugiat nec molestie sit amet, suscipit et metus.',\n  'Curabitur ac enim dictum arcu varius fermentum vel sodales dui.',\n  'Ut tristique augue at congue molestie.',\n  'Integer semper sem lorem, scelerisque suscipit lacus consequat nec.',\n  'Etiam euismod efficitur magna nec dignissim.',\n  'Morbi vel neque lectus.',\n  'Etiam ac accumsan elit, et pharetra ex.',\n  'Suspendisse vitae gravida mauris.',\n  'Pellentesque sed laoreet erat.',\n  'Nam aliquet purus quis massa eleifend, et efficitur felis aliquam.',\n  'Fusce faucibus diam erat, sed consectetur urna auctor at.',\n  'Praesent et nulla velit.',\n  'Cras eget enim nec odio feugiat tristique eu quis ante.',\n  'Morbi blandit diam vitae odio sollicitudin finibus.',\n  'Integer ac ante fermentum, placerat orci vel, fermentum lacus.',\n  'Maecenas est elit, semper ut posuere et, congue ut orci.',\n  'Phasellus eget enim vitae nunc luctus sodales a eu erat.',\n  'Curabitur dapibus nisi sed nisi dictum, in imperdiet urna posuere.',\n  'Vivamus commodo odio metus, tincidunt facilisis augue dictum quis.',\n  'Curabitur sagittis a lectus ac sodales.',\n  'Nam eget eros purus.',\n  'Nam scelerisque et ante in porta.',\n  'Proin vitae augue tristique, malesuada nisl ut, fermentum nisl.',\n  'Nulla bibendum quam id velit blandit dictum.',\n  'Cras tempus ac dolor ut convallis.',\n  'Sed vel ipsum est.',\n  'Nulla ut leo vestibulum, ultricies sapien ac, pellentesque dolor.',\n  'Etiam ultricies maximus tempus.',\n  'Donec dignissim mi ac libero feugiat, vitae lacinia odio viverra.',\n  'Curabitur condimentum tellus sit amet neque posuere, condimentum tempus purus eleifend.',\n  'Donec tempus, augue id hendrerit pretium, mauris leo congue nulla, ac iaculis erat nunc in dolor.',\n  'Praesent vel lectus venenatis, elementum mauris vitae, ullamcorper nulla.',\n  'Maecenas non diam cursus, imperdiet massa eget, pellentesque ex.',\n  'Vestibulum luctus risus vel augue auctor blandit.',\n  'Nullam augue diam, pulvinar sed sapien et, hendrerit venenatis risus.',\n  'Quisque sollicitudin nulla nec tellus feugiat hendrerit.',\n  'Vestibulum a eros accumsan, lacinia eros non, pretium diam.',\n  'Aenean iaculis augue sit amet scelerisque aliquam.',\n  'Donec ornare felis et dui hendrerit, eget bibendum nibh interdum.',\n  'Maecenas tellus magna, tristique vitae orci vel, auctor tincidunt nisi.',\n  'Fusce non libero quis velit porttitor maximus at eget enim.',\n  'Sed in aliquet tellus.',\n  'Etiam a tortor erat.',\n  'Donec nec diam vel tellus egestas lobortis.',\n  'Vivamus dictum erat nulla, sit amet accumsan dolor scelerisque eu.',\n  'In nec eleifend ex, pellentesque dapibus sapien.',\n  'Duis a mollis nisi.',\n  'Sed ornare nisl sit amet dolor pellentesque, eu fermentum leo interdum.',\n  'Sed eget mauris condimentum, molestie justo eu, feugiat felis.',\n  'Nunc suscipit leo non dui blandit, ac malesuada ex consequat.',\n  'Morbi varius placerat congue.',\n  'Praesent id velit in nunc elementum aliquet.',\n  'Sed luctus justo vitae nibh bibendum blandit.',\n  'Sed et sapien turpis.',\n  'Nulla ac eros vestibulum, mollis ante eu, rutrum nulla.',\n  'Sed cursus magna ut vehicula rutrum.',\n  'Ut consectetur feugiat consectetur.',\n  'Nulla nec ligula posuere neque sollicitudin rutrum a a dui.',\n  'Nulla ut quam odio.',\n  'Integer dignissim sapien et orci sodales volutpat.',\n  'Nullam a sapien leo.',\n  'Praesent cursus semper purus, vitae gravida risus dapibus mattis.',\n  'Sed pellentesque nulla lorem, in commodo arcu feugiat sed.',\n  'Phasellus blandit arcu non diam varius ornare.',\n];\n"
  },
  {
    "path": "source/index.js",
    "content": "export {ArrowKeyStepper} from './ArrowKeyStepper';\nexport {AutoSizer} from './AutoSizer';\nexport {CellMeasurer, CellMeasurerCache} from './CellMeasurer';\nexport {Collection} from './Collection';\nexport {ColumnSizer} from './ColumnSizer';\nexport {\n  accessibilityOverscanIndicesGetter,\n  defaultCellRangeRenderer,\n  defaultOverscanIndicesGetter,\n  Grid,\n} from './Grid';\nexport {InfiniteLoader} from './InfiniteLoader';\nexport {List} from './List';\nexport {\n  createCellPositioner as createMasonryCellPositioner,\n  Masonry,\n} from './Masonry';\nexport {MultiGrid} from './MultiGrid';\nexport {ScrollSync} from './ScrollSync';\nexport {\n  createMultiSort as createTableMultiSort,\n  defaultCellDataGetter as defaultTableCellDataGetter,\n  defaultCellRenderer as defaultTableCellRenderer,\n  defaultHeaderRenderer as defaultTableHeaderRenderer,\n  defaultHeaderRowRenderer as defaultTableHeaderRowRenderer,\n  defaultRowRenderer as defaultTableRowRenderer,\n  Table,\n  Column,\n  SortDirection,\n  SortIndicator,\n} from './Table';\nexport {WindowScroller} from './WindowScroller';\n"
  },
  {
    "path": "source/jest-setup.js",
    "content": "jest.mock('dom-helpers/scrollbarSize', () => {\n  return function getScrollbarSize() {\n    return 20;\n  };\n});\n"
  },
  {
    "path": "source/utils/TestHelper.js",
    "content": "import initCellMetadata from './initCellMetadata';\n\n// Default cell sizes and offsets for use in below tests\nexport function getCellMetadata() {\n  const cellSizes = [\n    10, // 0: 0..0 (min)\n    20, // 1: 0..10\n    15, // 2: 0..30\n    10, // 3: 5..45\n    15, // 4: 20..55\n    30, // 5: 50..70\n    20, // 6: 70..100\n    10, // 7: 80..110\n    30, //  8: 110..110 (max)\n  ];\n  return initCellMetadata({\n    cellCount: cellSizes.length,\n    size: ({index}) => cellSizes[index],\n  });\n}\n"
  },
  {
    "path": "source/utils/animationFrame.js",
    "content": "/** @flow */\n\ntype Callback = (timestamp: number) => void;\ntype CancelAnimationFrame = (requestId: number) => void;\ntype RequestAnimationFrame = (callback: Callback) => number;\n\n// Properly handle server-side rendering.\nlet win;\nif (typeof window !== 'undefined') {\n  win = window;\n} else if (typeof self !== 'undefined') {\n  win = self;\n} else {\n  win = {};\n}\n\n// requestAnimationFrame() shim by Paul Irish\n// http://paulirish.com/2011/requestanimationframe-for-smart-animating/\nconst request =\n  win.requestAnimationFrame ||\n  win.webkitRequestAnimationFrame ||\n  win.mozRequestAnimationFrame ||\n  win.oRequestAnimationFrame ||\n  win.msRequestAnimationFrame ||\n  function(callback: Callback): RequestAnimationFrame {\n    return (win: any).setTimeout(callback, 1000 / 60);\n  };\n\nconst cancel =\n  win.cancelAnimationFrame ||\n  win.webkitCancelAnimationFrame ||\n  win.mozCancelAnimationFrame ||\n  win.oCancelAnimationFrame ||\n  win.msCancelAnimationFrame ||\n  function(id: number) {\n    (win: any).clearTimeout(id);\n  };\n\nexport const raf: RequestAnimationFrame = (request: any);\nexport const caf: CancelAnimationFrame = (cancel: any);\n"
  },
  {
    "path": "source/utils/createCallbackMemoizer.jest.js",
    "content": "import createCallbackMemoizer from './createCallbackMemoizer';\n\ndescribe('createCallbackMemoizer', () => {\n  function OnRowsRendered() {\n    let numCalls = 0;\n    let overscanStartIndex;\n    let overscanStopIndex;\n    let startIndex;\n    let stopIndex;\n\n    return {\n      numCalls: () => numCalls,\n      overscanStartIndex: () => overscanStartIndex,\n      overscanStopIndex: () => overscanStopIndex,\n      startIndex: () => startIndex,\n      stopIndex: () => stopIndex,\n      update: params => {\n        overscanStartIndex = params.overscanStartIndex;\n        overscanStopIndex = params.overscanStopIndex;\n        startIndex = params.startIndex;\n        stopIndex = params.stopIndex;\n        numCalls++;\n      },\n    };\n  }\n\n  it('should not call onRowsRendered if startIndex or stopIndex are invalid', () => {\n    const util = new OnRowsRendered();\n    const helper = createCallbackMemoizer();\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: 0,\n        stopIndex: undefined,\n      },\n    });\n    expect(util.numCalls()).toEqual(0);\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: undefined,\n        stopIndex: 0,\n      },\n    });\n    expect(util.numCalls()).toEqual(0);\n  });\n\n  it('should call onRowsRendered if startIndex and stopIndex are valid', () => {\n    const util = new OnRowsRendered();\n    const helper = createCallbackMemoizer();\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: 0,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(1);\n    expect(util.startIndex()).toEqual(0);\n    expect(util.stopIndex()).toEqual(1);\n  });\n\n  it('should call onRowsRendered if startIndex and stopIndex are invalid but :requireAllKeys is false', () => {\n    const util = new OnRowsRendered();\n    const helper = createCallbackMemoizer(false);\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: undefined,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(1);\n    expect(util.startIndex()).toEqual(undefined);\n    expect(util.stopIndex()).toEqual(1);\n  });\n\n  it('should not call onRowsRendered if startIndex or stopIndex have not changed', () => {\n    const util = new OnRowsRendered();\n    const helper = createCallbackMemoizer();\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: 0,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(1);\n    expect(util.startIndex()).toEqual(0);\n    expect(util.stopIndex()).toEqual(1);\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: 0,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(1);\n  });\n\n  it('should not call onRowsRendered if startIndex or stopIndex have changed', () => {\n    const util = new OnRowsRendered();\n    const helper = createCallbackMemoizer();\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: 0,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(1);\n    expect(util.startIndex()).toEqual(0);\n    expect(util.stopIndex()).toEqual(1);\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: 1,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(2);\n    expect(util.startIndex()).toEqual(1);\n    expect(util.stopIndex()).toEqual(1);\n    helper({\n      callback: util.update,\n      indices: {\n        startIndex: 1,\n        stopIndex: 2,\n      },\n    });\n    expect(util.numCalls()).toEqual(3);\n    expect(util.startIndex()).toEqual(1);\n    expect(util.stopIndex()).toEqual(2);\n  });\n\n  it('should call onRowsRendered if :overscanCellsCount changes', () => {\n    const util = new OnRowsRendered();\n    const helper = createCallbackMemoizer();\n    helper({\n      callback: util.update,\n      indices: {\n        overscanStartIndex: 0,\n        overscanStopIndex: 2,\n        startIndex: 0,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(1);\n    expect(util.startIndex()).toEqual(0);\n    expect(util.stopIndex()).toEqual(1);\n    expect(util.overscanStartIndex()).toEqual(0);\n    expect(util.overscanStopIndex()).toEqual(2);\n    helper({\n      callback: util.update,\n      indices: {\n        overscanStartIndex: 0,\n        overscanStopIndex: 3,\n        startIndex: 0,\n        stopIndex: 1,\n      },\n    });\n    expect(util.numCalls()).toEqual(2);\n    expect(util.startIndex()).toEqual(0);\n    expect(util.stopIndex()).toEqual(1);\n    expect(util.overscanStartIndex()).toEqual(0);\n    expect(util.overscanStopIndex()).toEqual(3);\n  });\n\n  it('should support an array of indices', () => {\n    let numCalls = 0;\n    let indices;\n    const callback = params => {\n      indices = params;\n      numCalls++;\n    };\n    const helper = createCallbackMemoizer();\n    helper({\n      callback,\n      indices: [0, 1, 2],\n    });\n    expect(numCalls).toEqual(1);\n    expect(indices).toEqual([0, 1, 2]);\n    helper({\n      callback,\n      indices: [0, 1],\n    });\n    expect(numCalls).toEqual(2);\n    expect(indices).toEqual([0, 1]);\n  });\n\n  it('should support an attribute containing an array of indices', () => {\n    let numCalls = 0;\n    let indices;\n    const callback = params => {\n      indices = params.indices;\n      numCalls++;\n    };\n    const helper = createCallbackMemoizer();\n    helper({\n      callback,\n      indices: {\n        indices: [0, 1, 2],\n      },\n    });\n    expect(numCalls).toEqual(1);\n    expect(indices).toEqual([0, 1, 2]);\n    helper({\n      callback,\n      indices: {\n        indices: [0, 1],\n      },\n    });\n    expect(numCalls).toEqual(2);\n    expect(indices).toEqual([0, 1]);\n  });\n});\n"
  },
  {
    "path": "source/utils/createCallbackMemoizer.js",
    "content": "/**\n * Helper utility that updates the specified callback whenever any of the specified indices have changed.\n */\nexport default function createCallbackMemoizer(requireAllKeys = true) {\n  let cachedIndices = {};\n\n  return ({callback, indices}) => {\n    const keys = Object.keys(indices);\n    const allInitialized =\n      !requireAllKeys ||\n      keys.every(key => {\n        const value = indices[key];\n        return Array.isArray(value) ? value.length > 0 : value >= 0;\n      });\n    const indexChanged =\n      keys.length !== Object.keys(cachedIndices).length ||\n      keys.some(key => {\n        const cachedValue = cachedIndices[key];\n        const value = indices[key];\n\n        return Array.isArray(value)\n          ? cachedValue.join(',') !== value.join(',')\n          : cachedValue !== value;\n      });\n\n    cachedIndices = indices;\n\n    if (allInitialized && indexChanged) {\n      callback(indices);\n    }\n  };\n}\n"
  },
  {
    "path": "source/utils/getUpdatedOffsetForIndex.jest.js",
    "content": "import getUpdatedOffsetForIndex from './getUpdatedOffsetForIndex';\nimport {getCellMetadata} from './TestHelper';\n\ndescribe('getUpdatedOffsetForIndex', () => {\n  function testHelper(\n    targetIndex,\n    currentOffset,\n    cellMetadata = getCellMetadata(),\n  ) {\n    return getUpdatedOffsetForIndex({\n      cellOffset: cellMetadata[targetIndex].offset,\n      cellSize: cellMetadata[targetIndex].size,\n      containerSize: 50,\n      currentOffset,\n    });\n  }\n\n  it('should scroll to the beginning', () => {\n    expect(testHelper(0, 100)).toEqual(0);\n  });\n\n  it('should scroll forward to the middle', () => {\n    expect(testHelper(4, 0)).toEqual(20);\n  });\n\n  it('should scroll backward to the middle', () => {\n    expect(testHelper(2, 100)).toEqual(30);\n  });\n\n  it('should not scroll if an item is already visible', () => {\n    expect(testHelper(2, 20)).toEqual(20);\n  });\n\n  it('should scroll to the end', () => {\n    expect(testHelper(8, 0)).toEqual(110);\n  });\n\n  it('should honor specified :align values', () => {\n    expect(\n      getUpdatedOffsetForIndex({\n        align: 'auto',\n        cellOffset: 50,\n        cellSize: 10,\n        containerSize: 50,\n        currentOffset: 0,\n      }),\n    ).toEqual(10);\n    expect(\n      getUpdatedOffsetForIndex({\n        align: 'start',\n        cellOffset: 50,\n        cellSize: 10,\n        containerSize: 50,\n        currentOffset: 0,\n      }),\n    ).toEqual(50);\n    expect(\n      getUpdatedOffsetForIndex({\n        align: 'auto',\n        cellOffset: 50,\n        cellSize: 10,\n        containerSize: 50,\n        currentOffset: 100,\n      }),\n    ).toEqual(50);\n    expect(\n      getUpdatedOffsetForIndex({\n        align: 'end',\n        cellOffset: 50,\n        cellSize: 10,\n        containerSize: 50,\n        currentOffset: 100,\n      }),\n    ).toEqual(10);\n    expect(\n      getUpdatedOffsetForIndex({\n        align: 'center',\n        cellOffset: 50,\n        cellSize: 10,\n        containerSize: 50,\n        currentOffset: 100,\n      }),\n    ).toEqual(30);\n  });\n});\n"
  },
  {
    "path": "source/utils/getUpdatedOffsetForIndex.js",
    "content": "/**\n * Determines a new offset that ensures a certain cell is visible, given the current offset.\n * If the cell is already visible then the current offset will be returned.\n * If the current offset is too great or small, it will be adjusted just enough to ensure the specified index is visible.\n *\n * @param align Desired alignment within container; one of \"auto\" (default), \"start\", or \"end\"\n * @param cellOffset Offset (x or y) position for cell\n * @param cellSize Size (width or height) of cell\n * @param containerSize Total size (width or height) of the container\n * @param currentOffset Container's current (x or y) offset\n * @return Offset to use to ensure the specified cell is visible\n */\nexport default function getUpdatedOffsetForIndex({\n  align = 'auto',\n  cellOffset,\n  cellSize,\n  containerSize,\n  currentOffset,\n}) {\n  const maxOffset = cellOffset;\n  const minOffset = maxOffset - containerSize + cellSize;\n\n  switch (align) {\n    case 'start':\n      return maxOffset;\n    case 'end':\n      return minOffset;\n    case 'center':\n      return maxOffset - (containerSize - cellSize) / 2;\n    default:\n      return Math.max(minOffset, Math.min(maxOffset, currentOffset));\n  }\n}\n"
  },
  {
    "path": "source/utils/initCellMetadata.js",
    "content": "/**\n * Initializes metadata for an axis and its cells.\n * This data is used to determine which cells are visible given a container size and scroll position.\n *\n * @param cellCount Total number of cells.\n * @param size Either a fixed size or a function that returns the size for a given given an index.\n * @return Object mapping cell index to cell metadata (size, offset)\n */\nexport default function initCellMetadata({cellCount, size}) {\n  const sizeGetter = typeof size === 'function' ? size : () => size;\n\n  const cellMetadata = [];\n  let offset = 0;\n\n  for (var i = 0; i < cellCount; i++) {\n    let size = sizeGetter({index: i});\n\n    if (size == null || isNaN(size)) {\n      throw Error(`Invalid size returned for cell ${i} of value ${size}`);\n    }\n\n    cellMetadata[i] = {\n      size,\n      offset,\n    };\n\n    offset += size;\n  }\n\n  return cellMetadata;\n}\n"
  },
  {
    "path": "source/utils/requestAnimationTimeout.js",
    "content": "/** @flow */\n\nimport {caf, raf} from './animationFrame';\n\nexport type AnimationTimeoutId = {\n  id: number,\n};\n\nexport const cancelAnimationTimeout = (frame: AnimationTimeoutId) =>\n  caf(frame.id);\n\n/**\n * Recursively calls requestAnimationFrame until a specified delay has been met or exceeded.\n * When the delay time has been reached the function you're timing out will be called.\n *\n * Credit: Joe Lambert (https://gist.github.com/joelambert/1002116#file-requesttimeout-js)\n */\nexport const requestAnimationTimeout = (\n  callback: Function,\n  delay: number,\n): AnimationTimeoutId => {\n  let start;\n  // wait for end of processing current event handler, because event handler may be long\n  Promise.resolve().then(() => {\n    start = Date.now();\n  });\n\n  const timeout = () => {\n    if (Date.now() - start >= delay) {\n      callback.call();\n    } else {\n      frame.id = raf(timeout);\n    }\n  };\n\n  const frame: AnimationTimeoutId = {\n    id: raf(timeout),\n  };\n\n  return frame;\n};\n"
  },
  {
    "path": "source/vendor/binarySearchBounds.js",
    "content": "/**\n * Binary Search Bounds\n * https://github.com/mikolalysenko/binary-search-bounds\n * Mikola Lysenko\n *\n * Inlined because of Content Security Policy issue caused by the use of `new Function(...)` syntax.\n * Issue reported here: https://github.com/mikolalysenko/binary-search-bounds/issues/5\n **/\n\nfunction _GEA(a, l, h, y) {\n  var i = h + 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (x >= y) {\n      i = m;\n      h = m - 1;\n    } else {\n      l = m + 1;\n    }\n  }\n  return i;\n}\nfunction _GEP(a, l, h, y, c) {\n  var i = h + 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (c(x, y) >= 0) {\n      i = m;\n      h = m - 1;\n    } else {\n      l = m + 1;\n    }\n  }\n  return i;\n}\nfunction dispatchBsearchGE(a, y, c, l, h) {\n  if (typeof c === 'function') {\n    return _GEP(\n      a,\n      l === void 0 ? 0 : l | 0,\n      h === void 0 ? a.length - 1 : h | 0,\n      y,\n      c,\n    );\n  } else {\n    return _GEA(\n      a,\n      c === void 0 ? 0 : c | 0,\n      l === void 0 ? a.length - 1 : l | 0,\n      y,\n    );\n  }\n}\n\nfunction _GTA(a, l, h, y) {\n  var i = h + 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (x > y) {\n      i = m;\n      h = m - 1;\n    } else {\n      l = m + 1;\n    }\n  }\n  return i;\n}\nfunction _GTP(a, l, h, y, c) {\n  var i = h + 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (c(x, y) > 0) {\n      i = m;\n      h = m - 1;\n    } else {\n      l = m + 1;\n    }\n  }\n  return i;\n}\nfunction dispatchBsearchGT(a, y, c, l, h) {\n  if (typeof c === 'function') {\n    return _GTP(\n      a,\n      l === void 0 ? 0 : l | 0,\n      h === void 0 ? a.length - 1 : h | 0,\n      y,\n      c,\n    );\n  } else {\n    return _GTA(\n      a,\n      c === void 0 ? 0 : c | 0,\n      l === void 0 ? a.length - 1 : l | 0,\n      y,\n    );\n  }\n}\n\nfunction _LTA(a, l, h, y) {\n  var i = l - 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (x < y) {\n      i = m;\n      l = m + 1;\n    } else {\n      h = m - 1;\n    }\n  }\n  return i;\n}\nfunction _LTP(a, l, h, y, c) {\n  var i = l - 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (c(x, y) < 0) {\n      i = m;\n      l = m + 1;\n    } else {\n      h = m - 1;\n    }\n  }\n  return i;\n}\nfunction dispatchBsearchLT(a, y, c, l, h) {\n  if (typeof c === 'function') {\n    return _LTP(\n      a,\n      l === void 0 ? 0 : l | 0,\n      h === void 0 ? a.length - 1 : h | 0,\n      y,\n      c,\n    );\n  } else {\n    return _LTA(\n      a,\n      c === void 0 ? 0 : c | 0,\n      l === void 0 ? a.length - 1 : l | 0,\n      y,\n    );\n  }\n}\n\nfunction _LEA(a, l, h, y) {\n  var i = l - 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (x <= y) {\n      i = m;\n      l = m + 1;\n    } else {\n      h = m - 1;\n    }\n  }\n  return i;\n}\nfunction _LEP(a, l, h, y, c) {\n  var i = l - 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (c(x, y) <= 0) {\n      i = m;\n      l = m + 1;\n    } else {\n      h = m - 1;\n    }\n  }\n  return i;\n}\nfunction dispatchBsearchLE(a, y, c, l, h) {\n  if (typeof c === 'function') {\n    return _LEP(\n      a,\n      l === void 0 ? 0 : l | 0,\n      h === void 0 ? a.length - 1 : h | 0,\n      y,\n      c,\n    );\n  } else {\n    return _LEA(\n      a,\n      c === void 0 ? 0 : c | 0,\n      l === void 0 ? a.length - 1 : l | 0,\n      y,\n    );\n  }\n}\n\nfunction _EQA(a, l, h, y) {\n  l - 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    if (x === y) {\n      return m;\n    } else if (x <= y) {\n      l = m + 1;\n    } else {\n      h = m - 1;\n    }\n  }\n  return -1;\n}\nfunction _EQP(a, l, h, y, c) {\n  l - 1;\n  while (l <= h) {\n    var m = (l + h) >>> 1,\n      x = a[m];\n    var p = c(x, y);\n    if (p === 0) {\n      return m;\n    } else if (p <= 0) {\n      l = m + 1;\n    } else {\n      h = m - 1;\n    }\n  }\n  return -1;\n}\nfunction dispatchBsearchEQ(a, y, c, l, h) {\n  if (typeof c === 'function') {\n    return _EQP(\n      a,\n      l === void 0 ? 0 : l | 0,\n      h === void 0 ? a.length - 1 : h | 0,\n      y,\n      c,\n    );\n  } else {\n    return _EQA(\n      a,\n      c === void 0 ? 0 : c | 0,\n      l === void 0 ? a.length - 1 : l | 0,\n      y,\n    );\n  }\n}\n\nexport default {\n  ge: dispatchBsearchGE,\n  gt: dispatchBsearchGT,\n  lt: dispatchBsearchLT,\n  le: dispatchBsearchLE,\n  eq: dispatchBsearchEQ,\n};\n"
  },
  {
    "path": "source/vendor/detectElementResize.js",
    "content": "/**\n * Detect Element Resize.\n * https://github.com/sdecima/javascript-detect-element-resize\n * Sebastian Decima\n *\n * Forked from version 0.5.3; includes the following modifications:\n * 1) Guard against unsafe 'window' and 'document' references (to support SSR).\n * 2) Defer initialization code via a top-level function wrapper (to support SSR).\n * 3) Avoid unnecessary reflows by not measuring size for scroll events bubbling from children.\n * 4) Add nonce for style element.\n * 5) Added support for injecting custom window object\n **/\n\nexport default function createDetectElementResize(nonce, hostWindow) {\n  // Check `document` and `window` in case of server-side rendering\n  var _window;\n  if (typeof hostWindow !== 'undefined') {\n    _window = hostWindow;\n  } else if (typeof window !== 'undefined') {\n    _window = window;\n  } else if (typeof self !== 'undefined') {\n    _window = self;\n  } else {\n    _window = global;\n  }\n\n  var attachEvent =\n    typeof _window.document !== 'undefined' && _window.document.attachEvent;\n\n  if (!attachEvent) {\n    var requestFrame = (function() {\n      var raf =\n        _window.requestAnimationFrame ||\n        _window.mozRequestAnimationFrame ||\n        _window.webkitRequestAnimationFrame ||\n        function(fn) {\n          return _window.setTimeout(fn, 20);\n        };\n      return function(fn) {\n        return raf(fn);\n      };\n    })();\n\n    var cancelFrame = (function() {\n      var cancel =\n        _window.cancelAnimationFrame ||\n        _window.mozCancelAnimationFrame ||\n        _window.webkitCancelAnimationFrame ||\n        _window.clearTimeout;\n      return function(id) {\n        return cancel(id);\n      };\n    })();\n\n    var resetTriggers = function(element) {\n      var triggers = element.__resizeTriggers__,\n        expand = triggers.firstElementChild,\n        contract = triggers.lastElementChild,\n        expandChild = expand.firstElementChild;\n      contract.scrollLeft = contract.scrollWidth;\n      contract.scrollTop = contract.scrollHeight;\n      expandChild.style.width = expand.offsetWidth + 1 + 'px';\n      expandChild.style.height = expand.offsetHeight + 1 + 'px';\n      expand.scrollLeft = expand.scrollWidth;\n      expand.scrollTop = expand.scrollHeight;\n    };\n\n    var checkTriggers = function(element) {\n      return (\n        element.offsetWidth != element.__resizeLast__.width ||\n        element.offsetHeight != element.__resizeLast__.height\n      );\n    };\n\n    var scrollListener = function(e) {\n      // Don't measure (which forces) reflow for scrolls that happen inside of children!\n      if (\n        e.target.className &&\n        typeof e.target.className.indexOf === 'function' &&\n        e.target.className.indexOf('contract-trigger') < 0 &&\n        e.target.className.indexOf('expand-trigger') < 0\n      ) {\n        return;\n      }\n\n      var element = this;\n      resetTriggers(this);\n      if (this.__resizeRAF__) {\n        cancelFrame(this.__resizeRAF__);\n      }\n      this.__resizeRAF__ = requestFrame(function() {\n        if (checkTriggers(element)) {\n          element.__resizeLast__.width = element.offsetWidth;\n          element.__resizeLast__.height = element.offsetHeight;\n          element.__resizeListeners__.forEach(function(fn) {\n            fn.call(element, e);\n          });\n        }\n      });\n    };\n\n    /* Detect CSS Animations support to detect element display/re-attach */\n    var animation = false,\n      keyframeprefix = '',\n      animationstartevent = 'animationstart',\n      domPrefixes = 'Webkit Moz O ms'.split(' '),\n      startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(\n        ' ',\n      ),\n      pfx = '';\n    {\n      var elm = _window.document.createElement('fakeelement');\n      if (elm.style.animationName !== undefined) {\n        animation = true;\n      }\n\n      if (animation === false) {\n        for (var i = 0; i < domPrefixes.length; i++) {\n          if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) {\n            pfx = domPrefixes[i];\n            keyframeprefix = '-' + pfx.toLowerCase() + '-';\n            animationstartevent = startEvents[i];\n            animation = true;\n            break;\n          }\n        }\n      }\n    }\n\n    var animationName = 'resizeanim';\n    var animationKeyframes =\n      '@' +\n      keyframeprefix +\n      'keyframes ' +\n      animationName +\n      ' { from { opacity: 0; } to { opacity: 0; } } ';\n    var animationStyle =\n      keyframeprefix + 'animation: 1ms ' + animationName + '; ';\n  }\n\n  var createStyles = function(doc) {\n    if (!doc.getElementById('detectElementResize')) {\n      //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360\n      var css =\n          (animationKeyframes ? animationKeyframes : '') +\n          '.resize-triggers { ' +\n          (animationStyle ? animationStyle : '') +\n          'visibility: hidden; opacity: 0; } ' +\n          '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; z-index: -1; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }',\n        head = doc.head || doc.getElementsByTagName('head')[0],\n        style = doc.createElement('style');\n\n      style.id = 'detectElementResize';\n      style.type = 'text/css';\n\n      if (nonce != null) {\n        style.setAttribute('nonce', nonce);\n      }\n\n      if (style.styleSheet) {\n        style.styleSheet.cssText = css;\n      } else {\n        style.appendChild(doc.createTextNode(css));\n      }\n\n      head.appendChild(style);\n    }\n  };\n\n  var addResizeListener = function(element, fn) {\n    if (attachEvent) {\n      element.attachEvent('onresize', fn);\n    } else {\n      if (!element.__resizeTriggers__) {\n        var doc = element.ownerDocument;\n        var elementStyle = _window.getComputedStyle(element);\n        if (elementStyle && elementStyle.position == 'static') {\n          element.style.position = 'relative';\n        }\n        createStyles(doc);\n        element.__resizeLast__ = {};\n        element.__resizeListeners__ = [];\n        (element.__resizeTriggers__ = doc.createElement('div')).className =\n          'resize-triggers';\n        var expandTrigger = doc.createElement('div');\n        expandTrigger.className = 'expand-trigger';\n        expandTrigger.appendChild(doc.createElement('div'));\n        var contractTrigger = doc.createElement('div');\n        contractTrigger.className = 'contract-trigger';\n        element.__resizeTriggers__.appendChild(expandTrigger);\n        element.__resizeTriggers__.appendChild(contractTrigger);\n        element.appendChild(element.__resizeTriggers__);\n        resetTriggers(element);\n        element.addEventListener('scroll', scrollListener, true);\n\n        /* Listen for a css animation to detect element display/re-attach */\n        if (animationstartevent) {\n          element.__resizeTriggers__.__animationListener__ = function animationListener(\n            e,\n          ) {\n            if (e.animationName == animationName) {\n              resetTriggers(element);\n            }\n          };\n          element.__resizeTriggers__.addEventListener(\n            animationstartevent,\n            element.__resizeTriggers__.__animationListener__,\n          );\n        }\n      }\n      element.__resizeListeners__.push(fn);\n    }\n  };\n\n  var removeResizeListener = function(element, fn) {\n    if (attachEvent) {\n      element.detachEvent('onresize', fn);\n    } else {\n      element.__resizeListeners__.splice(\n        element.__resizeListeners__.indexOf(fn),\n        1,\n      );\n      if (!element.__resizeListeners__.length) {\n        element.removeEventListener('scroll', scrollListener, true);\n        if (element.__resizeTriggers__.__animationListener__) {\n          element.__resizeTriggers__.removeEventListener(\n            animationstartevent,\n            element.__resizeTriggers__.__animationListener__,\n          );\n          element.__resizeTriggers__.__animationListener__ = null;\n        }\n        try {\n          element.__resizeTriggers__ = !element.removeChild(\n            element.__resizeTriggers__,\n          );\n        } catch (e) {\n          // Preact compat; see developit/preact-compat/issues/228\n        }\n      }\n    }\n  };\n\n  return {\n    addResizeListener,\n    removeResizeListener,\n  };\n}\n"
  },
  {
    "path": "source/vendor/intervalTree.js",
    "content": "/**\n * Binary Search Bounds\n * https://github.com/mikolalysenko/interval-tree-1d\n * Mikola Lysenko\n *\n * Inlined because of Content Security Policy issue caused by the use of `new Function(...)` syntax in an upstream dependency.\n * Issue reported here: https://github.com/mikolalysenko/binary-search-bounds/issues/5\n **/\n\nimport bounds from './binarySearchBounds';\n\nvar NOT_FOUND = 0;\nvar SUCCESS = 1;\nvar EMPTY = 2;\n\nfunction IntervalTreeNode(mid, left, right, leftPoints, rightPoints) {\n  this.mid = mid;\n  this.left = left;\n  this.right = right;\n  this.leftPoints = leftPoints;\n  this.rightPoints = rightPoints;\n  this.count =\n    (left ? left.count : 0) + (right ? right.count : 0) + leftPoints.length;\n}\n\nvar proto = IntervalTreeNode.prototype;\n\nfunction copy(a, b) {\n  a.mid = b.mid;\n  a.left = b.left;\n  a.right = b.right;\n  a.leftPoints = b.leftPoints;\n  a.rightPoints = b.rightPoints;\n  a.count = b.count;\n}\n\nfunction rebuild(node, intervals) {\n  var ntree = createIntervalTree(intervals);\n  node.mid = ntree.mid;\n  node.left = ntree.left;\n  node.right = ntree.right;\n  node.leftPoints = ntree.leftPoints;\n  node.rightPoints = ntree.rightPoints;\n  node.count = ntree.count;\n}\n\nfunction rebuildWithInterval(node, interval) {\n  var intervals = node.intervals([]);\n  intervals.push(interval);\n  rebuild(node, intervals);\n}\n\nfunction rebuildWithoutInterval(node, interval) {\n  var intervals = node.intervals([]);\n  var idx = intervals.indexOf(interval);\n  if (idx < 0) {\n    return NOT_FOUND;\n  }\n  intervals.splice(idx, 1);\n  rebuild(node, intervals);\n  return SUCCESS;\n}\n\nproto.intervals = function(result) {\n  result.push.apply(result, this.leftPoints);\n  if (this.left) {\n    this.left.intervals(result);\n  }\n  if (this.right) {\n    this.right.intervals(result);\n  }\n  return result;\n};\n\nproto.insert = function(interval) {\n  var weight = this.count - this.leftPoints.length;\n  this.count += 1;\n  if (interval[1] < this.mid) {\n    if (this.left) {\n      if (4 * (this.left.count + 1) > 3 * (weight + 1)) {\n        rebuildWithInterval(this, interval);\n      } else {\n        this.left.insert(interval);\n      }\n    } else {\n      this.left = createIntervalTree([interval]);\n    }\n  } else if (interval[0] > this.mid) {\n    if (this.right) {\n      if (4 * (this.right.count + 1) > 3 * (weight + 1)) {\n        rebuildWithInterval(this, interval);\n      } else {\n        this.right.insert(interval);\n      }\n    } else {\n      this.right = createIntervalTree([interval]);\n    }\n  } else {\n    var l = bounds.ge(this.leftPoints, interval, compareBegin);\n    var r = bounds.ge(this.rightPoints, interval, compareEnd);\n    this.leftPoints.splice(l, 0, interval);\n    this.rightPoints.splice(r, 0, interval);\n  }\n};\n\nproto.remove = function(interval) {\n  var weight = this.count - this.leftPoints;\n  if (interval[1] < this.mid) {\n    if (!this.left) {\n      return NOT_FOUND;\n    }\n    var rw = this.right ? this.right.count : 0;\n    if (4 * rw > 3 * (weight - 1)) {\n      return rebuildWithoutInterval(this, interval);\n    }\n    var r = this.left.remove(interval);\n    if (r === EMPTY) {\n      this.left = null;\n      this.count -= 1;\n      return SUCCESS;\n    } else if (r === SUCCESS) {\n      this.count -= 1;\n    }\n    return r;\n  } else if (interval[0] > this.mid) {\n    if (!this.right) {\n      return NOT_FOUND;\n    }\n    var lw = this.left ? this.left.count : 0;\n    if (4 * lw > 3 * (weight - 1)) {\n      return rebuildWithoutInterval(this, interval);\n    }\n    var r = this.right.remove(interval);\n    if (r === EMPTY) {\n      this.right = null;\n      this.count -= 1;\n      return SUCCESS;\n    } else if (r === SUCCESS) {\n      this.count -= 1;\n    }\n    return r;\n  } else {\n    if (this.count === 1) {\n      if (this.leftPoints[0] === interval) {\n        return EMPTY;\n      } else {\n        return NOT_FOUND;\n      }\n    }\n    if (this.leftPoints.length === 1 && this.leftPoints[0] === interval) {\n      if (this.left && this.right) {\n        var p = this;\n        var n = this.left;\n        while (n.right) {\n          p = n;\n          n = n.right;\n        }\n        if (p === this) {\n          n.right = this.right;\n        } else {\n          var l = this.left;\n          var r = this.right;\n          p.count -= n.count;\n          p.right = n.left;\n          n.left = l;\n          n.right = r;\n        }\n        copy(this, n);\n        this.count =\n          (this.left ? this.left.count : 0) +\n          (this.right ? this.right.count : 0) +\n          this.leftPoints.length;\n      } else if (this.left) {\n        copy(this, this.left);\n      } else {\n        copy(this, this.right);\n      }\n      return SUCCESS;\n    }\n    for (\n      var l = bounds.ge(this.leftPoints, interval, compareBegin);\n      l < this.leftPoints.length;\n      ++l\n    ) {\n      if (this.leftPoints[l][0] !== interval[0]) {\n        break;\n      }\n      if (this.leftPoints[l] === interval) {\n        this.count -= 1;\n        this.leftPoints.splice(l, 1);\n        for (\n          var r = bounds.ge(this.rightPoints, interval, compareEnd);\n          r < this.rightPoints.length;\n          ++r\n        ) {\n          if (this.rightPoints[r][1] !== interval[1]) {\n            break;\n          } else if (this.rightPoints[r] === interval) {\n            this.rightPoints.splice(r, 1);\n            return SUCCESS;\n          }\n        }\n      }\n    }\n    return NOT_FOUND;\n  }\n};\n\nfunction reportLeftRange(arr, hi, cb) {\n  for (var i = 0; i < arr.length && arr[i][0] <= hi; ++i) {\n    var r = cb(arr[i]);\n    if (r) {\n      return r;\n    }\n  }\n}\n\nfunction reportRightRange(arr, lo, cb) {\n  for (var i = arr.length - 1; i >= 0 && arr[i][1] >= lo; --i) {\n    var r = cb(arr[i]);\n    if (r) {\n      return r;\n    }\n  }\n}\n\nfunction reportRange(arr, cb) {\n  for (var i = 0; i < arr.length; ++i) {\n    var r = cb(arr[i]);\n    if (r) {\n      return r;\n    }\n  }\n}\n\nproto.queryPoint = function(x, cb) {\n  if (x < this.mid) {\n    if (this.left) {\n      var r = this.left.queryPoint(x, cb);\n      if (r) {\n        return r;\n      }\n    }\n    return reportLeftRange(this.leftPoints, x, cb);\n  } else if (x > this.mid) {\n    if (this.right) {\n      var r = this.right.queryPoint(x, cb);\n      if (r) {\n        return r;\n      }\n    }\n    return reportRightRange(this.rightPoints, x, cb);\n  } else {\n    return reportRange(this.leftPoints, cb);\n  }\n};\n\nproto.queryInterval = function(lo, hi, cb) {\n  if (lo < this.mid && this.left) {\n    var r = this.left.queryInterval(lo, hi, cb);\n    if (r) {\n      return r;\n    }\n  }\n  if (hi > this.mid && this.right) {\n    var r = this.right.queryInterval(lo, hi, cb);\n    if (r) {\n      return r;\n    }\n  }\n  if (hi < this.mid) {\n    return reportLeftRange(this.leftPoints, hi, cb);\n  } else if (lo > this.mid) {\n    return reportRightRange(this.rightPoints, lo, cb);\n  } else {\n    return reportRange(this.leftPoints, cb);\n  }\n};\n\nfunction compareNumbers(a, b) {\n  return a - b;\n}\n\nfunction compareBegin(a, b) {\n  var d = a[0] - b[0];\n  if (d) {\n    return d;\n  }\n  return a[1] - b[1];\n}\n\nfunction compareEnd(a, b) {\n  var d = a[1] - b[1];\n  if (d) {\n    return d;\n  }\n  return a[0] - b[0];\n}\n\nfunction createIntervalTree(intervals) {\n  if (intervals.length === 0) {\n    return null;\n  }\n  var pts = [];\n  for (var i = 0; i < intervals.length; ++i) {\n    pts.push(intervals[i][0], intervals[i][1]);\n  }\n  pts.sort(compareNumbers);\n\n  var mid = pts[pts.length >> 1];\n\n  var leftIntervals = [];\n  var rightIntervals = [];\n  var centerIntervals = [];\n  for (var i = 0; i < intervals.length; ++i) {\n    var s = intervals[i];\n    if (s[1] < mid) {\n      leftIntervals.push(s);\n    } else if (mid < s[0]) {\n      rightIntervals.push(s);\n    } else {\n      centerIntervals.push(s);\n    }\n  }\n\n  //Split center intervals\n  var leftPoints = centerIntervals;\n  var rightPoints = centerIntervals.slice();\n  leftPoints.sort(compareBegin);\n  rightPoints.sort(compareEnd);\n\n  return new IntervalTreeNode(\n    mid,\n    createIntervalTree(leftIntervals),\n    createIntervalTree(rightIntervals),\n    leftPoints,\n    rightPoints,\n  );\n}\n\n//User friendly wrapper that makes it possible to support empty trees\nfunction IntervalTree(root) {\n  this.root = root;\n}\n\nvar tproto = IntervalTree.prototype;\n\ntproto.insert = function(interval) {\n  if (this.root) {\n    this.root.insert(interval);\n  } else {\n    this.root = new IntervalTreeNode(\n      interval[0],\n      null,\n      null,\n      [interval],\n      [interval],\n    );\n  }\n};\n\ntproto.remove = function(interval) {\n  if (this.root) {\n    var r = this.root.remove(interval);\n    if (r === EMPTY) {\n      this.root = null;\n    }\n    return r !== NOT_FOUND;\n  }\n  return false;\n};\n\ntproto.queryPoint = function(p, cb) {\n  if (this.root) {\n    return this.root.queryPoint(p, cb);\n  }\n};\n\ntproto.queryInterval = function(lo, hi, cb) {\n  if (lo <= hi && this.root) {\n    return this.root.queryInterval(lo, hi, cb);\n  }\n};\n\nObject.defineProperty(tproto, 'count', {\n  get: function() {\n    if (this.root) {\n      return this.root.count;\n    }\n    return 0;\n  },\n});\n\nObject.defineProperty(tproto, 'intervals', {\n  get: function() {\n    if (this.root) {\n      return this.root.intervals([]);\n    }\n    return [];\n  },\n});\n\nexport default function createWrapper(intervals) {\n  if (!intervals || intervals.length === 0) {\n    return new IntervalTree(null);\n  }\n  return new IntervalTree(createIntervalTree(intervals));\n}\n"
  },
  {
    "path": "webpack.config.demo.js",
    "content": "const HtmlWebpackPlugin = require('html-webpack-plugin');\nconst path = require('path');\nconst webpack = require('webpack');\n\nmodule.exports = {\n  devtool: 'source-map',\n  entry: {\n    demo: './source/demo/index',\n  },\n  output: {\n    path: path.join(__dirname, 'build'),\n    filename: 'static/[name].js',\n  },\n  plugins: [\n    new HtmlWebpackPlugin({\n      filename: 'index.html',\n      inject: true,\n      template: './index.html',\n    }),\n    new webpack.DefinePlugin({\n      'process.env': {\n        NODE_ENV: JSON.stringify('production'),\n      },\n    }),\n  ],\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        use: ['babel-loader'],\n        include: path.join(__dirname, 'source'),\n      },\n      {\n        test: /\\.css$/,\n        use: ['style-loader', 'css-loader?modules', 'postcss-loader'],\n        include: path.join(__dirname, 'source'),\n      },\n      {\n        test: /\\.css$/,\n        use: ['style-loader', 'css-loader'],\n        include: path.join(__dirname, 'styles.css'),\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": "webpack.config.dev.js",
    "content": "const HtmlWebpackPlugin = require('html-webpack-plugin');\nconst path = require('path');\n\nmodule.exports = {\n  devtool: 'eval',\n  entry: {\n    demo: './source/demo/index',\n  },\n  output: {\n    path: path.join(__dirname, 'build'),\n    filename: 'static/[name].js',\n  },\n  plugins: [\n    new HtmlWebpackPlugin({\n      filename: 'index.html',\n      inject: true,\n      template: './index.html',\n    }),\n  ],\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        use: ['babel-loader'],\n        include: path.join(__dirname, 'source'),\n      },\n      {\n        test: /\\.css$/,\n        use: ['style-loader', 'css-loader?modules', 'postcss-loader'],\n        include: path.join(__dirname, 'source'),\n      },\n      {\n        test: /\\.css$/,\n        use: ['style-loader', 'css-loader'],\n        include: path.join(__dirname, 'styles.css'),\n      },\n    ],\n  },\n  devServer: {\n    contentBase: 'build',\n    port: 3001,\n  },\n};\n"
  }
]