[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\"@researchgate/babel-preset\"]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# http://EditorConfig.org\n# top-most EditorConfig file\nroot = true\n\n[*]\nend_of_line = lf\ntrim_trailing_whitespace = true\ninsert_final_newline = false\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.{js,scss}]\ncharset = utf-8\nindent_style = space\ninsert_final_newline = true\n\n[*.js]\nindent_size = 4\n\n[*.scss]\nindent_size = 2\n\n[{*.json,.travis.yml,.*rc}]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".eslintignore",
    "content": "node_modules\nlib\n.docs\ntypes"
  },
  {
    "path": ".eslintrc.js",
    "content": "'use strict';\n// This file was created by spire-plugin-eslint for editor support\nmodule.exports = require('@researchgate/spire-config/eslint/react-typescript');\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open, inclusive, and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, nationality, personal appearance, race, religion, sexual identity and orientation, level of experience, or technical ability.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of different viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Sustained disruption of discussion\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\nThe ResearchGate community prioritizes (marginalized) people's safety over (privileged) people's comfort.\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instance of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behavior that they deem inappropriate, threatening, offensive, or harmful.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [researchgate@github.com](mailto:researchgate@github.com). All complaints will be reviewed and investigated, and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regards to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers 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.\n\n## Attribution\n\nThis Code of Conduct is based on [Contributor Covenant][homepage].\n\n[homepage]: http://contributor-covenant.org\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing Guide\n\nThanks for taking the time to contribute!\n\n### Table of contents\n\n[Code of Conduct](#code-of-conduct)\n\n[Contribution prerequisites](#contribution-prerequisites)\n\n[Project Setup](#project-setup)\n  * [How to run the project](#how-to-run-the-project)\n  * [How to run tests](#how-to-run-tests)\n\n[How can you contribute](#how-can-you-contribute)\n  * [Reporting bugs](#reporting-bugs)\n  * [Suggesting enhancements](#suggesting-enhancements)\n  * [Writing documentation](#writing-documentation)\n  * [Your first code contribution](#your-first-code-contribution)\n\n[Additional Notes](#additional-notes)\n  * [Issue and Pull Request Templates](#issue-and-pull-request-templates)\n  * [Need help?](#need-help?)\n\n## Code of Conduct\n\nThis project and everyone contributing to it adheres to the [ResearchGate Code of Conduct](CODE_OF_CONDUCT.md).\nBy participating you are expected to uphold this code. Please report any behavior you find unacceptable to [researchgate@github.com](mailto:researchgate@github.com).\n\n## Contribution prerequisites\n\nBefore you start your work, make sure that you:\n\n* have `node` installed at v6.10.0\n* have `npm` installed at 5.3.0\n* have `yarn` installed at v0.21.3\n* are familiar with `git`\n* are familiar with [conventional commits](http://conventionalcommits.org)\n* have read and agree to abide by the [ResearchGate Code of Conduct](CODE_OF_CONDUCT.md)\n\n## Project setup\n\n### How to run the project\n\nCreate a clone of the repository:\n\n```\ngit clone https://github.com/researchgate/react-intersection-list.git\ncd react-intersection-list\n```\n\nInstall the dependencies with:\n\n```\nnpm install\n```\n\nNow run storybook:\n\n```\nnpm run storybook\n```\n\n### How to run tests\n\nThis project uses [jest](http://facebook.github.io/jest/) for JavaScript testing.\n\nTo run tests, execute:\n\n```\nyarn test\n```\n\nSince coverage is not collected by default when running yarn test, run:\n\n```\nyarn coverage\n```\n\nTo run linters use:\n\n```\nyarn lint\n```\n\n## How you can contribute\n\n### Reporting bugs\n\nThis section guides you through the steps to follow when you submit a bug report for a ResearchGate project.\n\nFollowing these guidelines makes it easy for the maintainers and community to understand your report, reproduce the behavior, and find related reports.\n\nBefore creating a bug report, please check Open Issues as you may find that there is already an issue open for the bug you’ve found. When you create a bug report, be sure to include as much detail as possible and fill out [the required template](ISSUE_TEMPLATE.md), the information it asks for helps us resolve issues faster.\n\nAfter you have submitted the issue, we'll try to get back to you as soon as possible.\n\n### Suggesting enhancements\n\nThis section guides you through submitting a feature suggestion.\n\nAll enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/) and, when approved by a core team member or project maintainer, are given the green light to then be turned into a Pull Request.\nFor the best possible experience, please provide us with:\n\n* **A clear and descriptive title**\n* **A step-by-step description of the suggested enhancement** and what it should do\n* **Specific examples to demonstrate the steps**. Ideally, you should support it with code snippets, screenshots, and/or animated GIFs\n* **Explanation of why this feature would be useful** to this project\n* **Your development environment** and context for creating the feature\n\nAfter you have submitted your Pull Request, we'll try to get back to you as soon as possible.\n\n### Writing documentation\n\nAll great projects require good documentation.\nThere is __always__ room for (better) docs, so why not to contribute to the project by enhancing them?\nPlease do so via Pull Request.\n\n### Your first code contribution\n\nUnsure where you can start contributing?\nWe strive to make all our projects easy for beginners to contribute to. Just look out for issues labeled `help-wanted` and `beginner-friendly`, then get stuck in!\nIf you still need some guidance, consider [this resource](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) and/or contact us.\n\n## Additional notes\n\n### Issue and pull request templates\n\nWhen filing an issue or pull request, please take the time to fill out the templates we provide in as much detail as you can. This helps ensure that we have all the information we need to provide you with the right support so that your experience contributing to our open-source project runs as smoothly and easily as you would like it to.\n\n### Need help?\n\nIf you need any help or require additional information, don't hesitate to contact the project maintainer or any of the contributors.\n\nThank you for contributing!"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "Thanks for taking the time to file an issue with us.\nPlease note that this issue template is used **ONLY** for reporting bugs.\n\nIf you have an issue that isn’t a bug, please follow the steps listed in the [Contributing](CONTRIBUTING.md).\nThanks!\n\n## Expected behavior\n<!--- What should happen -->\n\n## Current behavior\n<!--- What is happening instead of the expected behavior -->\n\n## Steps to reproduce\n<!--- Provide a link to a live example, a code snippet, or a set of steps to -->\n<!--- reproduce this bug. -->\n1.\n2.\n3.\n4.\n\n## Context (environment)\n<!--- Please provide technical context, as well as possible background -->\n<!--- information that can help us identify the problem -->\n\n* **Version**: <!-- compulsory. you must provide your version -->\n* **Platform**: <!-- either `uname -a` output, or if Windows, version and 32-bit or\n  64-bit -->"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "Thanks for taking the time to file a pull request with us.\nPlease take a moment to provide the following details:\n\n<!--- Provide a general summary of your changes in the Title above -->\n\n## Description\n<!--- Describe your changes in detail -->\n\n## Motivation and context\n<!--- Why is this change required? What problem does it solve? -->\n<!--- If it fixes an open issue, please link to the issue here. -->\n\n## How has this been tested?\n<!--- Please describe in detail how you tested your changes. -->\n<!--- Include details of your testing environment, and the tests you ran to -->\n<!--- see how your change affects other areas of the code, etc. -->\n\n## Screenshots (if relevant):\n\n## Type of change\n<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to change)\n\n## Checklist:\n<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->\n<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->\n- [ ] My code follows the code style of this project\n- [ ] My change requires a change to the documentation\n- [ ] I have updated the documentation accordingly\n- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) document\n- [ ] I have added tests to cover my changes\n- [ ] All new and existing tests passed"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\nenv:\n  NODE_VERSION: 14\n\njobs:\n  tests:\n    name: Unit tests\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v3\n\n    - name: Setup Node.js\n      uses: actions/setup-node@v3\n      with:\n        node-version: ${{ env.NODE_VERSION }}\n\n    - name: Cache node_modules\n      uses: actions/cache@v3\n      id: cache-nodemodules\n      with:\n        path: node_modules\n        key: ${{ runner.os }}-${{ env.NODE_VERSION }}-nodemodules-${{ hashFiles('**/yarn.lock') }}\n\n    - name: Install dependencies\n      if: steps.cache-nodemodules.outputs.cache-hit != 'true'\n      run: yarn install --frozen-lockfile --non-interactive\n\n    - name: Unit tests\n      run: yarn test --ci --coverage\n\n    - name: Upload coverage to Codecov\n      uses: codecov/codecov-action@v2\n      with:\n        files: ./src/coverage/coverage-final.json\n        fail_ci_if_error: true\n\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v3\n\n    - name: Setup Node.js\n      uses: actions/setup-node@v3\n      with:\n        node-version: ${{ env.NODE_VERSION }}\n\n    - name: Cache node_modules\n      uses: actions/cache@v3\n      id: cache-nodemodules\n      with:\n        path: node_modules\n        key: ${{ runner.os }}-${{ env.NODE_VERSION }}-nodemodules-${{ hashFiles('**/yarn.lock') }}\n\n    - name: Install dependencies\n      if: steps.cache-nodemodules.outputs.cache-hit != 'true'\n      run: yarn install --frozen-lockfile --non-interactive\n\n    - name: Lint\n      run: yarn lint\n\n  docs:\n    needs: [tests, lint]\n    if: github.ref == 'refs/heads/main'\n    name: Update docs\n    runs-on: ubuntu-latest\n    \n    steps:\n    - name: Checkout\n      uses: actions/checkout@v3\n\n    - name: Setup Node.js\n      uses: actions/setup-node@v3\n      with:\n        node-version: ${{ env.NODE_VERSION }}\n\n    - name: Cache node_modules\n      uses: actions/cache@v3\n      id: cache-nodemodules\n      with:\n        path: node_modules\n        key: ${{ runner.os }}-${{ env.NODE_VERSION }}-nodemodules-${{ hashFiles('**/yarn.lock') }}\n\n    - name: Install dependencies\n      if: steps.cache-nodemodules.outputs.cache-hit != 'true'\n      run: yarn install --frozen-lockfile --non-interactive\n\n    - name: Build latest app\n      run: yarn build\n\n    - name: Generate storybook documentation\n      run: yarn build:storybook\n  \n    - name: Deploy storybook documentation to gh-pages\n      uses: JamesIves/github-pages-deploy-action@v4.2.5\n      with:\n        branch: gh-pages # The branch the action should deploy to.\n        folder: .docs # The folder the action should deploy.\n          \n  release:\n    needs: [tests, lint, docs]\n    if: github.ref == 'refs/heads/main'\n    name: Release\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v3\n        with:\n          node-version: ${{ env.NODE_VERSION }}\n\n      - name: Cache node_modules\n        uses: actions/cache@v3\n        id: cache-nodemodules\n        with:\n          path: node_modules\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-nodemodules-${{ hashFiles('**/yarn.lock') }}\n\n      - name: Install dependencies\n        if: steps.cache-nodemodules.outputs.cache-hit != 'true'\n        run: yarn install --frozen-lockfile --non-interactive\n\n      - name: Release\n        run: yarn release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "npm-debug.log*\nyarn-error.log\n/node_modules\n/lib\n/src/coverage\n/.docs\n.DS_Store"
  },
  {
    "path": ".prettierignore",
    "content": "node_modules/\nyarn.lock"
  },
  {
    "path": ".spire/spire-plugin-tslint.js",
    "content": "function tslint({ setState }) {\n    return {\n        name: 'spire-plugin-tslint',\n        async precommit() {\n            setState(state => ({\n                linters: [\n                    ...state.linters,\n                    { '**/*.tsx?': ['tsc', '--project', 'types'] },\n                ],\n            }));\n        },\n    };\n}\n\nmodule.exports = tslint;\n"
  },
  {
    "path": ".storybook/addons.js",
    "content": "import '@storybook/addon-options/register';\n"
  },
  {
    "path": ".storybook/config.js",
    "content": "import { addParameters, configure } from '@storybook/react';\nimport { withOptions } from '@storybook/addon-options';\n\nwithOptions({\n    name: 'React Intersection List',\n    url: 'https://github.com/researchgate/react-intersection-list',\n    showDownPanel: false,\n});\n\naddParameters({ options: { theme: {} } });\n\nconfigure(() => require('../docs/docs/index.js'), module);\n"
  },
  {
    "path": ".storybook/preview-head.html",
    "content": "<link\n  href=\"https://fonts.googleapis.com/css?family=Roboto&display=swap\"\n  rel=\"stylesheet\"\n/>\n"
  },
  {
    "path": "AUTHORS",
    "content": "Luis Merino <mail@luismerino.name>\nDaniel Tschinder <daniel.tschinder@researchgate.net>\nSergey Tatarintsev <sergey.tatarintsev@researchgate.net>"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## [3.0.12](https://github.com/researchgate/react-intersection-list/compare/v3.0.11...v3.0.12) (2021-05-11)\n\n\n### Bug Fixes\n\n* **renovate:** Force default branch to be main ([74fa774](https://github.com/researchgate/react-intersection-list/commit/74fa77474de0c95d94f4bfcf24d7b198bfda98e6))\n\n## [3.0.11](https://github.com/researchgate/react-intersection-list/compare/v3.0.10...v3.0.11) (2020-08-14)\n\n\n### Bug Fixes\n\n* **security:** Fix security issue with serialize-javascript ([8c3265e](https://github.com/researchgate/react-intersection-list/commit/8c3265e10a4de29a7717edc53f6be7158f41b77d))\n\n## [3.0.10](https://github.com/researchgate/react-intersection-list/compare/v3.0.9...v3.0.10) (2020-05-04)\n\n\n### Bug Fixes\n\n* fix import path of intersection-observer dependency in sentinel ([#124](https://github.com/researchgate/react-intersection-list/issues/124)) ([659afa8](https://github.com/researchgate/react-intersection-list/commit/659afa8835ede0b07bd49d3d23f8e652312c1dff))\n\n## [3.0.9](https://github.com/researchgate/react-intersection-list/compare/v3.0.8...v3.0.9) (2020-03-26)\n\n\n### Bug Fixes\n\n* **intersection-observer:** upgrade to latest version and adapt ([1a5fa7c](https://github.com/researchgate/react-intersection-list/commit/1a5fa7c60e1aaab3a4056b4bd1e5049fbb3c7294))\n\n## [3.0.8](https://github.com/researchgate/react-intersection-list/compare/v3.0.7...v3.0.8) (2019-12-01)\n\n\n### Bug Fixes\n\n* **deps:** lock file maintenance ([#79](https://github.com/researchgate/react-intersection-list/issues/79)) ([fd19604](https://github.com/researchgate/react-intersection-list/commit/fd1960478c2dadb8518c45fc113216168325061a))\n\n## [3.0.7](https://github.com/researchgate/react-intersection-list/compare/v3.0.6...v3.0.7) (2019-11-23)\n\n\n### Bug Fixes\n\n* **publish:** Do not publish tests ([ef19403](https://github.com/researchgate/react-intersection-list/commit/ef19403259e91b9fd27109d3c56cae5f84679167))\n\n## [3.0.6](https://github.com/researchgate/react-intersection-list/compare/v3.0.5...v3.0.6) (2019-11-23)\n\n\n### Bug Fixes\n\n* **spire:** change tooling and build system to spire ([36bdcd7](https://github.com/researchgate/react-intersection-list/commit/36bdcd7bc9f61d5c3f33e0e73f0afbdba6cc353f))\n\n# Change Log\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n\n<a name=\"3.0.5\"></a>\n## [3.0.5](https://github.com/researchgate/react-intersection-list/compare/v3.0.4...v3.0.5) (2019-05-16)\n\n\n\n<a name=\"3.0.4\"></a>\n## [3.0.4](https://github.com/researchgate/react-intersection-list/compare/v3.0.3...v3.0.4) (2019-04-25)\n\n\n### Bug Fixes\n\n* **reobserve:** using externalUnobserver to operate on the right target ([f6d9dc2](https://github.com/researchgate/react-intersection-list/commit/f6d9dc2))\n\n\n\n<a name=\"3.0.3\"></a>\n## [3.0.3](https://github.com/researchgate/react-intersection-list/compare/v3.0.2...v3.0.3) (2019-04-23)\n\n\n\n<a name=\"3.0.2\"></a>\n## [3.0.2](https://github.com/researchgate/react-intersection-list/compare/v3.0.1...v3.0.2) (2018-06-18)\n\n\n\n<a name=\"3.0.1\"></a>\n## [3.0.1](https://github.com/researchgate/react-intersection-list/compare/v3.0.0...v3.0.1) (2018-05-28)\n\n\n\n<a name=\"3.0.0\"></a>\n# [3.0.0](https://github.com/researchgate/react-intersection-list/compare/v1.0.2...v3.0.0) (2018-05-15)\n\n\n### Features\n\n* **prop_api_update:** introducing API for 2.0.0 ([276ca7b](https://github.com/researchgate/react-intersection-list/commit/276ca7b))\n* **React16.4:** Migrated codebase to stop using legacy lifecycles ([ab7fc85](https://github.com/researchgate/react-intersection-list/commit/ab7fc85))\n\n\n### BREAKING CHANGES\n\n* **prop_api_update:** * Replace `currentLength` with more descriptive `itemCount` prop\n* New render prop `renderItem` to completement `children` as render prop\n* New prop `items` as a unique identifier/symbol to provide an easier integration of reusable lists\n* TypeScript Module definitions\n\n\n\n<a name=\"2.0.0\"></a>\n# [2.0.0](https://github.com/researchgate/react-intersection-list/compare/v1.0.2...v2.0.0) (2018-05-09)\n\n\n### Features\n\n* **prop_api_update:** introducing API for 2.0.0\n\n\n### BREAKING CHANGES\n\n* **prop_api_update:** * Replace `currentLength` with more descriptive `itemCount` prop\n* New render prop `renderItem` to completement `children` as render prop\n* New prop `items` as a unique identifier/symbol to provide an easier integration of reusable lists\n* TypeScript Module definitions\n\n\n\n<a name=\"1.0.2\"></a>\n## [1.0.2](https://github.com/researchgate/react-intersection-list/compare/v1.0.1...v1.0.2) (2018-04-12)\n\n\n\n<a name=\"1.0.1\"></a>\n## [1.0.1](https://github.com/researchgate/react-intersection-list/compare/v1.0.0...v1.0.1) (2018-02-26)\n\n\n\n<a name=\"1.0.0\"></a>\n# [1.0.0](https://github.com/researchgate/react-intersection-list/compare/v0.4.1...v1.0.0) (2017-11-24)\n\n\n### Features\n\n* **React16:** support for React 16 ([12ba423](https://github.com/researchgate/react-intersection-list/commit/12ba423))\n* **setRootNode:** save call to getComputedStyle if root node is unchanged ([80659dd](https://github.com/researchgate/react-intersection-list/commit/80659dd))\n\n\n### BREAKING CHANGES\n\n* **React16:** <sentinel> tag replaced by a <span> tag.\n* **React16:** deprecation warning will not longer appear for itemsLength prop.\n\n\n\n<a name=\"0.4.1\"></a>\n## [0.4.1](https://github.com/researchgate/react-intersection-list/compare/v0.4.0...v0.4.1) (2017-10-11)\n\n\n* Update @researchgate/react-intersection-observer dependency to the latest version\n\n\n\n<a name=\"0.4.0\"></a>\n# [0.4.0](https://github.com/researchgate/react-intersection-list/compare/v0.3.2...v0.4.0) (2017-10-10)\n\n\n### Features\n\n* **currentLength:** replace itemsLength with more explicit currentLength prop name ([de19504](https://github.com/researchgate/react-intersection-list/commit/de19504))\n\n\n\n<a name=\"0.3.2\"></a>\n## [0.3.2](https://github.com/researchgate/react-intersection-list/compare/v0.3.1...v0.3.2) (2017-09-23)\n\n\n### Bug Fixes\n\n* **dependencies:** Remove rimraf from dependencies ([b78adde](https://github.com/researchgate/react-intersection-list/commit/b78adde))\n\n\n\n<a name=\"0.3.1\"></a>\n## [0.3.1](https://github.com/researchgate/react-intersection-list/compare/v0.3.0...v0.3.1) (2017-09-20)\n\n\n\n<a name=\"0.3.0\"></a>\n# 0.3.0 (2017-09-14)\n\n\n### Features\n\n* **core_dep_update:** update react-intersection-observer to latest version ([201c2d1](https://github.com/researchgate/react-intersection-list/commit/201c2d1))\n\n\n\n<a name=\"0.1.1\"></a>\n## [0.1.1](https://github.com/researchgate/react-intersection-list/compare/v0.1.0...v0.1.1) (2017-09-12)\n\n\n### Bug Fixes\n\n* **sentinel:** ensure sentinel re-observes after items render ([e6d0fa5](https://github.com/researchgate/react-intersection-list/commit/e6d0fa5))\n\n\n\n<a name=\"0.1.0\"></a>\n# 0.1.0 (2017-09-11)\n\n\n### Features\n\n* **awaitMore:** improved API with awaitMore ([6c58e95](https://github.com/researchgate/react-intersection-list/commit/6c58e95))\n* **hasMore:** prop that allows to bypass the length-size check ([f67ab3d](https://github.com/researchgate/react-intersection-list/commit/f67ab3d))\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) @researchgate/react-intersection-list authors\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"
  },
  {
    "path": "README.md",
    "content": "⚠️ This repository is not as actively maintained as we wish it to be. Feel free to fork this project and fix any outstanding issues you might have, and we'll try to merge relevant changes eventually. We apologize for the inconvenience.\n\n<p align=\"center\">\n  <img alt=\"React Intersection List\" src=\".github/logo.svg\" width=\"888\">\n</p>\n\n<p align=\"center\">\n  <a href=\"https://travis-ci.com/researchgate/react-intersection-list\"><img alt=\"Build Status\" src=\"https://travis-ci.com/researchgate/react-intersection-list.svg?branch=master\"></a>\n  <a href=\"https://codecov.io/gh/researchgate/react-intersection-list\"><img alt=\"Codecov\" src=\"https://img.shields.io/codecov/c/github/researchgate/react-intersection-list.svg\"></a>\n  <a href=\"https://www.npmjs.com/package/@researchgate/react-intersection-list\"><img alt=\"NPM version\" src=\"https://img.shields.io/npm/v/@researchgate/react-intersection-list.svg\"></a>\n  <a href=\"https://github.com/prettier/prettier\"><img alt=\"styled with prettier\" src=\"https://img.shields.io/badge/styled_with-prettier-ff69b4.svg\"></a>\n</p>\n\n<br>\n\n> **Agent Smith:** ...we have no choice but to continue as planned. Deploy the\n> sentinels. Immediately.\n\n**React Intersection List** builds on top of\n**[React Intersection Observer](https://github.com/researchgate/react-intersection-observer)**,\nusing a [sentinel](https://en.wikipedia.org/wiki/Sentinel_value) in the DOM to\ndeliver a high-performance and smooth scrolling experience, even on low-end\ndevices.\n\n<br>\n\n<details>\n<summary><strong>Table of Contents</strong></summary>\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Getting Started](#getting-started)\n- [Why React Intersection List?](#why-react-intersection-list)\n- [Documentation](#documentation)\n  - [How to](#how-to)\n  - [FAQ](#faq)\n  - [Props](#props)\n  - [Examples](#examples)\n- [Contributing](#contributing)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n</details>\n\n## Getting Started\n\n```\n$ npm install --save @researchgate/react-intersection-list\n```\n\nAnd optionally the\n[polyfill](https://github.com/w3c/IntersectionObserver/tree/gh-pages/polyfill):\n\n```\n$ npm install --save intersection-observer\n```\n\nNext create a `<List>` and two instance methods as props `children` and\n`itemRenderer`:\n\n```jsx\nimport React, { Component } from 'react';\nimport List from '@researchgate/react-intersection-list';\n\nexport default class InfiniteList extends Component {\n  itemsRenderer = (items, ref) => (\n    <ul className=\"list\" ref={ref}>\n      {items}\n    </ul>\n  );\n\n  itemRenderer = (index, key) => <li key={key}>{index}</li>;\n\n  render() {\n    return (\n      <List\n        itemCount={1000}\n        itemsRenderer={this.itemsRenderer}\n        renderItem={this.itemRenderer}\n      />\n    );\n  }\n}\n```\n\nNote that `<List>` is a `PureComponent` so it can keep itself from re-rendering.\nIt's highly recommended to avoid creating new functions for `renderItem` and\n`itemsRenderer` so that it can successfully shallow compare props on re-render.\n\n## Why React Intersection List?\n\nThe approach to infinite scrolling was commonly done by devs implementing\nthrottled `scroll` event callbacks. This keeps the main thread unnecessarily\nbusy... No more! `IntersectionObservers` invoke callbacks in a **low-priority\nand asynchronous** way by design.\n\n> **Agent Smith:** Never send a human to do a machine's job.\n\nThe implementation follows these steps:\n\n1. Add a sentinel close to the last item in the list\n2. Update the list moving the internal cursor\n3. Trigger a callback when the sentinel comes into view\n4. Reposition the recycled sentinel at the end\n5. Repeat (∞) ?\n\n## Documentation\n\n### How to\n\nProvided an `itemsRenderer` prop you must attach the `ref` argument to your\nscrollable DOM element:\n\n```jsx\n<div ref={ref}>{items}</div>\n```\n\nThis element specifies `overflow: auto|scroll` and it'll become the\n`IntersectionObserver root`. If the `overflow` property isn't found, then\n`window` will be used as the `root` instead.\n\nThe `sentinel` element is by default detached from the list when the current\nsize reaches the available length, unless you're using `awaitMore`. In case your\nlist is in memory and you rely on the list for incremental rendering only, the\ndefault detaching behavior suffices. If you're loading more items in an\nasynchoronous way, make sure you switch `awaitMore` once you reach the total\nlength (bottom of the list).\n\nThe prop `itemCount` must be used if the prop `items` is not provided, and\nviceversa. Calculating the list size is done by adding the current size and the\npage size until the items' length is reached.\n\n### FAQ\n\n<details>\n  <summary>Why am I receiving too many `onIntersection` callbacks</summary>\n  We extend `PureComponent`. That means, if the parent component re-renders and the _props_ passed to your `<List />` don't\nhold the same reference anymore, the list re-renders and we accidentally restart the `IntersectionObserver` of the `Sentinel`.\n</details>\n<br />\n<details>\n  <summary>Do I always need to assign the `ref`?</summary>\n  Yes, the ref callback will be used as the `root` and is forwarded to the `IntersectionObserver` within the `Sentinel`.\n</details>\n<br />\n<details>\n  <summary>What's the `threshold` value, and why does it need a *unit*?</summary>\n  The `threshold` value is the amount of space needed before the `sentinel` intersects with the root. The prop is\ntransformed into a valid `rootMargin` property for the `IntersectionObserver`, depending on the `axis` you select. As a\nsidenote, we believe that a percentage unit works best for responsive layouts.\n</details>\n<br />\n<details>\n  <summary>I am getting a console warning when I first load the list</summary>\n  <blockquote>The sentinel detected a viewport with a bigger size than the size of its items...</blockquote>\nThe prop `pageSize` is `10` by default, so make sure you're not falling short on items when you first render the\ncomponent. The idea of an infinite scrolling list is that items overflow the viewport, so that users have the impression that there're always more items available.\n</details>\n<br />\n<details>\n  <summary>Why doesn't the list render my updated list element(s)?</summary>\n  The list renders items based on its props. An update somewhere else in your app (or within your list item) might update\nyour list element(s), but if your list's `currentLength` prop for instance, remains unchanged, the list prevents a\nre-render. Updating the entire infinite list when one of its items has changed is far from optimal. Instead, update each item individually with some form of `connect()` function or observables.\n</details>\n<br />\n<details>\n  <summary>Are you planning to implement a \"virtual list mode\" like react-virtualized?</summary>\n  Yes, there's already an [open issue](https://github.com/researchgate/react-intersection-list/issues/2) to implement a\nmode using occlusion culling. It will be implemented in a future release. If you can't wait, you could help us out by opening a Pull Request :)\n</details>\n\n### Props\n\n| property              | type                                                               | default                                        | description                                                                                             |\n| --------------------- | ------------------------------------------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| `renderItem/children` | `(index: number, key: number) => React.Element`                    | `(index, key) => <div key={key}>{index}</div>` | render function as children or render props;<br />gets call once for each item.                         |\n| `itemsRenderer`       | `(items: Array(React.Element), ref: HTMLElement) => React.Element` | `(items, ref) => <div ref={ref}>{items}</div>` | render function for the list's<br />root element, often returning a scrollable element.                 |\n| `itemCount/items`     | `number/Array (or Iterable Object)`                                | `0`                                            | item count to render.                                                                                   |\n| `awaitMore`           | `boolean`                                                          |                                                | if true keeps the sentinel from detaching.                                                              |\n| `onIntersection`      | `(size: number, pageSize: number) => void`                         |                                                | invoked when the sentinel comes into view.                                                              |\n| `threshold`           | `string`                                                           | `100px`                                        | value in absolute `px` or `%`<br />as spacing before the sentinel hits the edge of the list's viewport. |\n| `axis`                | `string`                                                           | `y`                                            | scroll direction: `y` == vertical and `x` == horizontal                                                 |\n| `pageSize`            | `number`                                                           | `10`                                           | number of items to render each hit.                                                                     |\n| `initialIndex`        | `number`                                                           | `0`                                            | start position of iterator of items.                                                                    |\n\n### Examples\n\nFind multiple examples under:\n[https://researchgate.github.io/react-intersection-list/](https://researchgate.github.io/react-intersection-list/)\n\n## Contributing\n\nWe'd love your help on creating React Intersection List!\n\nBefore you do, please read our [Code of Conduct](.github/CODE_OF_CONDUCT.md) so\nyou know what we expect when you contribute to our projects.\n\nOur [Contributing Guide](.github/CONTRIBUTING.md) tells you about our\ndevelopment process and what we're looking for, gives you instructions on how to\nissue bugs and suggest features, and explains how you can build and test your\nchanges.\n\n**Haven't contributed to an open source project before?** No problem!\n[Contributing Guide](.github/CONTRIBUTING.md) has you covered as well.\n"
  },
  {
    "path": "docs/README.md",
    "content": "## Documentation\n\nWelcome to the code docs section! Here you'll find some implementation use cases:\n\n* Visit our [examples](https://researchgate.github.io/react-intersection-list/) page\n* Take a look at the [recipes](./recipes) page"
  },
  {
    "path": "docs/docs/components/AsyncList/index.js",
    "content": "import React, { Component } from 'react';\nimport List from '../../../../src';\n\nconst PAGE_SIZE = 20;\n\nexport default class AsyncList extends Component {\n    state = {\n        awaitMore: true,\n        isLoading: false,\n        currentPage: 0,\n        repos: [],\n    };\n\n    feedList(repos) {\n        this.setState({\n            awaitMore: repos.length > 0,\n            isLoading: false,\n            repos: [...this.state.repos, ...repos],\n        });\n    }\n\n    handleLoadMore = () => {\n        const currentPage = this.state.currentPage + 1;\n\n        this.setState({\n            isLoading: true,\n            currentPage,\n        });\n\n        const url = 'https://api.github.com/users/researchgate/repos';\n        const qs = `?type=public&per_page=${PAGE_SIZE}&page=${currentPage}`;\n\n        const headers = {\n            'Accept-Encoding': '',\n        };\n        const ifNoneMatch = sessionStorage.getItem(`etag_${currentPage}`);\n        if (ifNoneMatch) {\n            headers['If-None-Match'] = ifNoneMatch.match(/(?:\\d|[a-z])+/)[0];\n        }\n\n        let hasError = false;\n\n        fetch(url + qs, { headers })\n            .then((response) => {\n                if (!(response.status !== 200)) {\n                    const etag = response.headers.get('etag');\n                    if (etag) {\n                        sessionStorage.setItem(`etag_${currentPage}`, etag);\n                    }\n                } else {\n                    hasError = true;\n                }\n                return response.json();\n            })\n            .then((repos) => {\n                if (hasError) {\n                    throw new Error(repos.message);\n                }\n                this.feedList(\n                    repos.filter((repo) => repo.fork === false && repo.language)\n                );\n            })\n            .catch((err) => {\n                console.error(err); // eslint-disable-line\n                this.feedList([]);\n            });\n    };\n\n    renderItems = (items, ref) => (\n        <div className=\"list list--compact\" ref={ref}>\n            {items}\n        </div>\n    );\n\n    renderItem = (index, key) => {\n        const repo = this.state.repos[index];\n        return (\n            <div key={key}>\n                <a href={repo.html_url} target=\"_blank\">\n                    <strong>{repo.name}</strong>\n                </a>\n                &lt;{repo.language}&gt;\n            </div>\n        );\n    };\n\n    render() {\n        return (\n            <div>\n                {this.state.isLoading && <div className=\"loading\">Loading</div>}\n                <List\n                    awaitMore={this.state.awaitMore}\n                    itemsRenderer={this.renderItems}\n                    itemCount={this.state.repos.length}\n                    onIntersection={this.handleLoadMore}\n                    pageSize={PAGE_SIZE}\n                    renderItem={this.renderItem}\n                />\n            </div>\n        );\n    }\n}\n"
  },
  {
    "path": "docs/docs/components/Axis/index.js",
    "content": "import React from 'react';\nimport List from '../../../../src';\n\nconst itemsRenderer = (items, ref) => (\n    <div className=\"list list--horizontal\" ref={ref}>\n        {items}\n    </div>\n);\n\n// eslint-disable-next-line react/no-multi-comp\nconst Axis = () => (\n    <List\n        axis=\"x\"\n        itemCount={Infinity}\n        itemsRenderer={itemsRenderer}\n        pageSize={40}\n    />\n);\n\nexport default Axis;\n"
  },
  {
    "path": "docs/docs/components/InfiniteList/index.js",
    "content": "import React from 'react';\nimport List from '../../../../src';\n\nconst itemsRenderer = (items, ref) => (\n    <div className=\"list\" ref={ref}>\n        {items}\n    </div>\n);\n\n// eslint-disable-next-line react/no-multi-comp\nconst InfiniteList = () => (\n    <List itemCount={Infinity} itemsRenderer={itemsRenderer} pageSize={40} />\n);\n\nexport default InfiniteList;\n"
  },
  {
    "path": "docs/docs/components/style.css",
    "content": "body {\n  font-family: -apple-system, '.SFNSText-Regular', 'San Francisco', Roboto, 'Segoe UI', 'Helvetica Neue',\n    'Lucida Grande', sans-serif;\n  margin: 0;\n  padding: 0;\n  color: #555;\n  -webkit-font-smoothing: antialiased;\n  min-height: 100%;\n}\n\n.loading {\n  background: #0080ff;\n  color: #fff;\n  height: 40px;\n  font-size: 20px;\n  line-height: 40px;\n  text-transform: capitalize;\n  font-weight: bold;\n  text-align: center;\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: 1;\n  pointer-events: none;\n}\n\n.list {\n  box-sizing: border-box;\n  height: 100vh;\n  background-color: #b2d2ed;\n  overflow: auto;\n  position: relative;\n}\n\n.list--horizontal {\n  width: 100%;\n  display: flex;\n  overflow-y: visible;\n  flex-direction: row;\n}\n\n.list--horizontal > div {\n  min-width: 100px;\n  text-align: center;\n}\n\n.list--compact {\n  height: 333px;\n}\n\n.list > div {\n  background-color: #4b9deb;\n  color: #fff;\n  margin: 15px;\n  padding: 15px;\n  font-size: 16px;\n  text-transform: capitalize;\n  cursor: default;\n  white-space: nowrap;\n}\n\n.list > div > a {\n  color: white;\n  text-decoration: none;\n  margin-right: 8px;\n}\n\n.list > div > a:hover {\n  text-decoration: underline;\n}\n"
  },
  {
    "path": "docs/docs/index.js",
    "content": "import 'intersection-observer'; // eslint-disable-line import/no-extraneous-dependencies\nimport 'whatwg-fetch'; // eslint-disable-line import/no-extraneous-dependencies\nimport React from 'react';\nimport { storiesOf } from '@storybook/react'; // eslint-disable-line import/no-extraneous-dependencies\nimport InfiniteList from './components/InfiniteList';\nimport AsyncList from './components/AsyncList';\nimport Axis from './components/Axis';\nimport './components/style.css';\n\nstoriesOf('Examples', module)\n    .add('Synchoronous', InfiniteList)\n    .add('Asynchoronous', () => <AsyncList />)\n    .add('X-Axis', Axis);\n"
  },
  {
    "path": "docs/recipes/README.md",
    "content": "## Recipes\n\n### Asynchonous Repo List\n\nWhen the sentinel comes into view, you can use the callback to load data, create the next items, and attach them. For\nthis case we're loading Github repositories with pagination. We assume that we don't know the total length and we'll\nwant to keep fetching until the (unknown) end of the list. The solution here is to pass the prop `awaitMore:bool =\ntrue`, so that the sentinel awaits for more items.\n\n```jsx\nimport React from 'react';\nimport List from '@researchgate/react-intersection-list';\n\nconst PAGE_SIZE = 20;\n\nexport default class extends React.Component {\n    state = {\n        awaitMore: true,\n        isLoading: false,\n        currentPage: 0,\n        repos: [],\n    };\n\n    feedList = repos => {\n        this.setState({\n            awaitMore: repos.length > 0,\n            isLoading: false,\n            repos: [...this.state.repos, ...repos],\n        });\n    };\n\n    handleLoadMore = () => {\n        const currentPage = this.state.currentPage + 1;\n\n        this.setState({\n            isLoading: true,\n            currentPage,\n        });\n\n        const url = 'https://api.github.com/users/researchgate/repos';\n        const qs = `?type=public&per_page=${PAGE_SIZE}&page=${currentPage}`;\n\n        fetch(url + qs)\n            .then(response => response.json())\n            .then(this.feedList)\n            .catch(err => {\n                throw err;\n            });\n    };\n\n    renderItems = (items, ref) => (\n        <div className=\"list\" ref={ref}>\n            {items}\n        </div>\n    );\n\n    renderItem = (index, key) => {\n        const repo = this.state.repos[index];\n        return (\n            <div key={key}>\n                <strong>{repo.name}</strong>\n            </div>\n        );\n    };\n\n    render() {\n        return (\n            <div>\n                {this.state.isLoading && <div className=\"loading\">Loading</div>}\n                <List\n                    awaitMore={this.state.awaitMore}\n                    itemsRenderer={this.renderItems}\n                    itemCount={this.state.repos.length}\n                    onIntersection={this.handleLoadMore}\n                    pageSize={PAGE_SIZE}\n                    renderItem={this.renderItem}\n                />\n            </div>\n        );\n    }\n}\n```\n\nIf the total amount of items are prefetched and available, we won't need `awaitMore` and the `pageSize` will be used to\npaginate results until we reach the bottom of the list.\n\n### Infinite Synchronous List\n\n```jsx\nimport React from 'react';\nimport List from '@researchgate/react-intersection-list';\n\nexport default () => <List itemCount={Infinity}>{(index, key) => <div key={key}>{index}</div>}</List>;\n```\n\n### Can I submit a new recipe?\n\nYes, of course!\n\n1. Fork the code repo.\n2. Create your new recipe in the correct subfolder within `./docs/docs/components/` (create a new folder if it doesn't\n   already exist).\n3. Make sure you have included a README as well as your source file.\n4. Submit a PR.\n\n_If you haven't yet, please read our\n[contribution guidelines](https://github.com/researchgate/react-intersection-list/blob/master/.github/CONTRIBUTING.md)._\n\n### What license are the recipes released under?\n\nBy default, all newly submitted code is licensed under the MIT license.\n\n### How else can I contribute?\n\nRecipes don't always have to be code - great documentation, tutorials, general tips and even general improvements to our\nexamples folder are greatly appreciated.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@researchgate/react-intersection-list\",\n  \"description\": \"React List Component using the Intersection Observer API\",\n  \"version\": \"3.0.12\",\n  \"author\": \"Luis Merino <mail@luismerino.name>\",\n  \"engines\": {\n    \"node\": \">=10.0.0\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/researchgate/react-intersection-list/issues\"\n  },\n  \"dependencies\": {\n    \"@researchgate/react-intersection-observer\": \"^1.1.2\",\n    \"prop-types\": \"^15.7.2\",\n    \"warning\": \"^4.0.3\"\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"7.20.7\",\n    \"@babel/core\": \"7.20.12\",\n    \"@researchgate/babel-preset\": \"2.0.14\",\n    \"@researchgate/spire-config\": \"7.0.0\",\n    \"@storybook/addon-options\": \"5.3.21\",\n    \"@storybook/react\": \"6.3.12\",\n    \"@types/react\": \"17.0.53\",\n    \"babel-loader\": \"8.3.0\",\n    \"cross-env\": \"7.0.3\",\n    \"intersection-observer\": \"0.12.2\",\n    \"react\": \"16.14.0\",\n    \"react-dom\": \"16.14.0\",\n    \"react-test-renderer\": \"16.14.0\",\n    \"spire\": \"4.1.2\",\n    \"spire-plugin-semantic-release\": \"4.1.0\",\n    \"typescript\": \"4.9.5\",\n    \"whatwg-fetch\": \"3.6.2\"\n  },\n  \"files\": [\n    \"lib\",\n    \"types/index.d.ts\"\n  ],\n  \"types\": \"types/index.d.ts\",\n  \"homepage\": \"https://github.com/researchgate/react-intersection-list#readme\",\n  \"keywords\": [\n    \"Intersection\",\n    \"Observer\",\n    \"react\",\n    \"component\",\n    \"list\",\n    \"infinite\",\n    \"scrollable\",\n    \"researchgate\"\n  ],\n  \"license\": \"MIT\",\n  \"main\": \"lib/js/index.js\",\n  \"module\": \"lib/es/index.js\",\n  \"peerDependencies\": {\n    \"react\": \"^16.3.2\",\n    \"react-dom\": \"^16.3.2\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/researchgate/react-intersection-list.git\"\n  },\n  \"jest\": {\n    \"rootDir\": \"src\",\n    \"testMatch\": [\n      \"**/__tests__/**/*.spec.js\"\n    ],\n    \"testURL\": \"http://localhost/\"\n  },\n  \"prettier\": \"@researchgate/prettier-config\",\n  \"spire\": {\n    \"extends\": [\n      [\n        \"@researchgate/spire-config\",\n        {\n          \"eslint\": \"react-typescript\"\n        }\n      ]\n    ],\n    \"plugins\": [\n      \"spire-plugin-semantic-release\",\n      \"<rootDir>/.spire/spire-plugin-tslint\"\n    ]\n  },\n  \"scripts\": {\n    \"build\": \"yarn build:js && yarn build:es\",\n    \"build:js\": \"cross-env BABEL_ENV=production BABEL_OUTPUT=cjs babel src --out-dir lib/js --ignore **/__tests__,**/__mocks__\",\n    \"build:es\": \"cross-env BABEL_ENV=production BABEL_OUTPUT=esm babel src --out-dir lib/es --ignore **/__tests__,**/__mocks__\",\n    \"build:storybook\": \"build-storybook -o .docs\",\n    \"lint\": \"spire lint\",\n    \"prepublishOnly\": \"yarn build\",\n    \"release\": \"spire release --branches main\",\n    \"storybook\": \"start-storybook -p 9001 -c .storybook\",\n    \"test\": \"spire test\",\n    \"ts:check\": \"tsc --project types\"\n  }\n}\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\"@researchgate:lib\"],\n  \"automergeType\": \"branch\",\n  \"labels\": [\"dependencies\"],\n  \"baseBranches\": [\"main\"]\n}\n"
  },
  {
    "path": "src/List.js",
    "content": "import React, { PureComponent } from 'react';\nimport PropTypes from 'prop-types';\nimport warning from 'warning';\nimport Sentinel from './Sentinel';\nimport { getItemCount, computeSize } from './utils';\n\nconst AXIS_CSS_MAP = { x: 'overflowX', y: 'overflowY' };\n\nclass List extends PureComponent {\n    static propTypes = {\n        awaitMore: PropTypes.bool,\n        axis: PropTypes.oneOf(['x', 'y']),\n        children: PropTypes.func,\n        initialIndex: PropTypes.number,\n        items(props, propName) {\n            const object = props[propName];\n            if (\n                object != null &&\n                !(\n                    Array.isArray(object) ||\n                    typeof object[Symbol.iterator] === 'function'\n                )\n            ) {\n                return new Error(\n                    `\\`${propName}\\` must be of type Array or a native type implementing the iterable interface`\n                );\n            }\n            return undefined;\n        },\n        itemCount: PropTypes.number,\n        itemsRenderer: PropTypes.func,\n        onIntersection: PropTypes.func,\n        pageSize: PropTypes.number,\n        renderItem: PropTypes.func,\n        threshold: PropTypes.string,\n    };\n\n    static defaultProps = {\n        axis: 'y',\n        initialIndex: 0,\n        itemsRenderer: (items, ref) => <div ref={ref}>{items}</div>,\n        pageSize: 10,\n        threshold: '100px',\n    };\n\n    state = {\n        size: 0,\n    };\n\n    static getDerivedStateFromProps({ pageSize, ...props }, prevState) {\n        const itemCount = getItemCount(props, true);\n\n        let newSize;\n        if (prevState.size === 0) {\n            newSize = pageSize;\n        } else {\n            if (prevState.itemCount < itemCount) {\n                newSize = prevState.size + pageSize;\n            } else {\n                newSize = prevState.size + -(prevState.pageSize - pageSize);\n            }\n        }\n\n        return {\n            pageSize,\n            itemCount,\n            size: computeSize(newSize, itemCount),\n        };\n    }\n\n    componentDidMount() {\n        this.prematureIntersectionChecked = this.state.size === 0;\n    }\n\n    setRef = (callback) => {\n        let prevRootNode;\n        this.setRootNode = (node) => {\n            if (node !== prevRootNode) {\n                prevRootNode = node;\n                const overflow = window.getComputedStyle(node)[\n                    AXIS_CSS_MAP[this.props.axis]\n                ];\n                callback(\n                    ['auto', 'scroll', 'overlay'].indexOf(overflow) !== -1\n                        ? node\n                        : null\n                );\n            }\n        };\n    };\n\n    handleUpdate = ({ isIntersecting }) => {\n        const { awaitMore, onIntersection } = this.props;\n        const { size, itemCount, pageSize } = this.state;\n\n        if (!this.prematureIntersectionChecked) {\n            this.prematureIntersectionChecked = true;\n            warning(\n                !isIntersecting,\n                'ReactIntersectionList: the sentinel detected a viewport with a bigger size than the size of its items. ' +\n                    'This could lead to detrimental behavior, e.g.: triggering more than one onIntersection callback at the start.\\n' +\n                    'To prevent this, use either a bigger `pageSize` value or avoid using the prop awaitMore initially.'\n            );\n        }\n\n        if (isIntersecting) {\n            const nextSize = computeSize(size + pageSize, itemCount);\n            if (size !== nextSize) {\n                this.setState({ size: nextSize });\n            }\n            if (onIntersection && (!awaitMore || this.awaitIntersection)) {\n                if (this.awaitIntersection) {\n                    this.awaitIntersection = false;\n                }\n                onIntersection(nextSize, pageSize);\n            }\n        }\n    };\n\n    getItemRenderer() {\n        const { children, renderItem } = this.props;\n        const hasChildren = typeof children !== 'undefined';\n        const hasRender = typeof renderItem !== 'undefined';\n\n        warning(\n            !(hasChildren && hasRender),\n            'ReactIntersectionList: cannot use children and renderItem props as render function at the same time.'\n        );\n\n        if (hasChildren) {\n            return children;\n        }\n\n        return hasRender\n            ? renderItem\n            : (index, key) => <div key={key}>{index}</div>;\n    }\n\n    renderItems() {\n        const {\n            awaitMore,\n            axis,\n            initialIndex,\n            itemsRenderer,\n            threshold,\n        } = this.props;\n        const { size, itemCount } = this.state;\n        const itemRenderer = this.getItemRenderer();\n        const items = [];\n\n        for (let i = 0; i < size; ++i) {\n            items.push(itemRenderer(initialIndex + i, i));\n        }\n\n        let sentinel;\n        if (size < itemCount || awaitMore) {\n            sentinel = (\n                <Sentinel\n                    key=\"sentinel\"\n                    threshold={threshold}\n                    axis={axis}\n                    setRef={this.setRef}\n                    onChange={this.handleUpdate}\n                />\n            );\n            items.push(sentinel);\n\n            if (awaitMore) {\n                this.awaitIntersection = true;\n            }\n        }\n\n        return itemsRenderer(items, (node) => {\n            if (node && sentinel) {\n                this.setRootNode(node);\n            }\n        });\n    }\n\n    render() {\n        return this.renderItems();\n    }\n}\n\nexport default List;\n"
  },
  {
    "path": "src/Sentinel.js",
    "content": "import React, { Component } from 'react';\nimport Observer from '@researchgate/react-intersection-observer';\nimport PropTypes from 'prop-types';\nimport { computeRootMargin } from './utils';\n\nclass Sentinel extends Component {\n    static propTypes = {\n        axis: PropTypes.oneOf(['x', 'y']).isRequired,\n        threshold: PropTypes.string.isRequired,\n        setRef: PropTypes.func.isRequired,\n        onChange: PropTypes.func.isRequired,\n    };\n\n    constructor(props) {\n        super(props);\n\n        this.state = {\n            rootElement: undefined,\n            rootMargin: computeRootMargin(props),\n        };\n\n        this.target = (\n            <template style={{ height: 1, minWidth: 1, display: 'block' }} />\n        );\n\n        props.setRef(this.setRootElement);\n    }\n\n    static getDerivedStateFromProps({ threshold, axis }, prevState) {\n        let newState = null;\n        if (threshold !== prevState.threshold || axis !== prevState.axis) {\n            newState = {\n                threshold,\n                axis,\n                rootMargin: computeRootMargin({ threshold, axis }),\n            };\n        }\n        return newState;\n    }\n\n    shouldComponentUpdate(nextProps, { rootMargin, rootElement }) {\n        const {\n            rootMargin: currentRootMargin,\n            rootElement: currentRootElement,\n        } = this.state;\n        // When the rootMargin stays the same but the sentinel is repositioned, it can fall within\n        // its threshold prematurely. In this case we don't get any update from the Observer instance.\n        // We need to guarantee an update, and re-observing is a cheap way to accomplish this.\n        if (\n            currentRootMargin === rootMargin &&\n            currentRootElement === rootElement\n        ) {\n            this.observer.externalUnobserve();\n            this.observer.observe();\n            return false;\n        }\n        return true;\n    }\n\n    setRootElement = (rootElement) => {\n        this.setState({ rootElement });\n    };\n\n    render() {\n        const { onChange } = this.props;\n        const { rootElement, rootMargin } = this.state;\n\n        return (\n            <Observer\n                ref={(instance) => {\n                    this.observer = instance;\n                }}\n                disabled={typeof rootElement === 'undefined'}\n                root={rootElement}\n                rootMargin={rootMargin}\n                onChange={onChange}\n            >\n                {this.target}\n            </Observer>\n        );\n    }\n}\n\nexport default Sentinel;\n"
  },
  {
    "path": "src/__mocks__/Sentinel.js",
    "content": "/* eslint-env jest */\nexport default jest.genMockFromModule(\n    '@researchgate/react-intersection-observer'\n).default;\n"
  },
  {
    "path": "src/__tests__/List.spec.js",
    "content": "/* eslint-env jest */\nimport 'intersection-observer';\nimport React from 'react';\nimport renderer from 'react-test-renderer';\nimport List from '../';\nimport { getItemCount } from '../utils';\nimport mockSentinel, { mockCallback } from './mockSentinel';\n\njest.mock('../Sentinel', () => (props) => {\n    if (props.setRef) {\n        props.setRef(mockCallback);\n    }\n    return mockSentinel;\n});\n\nconst target = { nodeType: 1 };\nconst createTree = (props = {}) =>\n    renderer.create(<List {...props} />, { createNodeMock: () => target });\n\nbeforeEach(() => {\n    jest.clearAllMocks();\n\n    window.getComputedStyle = jest.fn(() => ({\n        overflowX: 'auto',\n        overflowY: 'auto',\n    }));\n});\n\ntest('renders without crashing', () => {\n    createTree();\n});\n\ntest('pure component avoids unnecessary re-rendering', () => {\n    const props = { pageSize: 5, itemCount: 25 };\n    const tree = createTree(props);\n    const spy = jest.spyOn(tree.getInstance(), 'render');\n    tree.update(<List {...props} />);\n    expect(spy).not.toHaveBeenCalled();\n});\n\ntest('throws with two different render function props', () => {\n    const spy = global.spyOn(console, 'error');\n    createTree({ children() {}, renderItem() {}, itemCount: 2 });\n    expect(spy.calls.mostRecent().args[0]).toMatchSnapshot();\n});\n\ntest('render children function', () => {\n    const props = { children() {}, itemCount: 2 };\n    const spy = jest.spyOn(props, 'children');\n    createTree(props);\n    expect(spy).toHaveBeenCalledTimes(2);\n});\n\ntest('render prop function', () => {\n    const props = { renderItem() {}, itemCount: 2 };\n    const spy = jest.spyOn(props, 'renderItem');\n    createTree(props);\n    expect(spy).toHaveBeenCalledTimes(2);\n});\n\ntest('throws with both itemCount and items props set', () => {\n    const spy = global.spyOn(console, 'error');\n    createTree({ items: 666 });\n    expect(spy.calls.mostRecent().args[0]).toMatchSnapshot();\n});\n\ntest('render with items instead of itemCount', () => {\n    const json = createTree({ items: [1, 2] }).toJSON();\n    const children = json.children;\n    expect(children.length).toBe(2);\n});\n\ntest('render zero items if no props are given for count calculation', () => {\n    expect(getItemCount({})).toBe(0);\n});\n\ntest('render with iterable Set', () => {\n    const items = new Set([1, 2, 3]);\n    const json = createTree({ items }).toJSON();\n    const children = json.children;\n    expect(children.length).toBe(3);\n});\n\ndescribe('renderItem', () => {\n    test('renderItem given default props', () => {\n        const spy = jest.fn();\n        createTree({ itemCount: 10, renderItem: spy });\n        expect(spy).toHaveBeenCalledTimes(10);\n        expect(spy).lastCalledWith(9, 9);\n    });\n\n    test('renderItem given `pageSize` and `initialIndex` props', () => {\n        const spy = jest.fn();\n        createTree({\n            initialIndex: 10,\n            itemCount: 30,\n            pageSize: 15,\n            renderItem: spy,\n        });\n        expect(spy).toHaveBeenCalledTimes(15);\n        expect(spy).lastCalledWith(24, 14);\n    });\n});\n\ndescribe('sentinel', () => {\n    test('sentinel present if items are available in view', () => {\n        const json = createTree({ itemCount: 10, pageSize: 5 }).toJSON();\n        const children = json.children;\n        expect(children.length).toBe(6);\n        expect(children[children.length - 1].type).toBe('span');\n    });\n\n    test('sentinel not present if no items are available in view', () => {\n        const json = createTree({ itemCount: 5, pageSize: 5 }).toJSON();\n        const children = json.children;\n        expect(children.length).toBe(5);\n        expect(children[children.length - 1].type).toBe('div');\n    });\n\n    test('sentinel present again if `itemCount` is increased', () => {\n        const tree = createTree({ itemCount: 5, pageSize: 5 });\n        expect(tree.toJSON().children.length).toBe(5);\n        tree.update(<List pageSize={5} itemCount={20} />);\n        expect(tree.toJSON().children.length).toBe(11);\n    });\n\n    test('sentinel present again if `pageSize` is increased', () => {\n        const tree = createTree({ itemCount: 100, pageSize: 10 });\n        expect(tree.toJSON().children.length).toBe(11);\n        tree.update(<List itemCount={100} pageSize={20} />);\n        expect(tree.toJSON().children.length).toBe(21);\n    });\n\n    test('sentinel not present if `itemCount` is lower than `pageSize`', () => {\n        const tree = createTree({ itemCount: 0 });\n        const children = tree.toJSON().children;\n        expect(children).toBeNull();\n        tree.update(<List itemCount={8} />);\n        const newChildren = tree.toJSON().children;\n        expect(newChildren.length).toBe(8);\n        expect(newChildren[newChildren.length - 1].type).toBe('div');\n    });\n\n    test('sentinel observes with `awaitMore` bypassing the `itemCount` check', () => {\n        const tree = createTree({ itemCount: 5 });\n        const children = tree.toJSON().children;\n        expect(children.length).toBe(5);\n        tree.update(<List itemCount={5} awaitMore={true} />);\n        const newChildren = tree.toJSON().children;\n        expect(newChildren.length).toBe(6);\n        expect(newChildren[newChildren.length - 1].type).toBe('span');\n    });\n});\n\ndescribe('root node', () => {\n    test('ref callback sets root node', () => {\n        createTree({ itemCount: 20 });\n        expect(mockCallback).toBeCalledWith(target);\n    });\n\n    test('ref callback does sets root node if unmounting', () => {\n        renderer.create(<List itemCount={20} />, {\n            createNodeMock: () => undefined,\n        });\n        expect(mockCallback).not.toBeCalled();\n    });\n\n    test('ref callback does sets root node without sentinel', () => {\n        const tree = createTree({ itemCount: 10 });\n        expect(tree.getInstance().setRootNode).toBeUndefined();\n    });\n\n    test('ref callback sets a null root if it does not have overflow', () => {\n        window.getComputedStyle = jest.fn(() => ({\n            overflowY: 'visible',\n        }));\n        createTree({ itemCount: 20 });\n        expect(mockCallback).toBeCalledWith(null);\n    });\n\n    test('having same root node prevents call getComputedStyle', () => {\n        const tree = createTree({ itemCount: 20 });\n        tree.getInstance().setRootNode(target);\n        expect(window.getComputedStyle).toHaveBeenCalledTimes(1);\n    });\n});\n\ndescribe('intersection', () => {\n    test('does not throw if sentinel intersects with zero items on mount', () => {\n        const spy = global.spyOn(console, 'error');\n        const instance = createTree({ itemCount: 0 }).getInstance();\n        instance.handleUpdate({ isIntersecting: true });\n        expect(spy.calls.count()).toBe(0);\n    });\n\n    test('throws once if sentinel intersects with items on mount', () => {\n        const spy = global.spyOn(console, 'error');\n        const instance = createTree({ itemCount: 10 }).getInstance();\n        instance.handleUpdate({ isIntersecting: true });\n        expect(spy.calls.mostRecent().args[0]).toMatchSnapshot();\n        instance.handleUpdate({ isIntersecting: true });\n        expect(spy.calls.count()).toBe(1);\n        createTree({ itemCount: 0 })\n            .getInstance()\n            .handleUpdate({ isIntersecting: true });\n        expect(spy.calls.count()).toBe(1);\n    });\n\n    test('sets next size value computed into `pageSize`', () => {\n        const instance = createTree({ itemCount: 20 }).getInstance();\n        instance.handleUpdate({ isIntersecting: false });\n        instance.handleUpdate({ isIntersecting: true });\n        expect(instance.state.size).toBe(20);\n    });\n\n    test('sets next size value computed into `itemCount`', () => {\n        const instance = createTree({ itemCount: 15 }).getInstance();\n        instance.handleUpdate({ isIntersecting: false });\n        instance.handleUpdate({ isIntersecting: true });\n        expect(instance.state.size).toBe(15);\n    });\n\n    test('invokes `onIntersection` each time when it is not awaiting intersection', () => {\n        const spy = jest.fn();\n        const instance = createTree({\n            itemCount: 30,\n            onIntersection: spy,\n        }).getInstance();\n        instance.handleUpdate({ isIntersecting: false });\n        instance.handleUpdate({ isIntersecting: true });\n        instance.handleUpdate({ isIntersecting: true });\n        expect(instance.state.size).toBe(30);\n        expect(spy).toHaveBeenCalledTimes(2);\n    });\n\n    test('invokes `onIntersection` only once when it is awaiting intersection', () => {\n        const spy = jest.fn();\n        const props = {\n            awaitMore: true,\n            itemCount: 10,\n            onIntersection: spy,\n        };\n        const tree = createTree(props);\n        tree.getInstance().handleUpdate({ isIntersecting: false });\n        tree.getInstance().handleUpdate({ isIntersecting: true });\n        tree.getInstance().handleUpdate({ isIntersecting: true });\n        expect(tree.getInstance().state.size).toBe(10);\n        tree.update(<List {...props} itemCount={20} />);\n        tree.getInstance().handleUpdate({ isIntersecting: true });\n        expect(spy).toHaveBeenCalledTimes(2);\n    });\n});\n\ndescribe('getDerivedStateFromProps', () => {\n    test('pageSize increases', () => {\n        const tree = createTree({ itemCount: 40 });\n        tree.update(<List itemCount={40} pageSize={20} />);\n        expect(tree.getInstance().state.size).toBe(20);\n    });\n\n    test('pageSize decreases', () => {\n        const tree = createTree({ itemCount: 40 });\n        tree.update(<List itemCount={40} pageSize={5} />);\n        expect(tree.getInstance().state.size).toBe(5);\n    });\n\n    test('itemCount increases', () => {\n        const tree = createTree({ itemCount: 20 });\n        tree.update(<List itemCount={40} pageSize={10} />);\n        expect(tree.getInstance().state.size).toBe(20);\n    });\n\n    test('itemCount decreases', () => {\n        const tree = createTree({ itemCount: 20 });\n        tree.update(<List itemCount={5} pageSize={10} />);\n        expect(tree.getInstance().state.size).toBe(5);\n    });\n\n    test('both pageSize and itemCount update', () => {\n        const tree = createTree({ itemCount: 20 });\n        tree.update(<List itemCount={30} pageSize={15} />);\n        expect(tree.getInstance().state.size).toBe(25);\n        tree.update(<List itemCount={20} pageSize={15} />);\n        expect(tree.getInstance().state.size).toBe(20);\n        tree.update(<List itemCount={30} pageSize={5} />);\n        expect(tree.getInstance().state.size).toBe(25);\n        tree.update(<List itemCount={40} pageSize={10} />);\n        expect(tree.getInstance().state.size).toBe(35);\n    });\n});\n"
  },
  {
    "path": "src/__tests__/Sentinel.spec.js",
    "content": "/* eslint-env jest */\nimport 'intersection-observer';\nimport React from 'react';\nimport renderer from 'react-test-renderer';\nimport { IntersectionObserver as Observer } from '@researchgate/react-intersection-observer/lib/js/IntersectionObserver';\nimport Sentinel from '../Sentinel';\nimport { computeRootMargin } from '../utils';\n\nconst defaultProps = {\n    axis: 'y',\n    threshold: '100px',\n    setRef: jest.fn(),\n    onChange() {},\n};\nconst createTree = (props = defaultProps) =>\n    renderer.create(<Sentinel {...props} />);\n\ndescribe('constructor', () => {\n    const propTypes = Sentinel.propTypes;\n\n    beforeAll(() => {\n        Sentinel.propTypes = {};\n    });\n\n    afterAll(() => {\n        Sentinel.propTypes = propTypes;\n    });\n\n    test('calls computeRootMargin and setRef', () => {\n        const utils = require('../utils');\n        const original = utils.computeRootMargin;\n        const computeRootMarginSpy = jest.fn();\n        utils.computeRootMargin = computeRootMarginSpy;\n        const TestSentinel = require('../Sentinel').default;\n        const renderMock = jest\n            .spyOn(TestSentinel.prototype, 'render')\n            .mockImplementation(() => {});\n        const setRefMock = jest.fn();\n        const props = { setRef: setRefMock };\n        const tree = renderer.create(<TestSentinel {...props} />);\n\n        expect(computeRootMarginSpy).toBeCalledWith(props);\n        expect(setRefMock).toBeCalledWith(tree.getInstance().setRootElement);\n\n        computeRootMarginSpy.mockRestore();\n        renderMock.mockRestore();\n\n        utils.computeRootMargin = original;\n    });\n});\n\ndescribe('render', () => {\n    test('first time sets a disabled observer', () => {\n        const testRenderer = createTree().root;\n        const { props } = testRenderer.findByType(Observer);\n\n        expect(props).toHaveProperty('disabled', true);\n        expect(props).toHaveProperty('root', undefined);\n    });\n\n    test('re-renders when setRef callback is called', () => {\n        const instance = createTree().getInstance();\n        const spy = (instance.setState = jest.fn());\n        const setRefMock = instance.props.setRef.mock;\n        const setRefCallback = setRefMock.calls[setRefMock.calls.length - 1][0];\n        setRefCallback(null);\n        expect(spy).toHaveBeenCalledTimes(1);\n    });\n\n    test('prevents re-render if root and rootMargin stay the same', () => {\n        const tree = createTree();\n        const spy = jest.spyOn(tree.getInstance(), 'render');\n\n        tree.update(<Sentinel {...defaultProps} />);\n        expect(spy).not.toBeCalled();\n    });\n\n    test('does re-observer if root and rootMargin stay the same', () => {\n        const tree = createTree();\n        const instance = tree.getInstance();\n\n        const spy1 = jest.spyOn(instance.observer, 'externalUnobserve');\n        const spy2 = jest.spyOn(instance.observer, 'observe');\n\n        tree.update(<Sentinel {...defaultProps} />);\n\n        expect(spy1).toHaveBeenCalledTimes(1);\n        expect(spy2).toHaveBeenCalledTimes(1);\n    });\n});\n\ndescribe('compute', () => {\n    test('returns computed rootMargin', () => {\n        expect(computeRootMargin({ threshold: '50%', axis: 'x' })).toBe(\n            '0% 50%'\n        );\n        expect(computeRootMargin({ threshold: '50px', axis: 'y' })).toBe(\n            '50px 0px'\n        );\n        expect(computeRootMargin({ threshold: '50', axis: 'y' })).toBe('50 0');\n    });\n\n    test('new axis or threshold props update rootMargin', () => {\n        const tree = createTree();\n        const instance = tree.getInstance();\n        let prevRootMargin = instance.state.rootMargin;\n\n        tree.update(<Sentinel {...{ ...defaultProps, axis: 'x' }} />);\n        expect(prevRootMargin).not.toBe(instance.state.rootMargin);\n\n        prevRootMargin = instance.state.rootMargin;\n\n        tree.update(<Sentinel {...{ ...defaultProps, threshold: '200px' }} />);\n        expect(prevRootMargin).not.toBe(instance.state.rootMargin);\n    });\n});\n"
  },
  {
    "path": "src/__tests__/__snapshots__/List.spec.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`intersection throws once if sentinel intersects with items on mount 1`] = `\n\"Warning: ReactIntersectionList: the sentinel detected a viewport with a bigger size than the size of its items. This could lead to detrimental behavior, e.g.: triggering more than one onIntersection callback at the start.\nTo prevent this, use either a bigger \\`pageSize\\` value or avoid using the prop awaitMore initially.\"\n`;\n\nexports[`throws with both itemCount and items props set 1`] = `\n\"Warning: Failed prop type: \\`items\\` must be of type Array or a native type implementing the iterable interface\n    in List\"\n`;\n\nexports[`throws with two different render function props 1`] = `\"Warning: ReactIntersectionList: cannot use children and renderItem props as render function at the same time.\"`;\n"
  },
  {
    "path": "src/__tests__/mockSentinel.js",
    "content": "/* eslint-env jest */\nimport React from 'react';\n\nconst mockCallback = jest.fn();\n\nexport { mockCallback };\n\nexport default <span />;\n"
  },
  {
    "path": "src/index.js",
    "content": "export { default } from './List';\n"
  },
  {
    "path": "src/utils.js",
    "content": "import warning from 'warning';\n\nexport function computeRootMargin({ threshold, axis }) {\n    const margins = [threshold];\n    const unit = threshold.match(/^-?\\d*\\.?\\d+(px|%)$/) || [''];\n    const value = `0${unit.pop()}`;\n    if (axis === 'y') {\n        margins.push(value);\n    } else {\n        margins.unshift(value);\n    }\n    return margins.join(' ');\n}\n\nexport function getItemCount({ itemCount, items }, warnIfConflict) {\n    const hasItemCount = typeof itemCount !== 'undefined';\n    const hasItems = typeof items !== 'undefined';\n    const defaultValue = 0;\n\n    if (warnIfConflict) {\n        warning(\n            !(hasItemCount && hasItems),\n            'ReactIntersectionList: cannot use itemCount and items props at the same time, choose one to determine the number of items to render.'\n        );\n    }\n\n    if (hasItemCount) {\n        return itemCount;\n    }\n\n    return hasItems ? items.length || items.size || defaultValue : defaultValue;\n}\n\nexport function computeSize(pageSize, itemCount) {\n    return Math.max(0, Math.min(pageSize, itemCount));\n}\n"
  },
  {
    "path": "types/index.d.ts",
    "content": "import * as React from 'react';\n\nexport default class ListClass extends React.PureComponent<ListProps> {}\n\ntype RenderFunction = (index: number, key: number) => JSX.Element | string;\n\ntype IterableType =\n  | {\n      length: number;\n    }\n  | {\n      size: number;\n    };\n\ninterface ChildrenAsFunction {\n  children: RenderFunction;\n  renderItem?: never;\n}\n\ninterface RenderAsProp {\n  renderItem: RenderFunction;\n  children?: never;\n}\n\ninterface ItemCountScalar {\n  itemCount: number;\n  items?: never;\n}\n\ninterface ItemCountIterable {\n  items: IterableType;\n  itemCount?: never;\n}\n\ninterface OptionalProps {\n  awaitMore?: boolean;\n  axis?: 'x' | 'y';\n  initialIndex?: number;\n  itemsRenderer?: (\n    items: IterableType,\n    ref: (instance: React.ReactInstance) => void\n  ) => JSX.Element;\n  onIntersection?: (nextSize: number, pageSize: number) => void;\n  pageSize?: number;\n  threshold?: string;\n}\n\ntype RenderPropType = ChildrenAsFunction | RenderAsProp;\n\ntype ItemCountPropType = ItemCountScalar | ItemCountIterable;\n\ntype ListProps = RenderPropType & ItemCountPropType & OptionalProps;\n"
  },
  {
    "path": "types/test.tsx",
    "content": "import * as React from 'react';\nimport List from '..';\n\n<List items={[]}>{() => ''}</List>;\n\n<List itemCount={Infinity} renderItem={() => <b>bold</b>} />;\n\n<List\n  itemCount={100}\n  awaitMore={false}\n  axis=\"y\"\n  initialIndex={50}\n  pageSize={25}\n  threshold=\"50%\"\n  itemsRenderer={(items, ref) => <div ref={ref}>{items}</div>}\n  onIntersection={() => {}}\n>\n  {(index, key) => `${index}${key}`}\n</List>;\n"
  },
  {
    "path": "types/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react\",\n    \"noEmit\": true\n  }\n}\n"
  }
]