[
  {
    "path": ".changeset/config.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/@changesets/config@3.1.2/schema.json\",\n  \"changelog\": [\n    \"@svitejs/changesets-changelog-github-compact\",\n    { \"repo\": \"TanStack/virtual\" }\n  ],\n  \"commit\": false,\n  \"access\": \"public\",\n  \"baseBranch\": \"main\",\n  \"updateInternalDependencies\": \"patch\",\n  \"fixed\": [],\n  \"linked\": [],\n  \"ignore\": [],\n  \"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH\": {\n    \"onlyUpdatePeerDependentsWhenOutOfRange\": true\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: tannerlinsley\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: 🐛 Bug Report\ndescription: Report a reproducible bug or regression\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thank you for reporting an issue :pray:.\n\n        This issue tracker is for reporting reproducible bugs or regression's found in [tanstack-virtual](https://github.com/tanstack/virtual)\n        If you have a question about how to achieve or implement something and are struggling, please post a question\n        inside of tanstack-virtual's [Discussions tab](https://github.com/tanstack/virtual/discussions) instead of filing an issue.\n\n        Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already:\n        - tanstack-virtual's [Discussions tab](https://github.com/tanstack/virtual/discussions)\n        - tanstack-virtual's [Open Issues](https://github.com/tanstack/virtual/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)\n        - tanstack-virtual's [Closed Issues](https://github.com/tanstack/virtual/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed)\n\n        The more information you fill in, the better the community can help you.\n\n  - type: textarea\n    id: description\n    attributes:\n      label: Describe the bug\n      description: Provide a clear and concise description of the challenge you are running into.\n    validations:\n      required: true\n  - type: input\n    id: link\n    attributes:\n      label: Your minimal, reproducible example\n      description: |\n        Please add a link to a minimal reproduction.\n        Note:\n          - Your bug may get fixed much faster if we can run your code and it doesn't have dependencies other than React.\n          - To create a shareable code example for web, you can use CodeSandbox (https://codesandbox.io/s/new) or Stackblitz (https://stackblitz.com/).\n          - Please make sure the example is complete and runnable without prior dependencies and free of unnecessary abstractions\n          - Feel free to fork any of the official CodeSandbox examples to reproduce your issue: https://github.com/tanstack/virtual/tree/main/examples/\n          - For React Native, you can use: https://snack.expo.dev/\n          - For TypeScript related issues only, a TypeScript Playground link might be sufficient: https://www.typescriptlang.org/play\n          - Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve.\n      placeholder: |\n        e.g. Code Sandbox, Stackblitz, Expo Snack or TypeScript playground\n    validations:\n      required: true\n  - type: textarea\n    id: steps\n    attributes:\n      label: Steps to reproduce\n      description: Describe the steps we have to take to reproduce the behavior.\n      placeholder: |\n        1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error\n    validations:\n      required: true\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected behavior\n      description: Provide a clear and concise description of what you expected to happen.\n      placeholder: |\n        As a user, I expected **_ behavior but i am seeing _**\n    validations:\n      required: true\n  - type: dropdown\n    attributes:\n      options:\n        - Every time\n        - Often\n        - Sometimes\n        - Only once\n      label: How often does this bug happen?\n      description: |\n        Following the repro steps above, how easily are you able to reproduce this bug?\n        options: - Every time - Often - Sometimes - Only once\n  - type: textarea\n    id: screenshots_or_videos\n    attributes:\n      label: Screenshots or Videos\n      description: |\n        If applicable, add screenshots or a video to help explain your problem.\n        For more information on the supported file image/file types and the file size limits, please refer\n        to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files\n      placeholder: |\n        You can drag your video or image files inside of this editor ↓\n  - type: textarea\n    id: platform\n    attributes:\n      label: Platform\n      description: |\n        Please let us know which Operting System, Browser and Browser version you were using when the issue occurred.\n      placeholder: |\n        - OS: [e.g. macOS, Windows, Linux, iOS, Android] - Browser: [e.g. Chrome, Safari, Firefox, React Native] - Version: [e.g. 91.1]\n    validations:\n      required: true\n  - type: input\n    id: library-version\n    attributes:\n      label: tanstack-virtual version\n      description: |\n        Please let us know the exact version of tanstack-virtual you were using when the issue occurred. Please don't just put in \"latest\", as this is subject to change.\n      placeholder: |\n        e.g. v3.30.1\n    validations:\n      required: true\n  - type: input\n    id: ts-version\n    attributes:\n      label: TypeScript version\n      description: |\n        If you are using TypeScript, please let us know the exact version of TypeScript you were using when the issue occurred.\n      placeholder: |\n        e.g. v5.2.2\n  - type: textarea\n    id: additional\n    attributes:\n      label: Additional context\n      description: Add any other context about the problem here.\n  - type: checkboxes\n    id: agrees-to-terms\n    attributes:\n      label: Terms & Code of Conduct\n      description: By submitting this issue, you agree to follow our Code of Conduct and can verify that you have followed the requirements outlined above to the best of your ability.\n      options:\n        - label: I agree to follow this project's Code of Conduct\n          required: true\n        - label: I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: 🤔 Feature Requests & Questions\n    url: https://github.com/TanStack/virtual/discussions\n    about: Please ask and answer questions here.\n  - name: 💬 Community Chat\n    url: https://discord.gg/mQd7egN\n    about: A dedicated discord server hosted by TanStack\n  - name: 🦋 TanStack Bluesky\n    url: https://bsky.app/profile/tanstack.com\n    about: Stay up to date with new releases of our libraries\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "## 🎯 Changes\n\n<!-- What changes are made in this PR? Describe the change and its motivation. -->\n\n## ✅ Checklist\n\n- [ ] I have followed the steps in the [Contributing guide](https://github.com/TanStack/virtual/blob/main/CONTRIBUTING.md).\n- [ ] I have tested this code locally with `pnpm run test:pr`.\n\n## 🚀 Release Impact\n\n- [ ] This change affects published code, and I have generated a [changeset](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).\n- [ ] This change is docs/CI/dev-only (no release).\n"
  },
  {
    "path": ".github/renovate.json",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"configMigration\": true,\n  \"extends\": [\n    \"config:recommended\",\n    \"group:allNonMajor\",\n    \"schedule:weekly\",\n    \":approveMajorUpdates\",\n    \":automergeMinor\",\n    \":disablePeerDependencies\",\n    \":maintainLockFilesMonthly\",\n    \":semanticCommits\",\n    \":semanticCommitTypeAll(chore)\"\n  ],\n  \"ignorePresets\": [\":ignoreModulesAndTests\"],\n  \"labels\": [\"dependencies\"],\n  \"rangeStrategy\": \"bump\",\n  \"postUpdateOptions\": [\"pnpmDedupe\"],\n  \"ignoreDeps\": [\"@types/node\", \"node\", \"typescript\"]\n}\n"
  },
  {
    "path": ".github/workflows/autofix.yml",
    "content": "name: autofix.ci # needed to securely identify the workflow\n\non:\n  pull_request:\n  push:\n    branches: [main, alpha, beta]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.number || github.ref }}\n  cancel-in-progress: true\n\npermissions:\n  contents: read\n\njobs:\n  autofix:\n    name: autofix\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n      - name: Setup Tools\n        uses: tanstack/config/.github/setup@main\n      - name: Fix formatting\n        run: pnpm format\n      - name: Apply fixes\n        uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef\n        with:\n          commit-message: 'ci: apply automated fixes'\n"
  },
  {
    "path": ".github/workflows/pr.yml",
    "content": "name: PR\n\non:\n  pull_request:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.number || github.ref }}\n  cancel-in-progress: true\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  test:\n    name: Test\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n        with:\n          fetch-depth: 0\n      - name: Setup Tools\n        uses: tanstack/config/.github/setup@main\n      - name: Get base and head commits for `nx affected`\n        uses: nrwl/nx-set-shas@v4.4.0\n        with:\n          main-branch-name: main\n      - name: Install Playwright browsers\n        run: pnpm exec playwright install chromium\n      - name: Run Checks\n        run: pnpm run test:pr\n  preview:\n    name: Preview\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n      - name: Setup Tools\n        uses: tanstack/config/.github/setup@main\n      - name: Build Packages\n        run: pnpm run build:all\n      - name: Publish Previews\n        run: pnpx pkg-pr-new publish --pnpm --compact './packages/*' --template './examples/*/*'\n  provenance:\n    name: Provenance\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n      - name: Check Provenance\n        uses: danielroe/provenance-action@v0.1.1\n        with:\n          fail-on-downgrade: true\n  version-preview:\n    name: Version Preview\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n      - name: Setup Tools\n        uses: TanStack/config/.github/setup@main\n      - name: Changeset Preview\n        uses: TanStack/config/.github/changeset-preview@main\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n    branches: [main, alpha, beta]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.number || github.ref }}\n  cancel-in-progress: true\n\nenv:\n  NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}\n\npermissions:\n  contents: write\n  id-token: write\n  pull-requests: write\n\njobs:\n  release:\n    name: Release\n    if: github.repository_owner == 'TanStack'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n        with:\n          fetch-depth: 0\n      - name: Setup Tools\n        uses: tanstack/config/.github/setup@main\n      - name: Install Playwright browsers\n        run: pnpm exec playwright install chromium\n      - name: Run Tests\n        run: pnpm run test:ci\n      - name: Run Changesets (version or publish)\n        id: changesets\n        uses: changesets/action@v1.7.0\n        with:\n          version: pnpm run changeset:version\n          publish: pnpm run changeset:publish\n          commit: 'ci: Version Packages'\n          title: 'ci: Version Packages'\n      - name: Comment on PRs about release\n        if: steps.changesets.outputs.published == 'true'\n        uses: TanStack/config/.github/comment-on-release@main\n        with:\n          published-packages: ${{ steps.changesets.outputs.publishedPackages }}\n"
  },
  {
    "path": ".gitignore",
    "content": "\n# See https://help.github.com/ignore-files/ for more about ignoring files.\n\n# dependencies\nnode_modules\npackage-lock.json\nyarn.lock\n\n# builds\nbuild\ncoverage\ndist\n\n# misc\n.DS_Store\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n.next\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.history\nsize-plugin.json\nstats-hydration.json\nstats.json\nstats.html\n.vscode/settings.json\n\n*.log\n.cache\n.idea\n.nx/cache\n.nx/workspace-data\n.pnpm-store\n.tsup\n.svelte-kit\n\nvite.config.js.timestamp-*\nvite.config.ts.timestamp-*\n\n# Playwright test artifacts\ntest-results/\nplaywright-report/\n*.log\n"
  },
  {
    "path": ".npmrc",
    "content": "provenance=true\n"
  },
  {
    "path": ".nvmrc",
    "content": "24.8.0\n"
  },
  {
    "path": ".prettierignore",
    "content": "**/.next\n**/.nx/cache\n**/.svelte-kit\n**/build\n**/coverage\n**/dist\n**/docs\n**/codemods/**/__testfixtures__\npnpm-lock.yaml\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Questions\n\nIf you have questions about implementation details, help or support, then please use our dedicated community forum at [GitHub Discussions](https://github.com/TanStack/virtual/discussions) **PLEASE NOTE:** If you choose to instead open an issue for your question, your issue will be immediately closed and redirected to the forum.\n\n## Reporting Issues\n\nIf you have found what you think is a bug, please [file an issue](https://github.com/TanStack/virtual/issues/new/choose). **PLEASE NOTE:** Issues that are identified as implementation questions or non-issues will be immediately closed and redirected to [GitHub Discussions](https://github.com/TanStack/virtual/discussions)\n\n## Suggesting new features\n\nIf you are here to suggest a feature, first create an issue if it does not already exist. From there, we will discuss use-cases for the feature and then finally discuss how it could be implemented.\n\n## Development\n\nIf you have been assigned to fix an issue or develop a new feature, please follow these steps to get started:\n\n- Fork this repository.\n- Install dependencies\n\n  ```bash\n  pnpm install\n  ```\n\n  - We use [pnpm](https://pnpm.io/) v9 for package management (run in case of pnpm-related issues).\n\n    ```bash\n    corepack enable && corepack prepare\n    ```\n\n  - We use [nvm](https://github.com/nvm-sh/nvm) to manage node versions - please make sure to use the version mentioned in `.nvmrc`\n\n    ```bash\n    nvm use\n    ```\n\n- Build all packages.\n\n  ```bash\n  pnpm build:all\n  ```\n\n- Run development server.\n\n  ```bash\n  pnpm run watch\n  ```\n\n- Implement your changes and tests to files in the `src/` directory and corresponding test files.\n- Document your changes in the appropriate doc page.\n- Git stage your required changes and commit (see below commit guidelines).\n- Submit PR for review.\n\n### Editing the docs locally and previewing the changes\n\nThe documentations for all the TanStack projects are hosted on [tanstack.com](https://tanstack.com), which is a TanStack Start application (https://github.com/TanStack/tanstack.com). You need to run this app locally to preview your changes in the `TanStack/virtual` docs.\n\n> [!NOTE]\n> The website fetches the doc pages from GitHub in production, and searches for them at `../virtual/docs` in development. Your local clone of `TanStack/virtual` needs to be in the same directory as the local clone of `TanStack/tanstack.com`.\n\nYou can follow these steps to set up the docs for local development:\n\n1. Make a new directory called `tanstack`.\n\n```sh\nmkdir tanstack\n```\n\n2. Enter that directory and clone the [`TanStack/virtual`](https://github.com/TanStack/virtual) and [`TanStack/tanstack.com`](https://github.com/TanStack/tanstack.com) repos.\n\n```sh\ncd tanstack\ngit clone git@github.com:TanStack/virtual.git\n# We probably don't need all the branches and commit history\n# from the `tanstack.com` repo, so let's just create a shallow\n# clone of the latest version of the `main` branch.\n# Read more about shallow clones here:\n# https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/#user-content-shallow-clones\ngit clone git@github.com:TanStack/tanstack.com.git --depth=1 --single-branch --branch=main\n```\n\n> [!NOTE]\n> Your `tanstack` directory should look like this:\n>\n> ```\n> tanstack/\n>    |\n>    +-- virtual/ (<-- this directory cannot be called anything else!)\n>    |\n>    +-- tanstack.com/\n> ```\n\n3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode:\n\n```sh\ncd tanstack.com\npnpm i\n# The app will run on https://localhost:3000 by default\npnpm dev\n```\n\n4. Now you can visit http://localhost:3000/virtual/latest/docs/overview in the browser and see the changes you make in `TanStack/virtual/docs` there.\n\n> [!WARNING]\n> You will need to update the `docs/config.json` file (in `TanStack/virtual`) if you add a new documentation page!\n\nYou can see the whole process in the screen capture below:\n\nhttps://github.com/fulopkovacs/form/assets/43729152/9d35a3c3-8153-4e74-9cb2-af275f7a269b\n\n### Running examples\n\n- Make sure you've installed the dependencies in the repo's root directory.\n\n  ```bash\n  pnpm install\n  ```\n\n- If you want to run the example against your local changes, run below in the repo's root directory. Otherwise, it will be run against the latest TanStack Virtual release.\n\n  ```bash\n  pnpm run watch\n  ```\n\n- Run below in the selected examples' directory.\n\n  ```bash\n  pnpm run dev\n  ```\n\n#### Note on standalone execution\n\nIf you want to run an example without installing dependencies for the whole repo, just follow instructions from the example's README.md file. It will be then run against the latest TanStack Virtual release.\n\n## Online one-click setup\n\nYou can use Gitpod (An Online Open Source VS Code like IDE which is free for Open Source) for developing online. With a single click it will start a workspace and automatically:\n\n- clone the `TanStack/virtual` repo.\n- install all the dependencies in `/` and `/docs`.\n- run below in the root(`/`) to Auto-build files.\n\n  ```bash\n  npm start\n  ```\n\n- run below in `/docs`.\n\n  ```bash\n  npm run dev\n  ```\n\n[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/TanStack/virtual)\n\n## Changesets\n\nThis repo uses [Changesets](https://github.com/changesets/changesets) to automate releases. If your PR should release a new package version (patch, minor, or major), please run run `pnpm changeset` and commit the file. If needed, changeset descriptions can be more descriptive, and will be included in the changelog. If your PR affects docs, examples, styles, etc., you probably don't need to generate a changeset.\n\n## Pull requests\n\nMaintainers merge pull requests by squashing all commits and editing the commit message if necessary using the GitHub user interface.\n\nUse an appropriate commit type. Be especially careful with breaking changes.\n\n## Releases\n\nFor each new commit added to `main`, a GitHub Workflow is triggered which runs the [Changesets Action](https://github.com/changesets/action). This generates a preview PR showing the impact of all changesets. When this PR is merged, the package will be published to NPM.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present Tanner Linsley\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": "<div align=\"center\">\n  <img src=\"./media/header_virtual.png\" alt=\"Tanstack Virtual\">\n</div>\n\n<br />\n\n<div align=\"center\">\n\t<a href=\"https://npmjs.com/package/@tanstack/virtual-core\" target=\"\\_parent\">\n  <img alt=\"npm downloads\" src=\"https://img.shields.io/npm/dm/@tanstack/virtual-core.svg\" />\n</a>\n\t<a href=\"https://github.com/tanstack/virtual\" target=\"\\_parent\">\n  <img alt=\"github stars\" src=\"https://img.shields.io/github/stars/tanstack/virtual.svg?style=social&label=Star\" />\n</a>\n<a href=\"https://bundlephobia.com/result?p=@tanstack/virtual-core@latest\" target=\"\\_parent\">\n  <img alt=\"bundle size\" src=\"https://badgen.net/bundlephobia/minzip/@tanstack/virtual-core@latest\" />\n</a>\n</div>\n\n<div align=\"center\">\n\t<a href=\"#badge\">\n    <img alt=\"semantic-release\" src=\"https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg\">\n  </a>\n\t<a href=\"https://bestofjs.org/projects/tanstack-virtual\">\n\t\t<img alt=\"Best of JS\" src=\"https://img.shields.io/endpoint?url=https://bestofjs-serverless.now.sh/api/project-badge?fullName=TanStack%2Fvirtual%26since=daily\" alt=\"Best of JS\"/>\n\t</a>\n\t<a href=\"https://twitter.com/tan_stack\">\n\t\t<img src=\"https://img.shields.io/twitter/follow/tan_stack.svg?style=social\" alt=\"Follow @TanStack\"/>\n\t</a>\n</div>\n\n<div align=\"center\">\n\n### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)\n\n</div>\n\n# TanStack Virtual\n\nA headless, framework‑agnostic virtualization library for rendering massive lists, grids, and tables at 60FPS while giving you full control over markup and styles.\n\n- Framework‑agnostic & headless\n- Virtualizes vertical, horizontal & grid layouts with a single hook/function\n- Lightweight (10–15kb) yet powerful, with dynamic & measured sizing support\n- Smooth 60FPS scrolling with sticky items and window‑scrolling utilities\n\n### <a href=\"https://tanstack.com/virtual\" >Read the docs →</a>\n\n## Get Involved\n\n- We welcome issues and pull requests!\n- Participate in [GitHub discussions](https://github.com/TanStack/virtual/discussions)\n- Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ)\n- See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions\n\n## Partners\n\n<table align=\"center\">\n  <tr>\n    <td>\n        <a href=\"https://www.coderabbit.ai/?via=tanstack&dub_id=aCcEEdAOqqutX6OS\">\n\t\t\t<picture>\n\t\t\t  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://tanstack.com/assets/coderabbit-dark-CMcuvjEy.svg\" height=\"40\" />\n\t\t\t  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://tanstack.com/assets/coderabbit-light-DVMJ2jHi.svg\" height=\"40\" />\n\t\t\t  <img src=\"https://tanstack.com/assets/coderabbit-light-DVMJ2jHi.svg\" height=\"40\" alt=\"CodeRabbit\" />\n\t\t\t</picture>        \n\t\t</a>\n    </td>\n    <td padding=\"20\">\n      <a href=\"https://www.cloudflare.com?utm_source=tanstack\">\n         <picture>\n\t\t  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://tanstack.com/assets/cloudflare-white-DQDB7UaL.svg\" height=\"60\" />\n\t\t  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://tanstack.com/assets/cloudflare-black-CPufaW0B.svg\" height=\"60\" />\n\t\t  <img src=\"https://tanstack.com/assets/cloudflare-black-CPufaW0B.svg\" height=\"60\" alt=\"Cloudflare\" />\n\t\t</picture>\n      </a>\n    </td>\n  </tr>\n</table>\n  \n \n<div align=\"center\">\n<img src=\"./media/partner_logo.svg\" alt=\"Virtual & you?\" height=\"65\">\n<p>\nWe're looking for TanStack Virtual Partners to join our mission! Partner with us to push the boundaries of TanStack Virtual and build amazing things together.\n</p>\n<a href=\"mailto:partners@tanstack.com?subject=TanStack Virtual Partnership\"><b>LET'S CHAT</b></a>\n</div>\n  \n## Explore the TanStack Ecosystem\n\n- <a href=\"https://github.com/tanstack/config\"><b>TanStack Config</b></a> – Tooling for JS/TS packages\n- <a href=\"https://github.com/tanstack/db\"><b>TanStack DB</b></a> – Reactive sync client store\n- <a href=\"https://github.com/tanstack/devtools\"><b>TanStack DevTools</b></a> – Unified devtools panel\n- <a href=\"https://github.com/tanstack/form\"><b>TanStack Form</b></a> – Type‑safe form state\n- <a href=\"https://github.com/tanstack/pacer\"><b>TanStack Pacer</b></a> – Debouncing, throttling, batching <br/>\n- <a href=\"https://github.com/tanstack/query\"><b>TanStack Query</b></a> – Async state & caching\n- <a href=\"https://github.com/tanstack/ranger\"><b>TanStack Ranger</b></a> – Range & slider primitives\n- <a href=\"https://github.com/tanstack/router\"><b>TanStack Router</b></a> – Type‑safe routing, caching & URL state\n- <a href=\"https://github.com/tanstack/router\"><b>TanStack Start</b></a> – Full‑stack SSR & streaming\n- <a href=\"https://github.com/tanstack/store\"><b>TanStack Store</b></a> – Reactive data store\n- <a href=\"https://github.com/tanstack/table\"><b>TanStack Table</b></a> – Headless datagrids\n\n… and more at <a href=\"https://tanstack.com\"><b>TanStack.com »</b></a>\n\n<!-- Use the force, Luke -->\n"
  },
  {
    "path": "docs/api/virtual-item.md",
    "content": "---\ntitle: VirtualItem\n---\n\nThe `VirtualItem` object represents a single item returned by the virtualizer. It contains information you need to render the item in the coordinate space within your virtualizer's scrollElement and other helpful properties/functions.\n\n```tsx\nexport interface VirtualItem {\n  key: string | number | bigint\n  index: number\n  start: number\n  end: number\n  size: number\n}\n```\n\nThe following properties and methods are available on each VirtualItem object:\n\n### `key`\n\n```tsx\nkey: string | number | bigint\n```\n\nThe unique key for the item. By default this is the item index, but should be configured via the `getItemKey` Virtualizer option.\n\n### `index`\n\n```tsx\nindex: number\n```\n\nThe index of the item.\n\n### `start`\n\n```tsx\nstart: number\n```\n\nThe starting pixel offset for the item. This is usually mapped to a css property or transform like `top/left` or `translateX/translateY`.\n\n### `end`\n\n```tsx\nend: number\n```\n\nThe ending pixel offset for the item. This value is not necessary for most layouts, but can be helpful so we've provided it anyway.\n\n### `size`\n\n```tsx\nsize: number\n```\n\nThe size of the item. This is usually mapped to a css property like `width/height`. Before an item is measured with the `VirtualItem.measureElement` method, this will be the estimated size returned from your `estimateSize` virtualizer option. After an item is measured (if you choose to measure it at all), this value will be the number returned by your `measureElement` virtualizer option (which by default is configured to measure elements with `getBoundingClientRect()`).\n\n### `lane`\n\n```tsx\nlane: number\n```\n\nThe lane index of the item. In regular lists it will always be set to `0` but becomes useful for masonry layouts (see variable examples for more details).\n"
  },
  {
    "path": "docs/api/virtualizer.md",
    "content": "---\ntitle: Virtualizer\n---\n\nThe `Virtualizer` class is the core of TanStack Virtual. Virtualizer instances are usually created for you by your framework adapter, but you do receive the virtualizer directly.\n\n```tsx\nexport class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {\n  constructor(options: VirtualizerOptions<TScrollElement, TItemElement>)\n}\n```\n\n## Required Options\n\n### `count`\n\n```tsx\ncount: number\n```\n\nThe total number of items to virtualize.\n\n### `getScrollElement`\n\n```tsx\ngetScrollElement: () => TScrollElement\n```\n\nA function that returns the scrollable element for the virtualizer. It may return null if the element is not available yet.\n\n### `estimateSize`\n\n```tsx\nestimateSize: (index: number) => number\n```\n\n> 🧠 If you are dynamically measuring your elements, it's recommended to estimate the largest possible size (width/height, within comfort) of your items. This will help the virtualizer calculate more accurate initial positions.\n\nThis function is passed the index of each item and should return the actual size (or estimated size if you will be dynamically measuring items with `virtualItem.measureElement`) for each item. This measurement should return either the width or height depending on the orientation of your virtualizer.\n\n## Optional Options\n\n### `enabled`\n\n```tsx\nenabled?: boolean\n```\n\nSet to `false` to disable scrollElement observers and reset the virtualizer's state\n\n### `debug`\n\n```tsx\ndebug?: boolean\n```\n\nSet to `true` to enable debug logs\n\n### `initialRect`\n\n```tsx\ninitialRect?: Rect\n```\n\nThe initial `Rect` of the scrollElement. This is mostly useful if you need to run the virtualizer in an SSR environment, otherwise the initialRect will be calculated on mount by the `observeElementRect` implementation.\n\n### `onChange`\n\n```tsx\nonChange?: (instance: Virtualizer<TScrollElement, TItemElement>, sync: boolean) => void\n```\n\nA callback function that fires when the virtualizer's internal state changes. It's passed the virtualizer instance and the sync parameter.\n\nThe sync parameter indicates whether scrolling is currently in progress. It is `true` when scrolling is ongoing, and `false` when scrolling has stopped or other actions (such as resizing) are being performed.\n\n### `overscan`\n\n```tsx\noverscan?: number\n```\n\nThe number of items to render above and below the visible area. Increasing this number will increase the amount of time it takes to render the virtualizer, but might decrease the likelihood of seeing slow-rendering blank items at the top and bottom of the virtualizer when scrolling. The default value is `1`.\n\n### `horizontal`\n\n```tsx\nhorizontal?: boolean\n```\n\nSet this to `true` if your virtualizer is oriented horizontally.\n\n### `paddingStart`\n\n```tsx\npaddingStart?: number\n```\n\nThe padding to apply to the start of the virtualizer in pixels.\n\n### `paddingEnd`\n\n```tsx\npaddingEnd?: number\n```\n\nThe padding to apply to the end of the virtualizer in pixels.\n\n### `scrollPaddingStart`\n\n```tsx\nscrollPaddingStart?: number\n```\n\nThe padding to apply to the start of the virtualizer in pixels when scrolling to an element.\n\n### `scrollPaddingEnd`\n\n```tsx\nscrollPaddingEnd?: number\n```\n\nThe padding to apply to the end of the virtualizer in pixels when scrolling to an element.\n\n### `initialOffset`\n\n```tsx\ninitialOffset?: number | (() => number)\n```\n\nThe position where the list is scrolled to on render. This is useful if you are rendering the virtualizer in a SSR environment or are conditionally rendering the virtualizer.\n\n### `getItemKey`\n\n```tsx\ngetItemKey?: (index: number) => Key\n```\n\nThis function is passed the index of each item and should return a unique key for that item. The default functionality of this function is to return the index of the item, but you should override this when possible to return a unique identifier for each item across the entire set.\n\n**Note:** The virtualizer automatically invalidates its measurement cache when measurement-affecting options change, ensuring `getTotalSize()` and other measurements return fresh values. While the virtualizer intelligently tracks which options actually affect measurements, it's still better to memoize `getItemKey` (e.g., using `useCallback` in React) to avoid unnecessary recalculations.\n\n### `rangeExtractor`\n\n```tsx\nrangeExtractor?: (range: Range) => number[]\n```\n\nThis function receives visible range indexes and should return array of indexes to render. This is useful if you need to add or remove items from the virtualizer manually regardless of the visible range, eg. rendering sticky items, headers, footers, etc. The default range extractor implementation will return the visible range indexes and is exported as `defaultRangeExtractor`.\n\n### `scrollToFn`\n\n```tsx\nscrollToFn?: (\n  offset: number,\n  options: { adjustments?: number; behavior?: 'auto' | 'smooth' },\n  instance: Virtualizer<TScrollElement, TItemElement>,\n) => void\n```\n\nAn optional function that (if provided) should implement the scrolling behavior for your scrollElement. It will be called with the following arguments: \n\n- An `offset` (in pixels) to scroll towards.\n- An object indicating whether there was a difference between the estimated size and actual size (`adjustments`) and/or whether scrolling was called with a smooth animation (`behaviour`).\n- The virtualizer instance itself. \n\nNote that built-in scroll implementations are exported as `elementScroll` and `windowScroll`, which are automatically configured by the framework adapter functions like `useVirtualizer` or `useWindowVirtualizer`.\n\n### `observeElementRect`\n\n```tsx\nobserveElementRect: (\n  instance: Virtualizer<TScrollElement, TItemElement>,\n  cb: (rect: Rect) => void,\n) => void | (() => void)\n```\n\nAn optional function that if provided is called when the scrollElement changes and should implement the initial measurement and continuous monitoring of the scrollElement's `Rect` (an object with `width` and `height`). It's called with the instance (which also gives you access to the scrollElement via `instance.scrollElement`. Built-in implementations are exported as `observeElementRect` and `observeWindowRect` which are automatically configured for you by your framework adapter's exported functions like `useVirtualizer` or `useWindowVirtualizer`.\n\n### `observeElementOffset`\n\n```tsx\nobserveElementOffset: (\n    instance: Virtualizer<TScrollElement, TItemElement>,\n    cb: (offset: number) => void,\n  ) => void | (() => void)\n```\n\nAn optional function that if provided is called when the scrollElement changes and should implement the initial measurement and continuous monitoring of the scrollElement's scroll offset (a number). It's called with the instance (which also gives you access to the scrollElement via `instance.scrollElement`. Built-in implementations are exported as `observeElementOffset` and `observeWindowOffset` which are automatically configured for you by your framework adapter's exported functions like `useVirtualizer` or `useWindowVirtualizer`.\n\n### `measureElement`\n\n```tsx\nmeasureElement?: (\n  element: TItemElement,\n  entry: ResizeObserverEntry | undefined,\n  instance: Virtualizer<TScrollElement, TItemElement>,\n) => number\n```\n\nThis optional function is called when the virtualizer needs to dynamically measure the size (width or height) of an item.\n\n> 🧠 You can use `instance.options.horizontal` to determine if the width or height of the item should be measured.\n\n### `scrollMargin`\n\n```tsx\nscrollMargin?: number\n```\n\nWith this option, you can specify where the scroll offset should originate. Typically, this value represents the space between the beginning of the scrolling element and the start of the list. This is especially useful in common scenarios such as when you have a header preceding a window virtualizer or when multiple virtualizers are utilized within a single scrolling element. If you are using absolute positioning of elements, you should take into account the `scrollMargin` in your CSS transform:\n```tsx\ntransform: `translateY(${\n   virtualRow.start - rowVirtualizer.options.scrollMargin\n}px)` \n``` \nTo dynamically measure value for `scrollMargin` you can use `getBoundingClientRect()` or ResizeObserver. This is helpful in scenarios when items above your virtual list might change their height.   \n\n### `gap`\n\n```tsx\ngap?: number\n```\n\nThis option allows you to set the spacing between items in the virtualized list. It's particularly useful for maintaining a consistent visual separation between items without having to manually adjust each item's margin or padding. The value is specified in pixels.\n\n### `lanes`\n\n```tsx\nlanes: number\n```\n\nThe number of lanes the list is divided into (aka columns for vertical lists and rows for horizontal lists).\n\n### `isScrollingResetDelay`\n\n```tsx\nisScrollingResetDelay: number\n```\n\nThis option allows you to specify the duration to wait after the last scroll event before resetting the isScrolling instance property. The default value is 150 milliseconds. \n\nThe implementation of this option is driven by the need for a reliable mechanism to handle scrolling behavior across different browsers. Until all browsers uniformly support the scrollEnd event.\n\n### `useScrollendEvent`\n\n```tsx\nuseScrollendEvent: boolean\n```\n\nDetermines whether to use the native scrollend event to detect when scrolling has stopped. If set to false, a debounced fallback is used to reset the isScrolling instance property after isScrollingResetDelay milliseconds. The default value is `false`. \n\nThe implementation of this option is driven by the need for a reliable mechanism to handle scrolling behavior across different browsers. Until all browsers uniformly support the scrollEnd event.\n\n### `isRtl`\n\n```tsx\nisRtl: boolean\n```\n\nWhether to invert horizontal scrolling to support right-to-left language locales.\n\n### `useAnimationFrameWithResizeObserver`\n\n```tsx\nuseAnimationFrameWithResizeObserver: boolean\n```\n\n**Default:** `false`\n\nWhen enabled, defers ResizeObserver measurement processing to the next animation frame using `requestAnimationFrame`.\n\n**Important:** This option typically **should not be enabled** in most cases. ResizeObserver callbacks already execute at an optimal time in the browser's rendering pipeline (after layout, before paint), and the measurements provided in the callback are pre-computed by the browser without causing additional reflows.\n\n**Potential use cases:**\n- If you're performing heavy DOM mutations in response to size changes and want to batch them with the next render cycle\n- As a workaround for the \"ResizeObserver loop completed with undelivered notifications\" error (though this usually indicates a deeper issue that should be fixed)\n\n**Tradeoffs:**\n- **Adds ~16ms delay:** Measurements are deferred to the next frame, which can cause visual artifacts, stale measurements, or slower time-to-interactive\n- **No batching benefit:** ResizeObserver already batches multiple element resizes into a single callback\n- **Defeats optimization:** The browser has already computed the measurements synchronously; deferring them provides no performance benefit for reading values\n\nOnly enable this option if you have a specific reason and have measured that it improves your use case.\n\n## Virtualizer Instance\n\nThe following properties and methods are available on the virtualizer instance:\n\n### `options`\n\n```tsx\noptions: readonly Required<VirtualizerOptions<TScrollElement, TItemElement>>\n```\n\nThe current options for the virtualizer. This property is updated via your framework adapter and is read-only.\n\n### `scrollElement`\n\n```tsx\nscrollElement: readonly TScrollElement | null\n```\n\nThe current scrollElement for the virtualizer. This property is updated via your framework adapter and is read-only.\n\n### `getVirtualItems`\n\n```tsx\ntype getVirtualItems = () => VirtualItem[]\n```\n\nReturns the virtual items for the current state of the virtualizer.\n\n### `getVirtualIndexes`\n\n```tsx\ntype getVirtualIndexes = () => number[]\n```\n\nReturns the virtual row indexes for the current state of the virtualizer.\n\n### `scrollToOffset`\n\n```tsx\nscrollToOffset: (\n  toOffset: number,\n  options?: {\n    align?: 'start' | 'center' | 'end' | 'auto',\n    behavior?: 'auto' | 'smooth'\n  }\n) => void\n```\n\nScrolls the virtualizer to the pixel offset provided. You can optionally pass an alignment mode to anchor the scroll to a specific part of the scrollElement.\n\n### `scrollToIndex`\n\n```tsx\nscrollToIndex: (\n  index: number,\n  options?: {\n    align?: 'start' | 'center' | 'end' | 'auto',\n    behavior?: 'auto' | 'smooth'\n  }\n) => void\n```\n\nScrolls the virtualizer to the items of the index provided. You can optionally pass an alignment mode to anchor the scroll to a specific part of the scrollElement.\n\n> 🧠 During smooth scrolling, the virtualizer only measures items within a buffer range around the scroll target. Items far from the target are skipped to prevent their size changes from shifting the target position and breaking the smooth animation.\n>\n> Because of this, the preferred layout strategy for smooth scrolling is **block translation** — translate the entire rendered block using the first item's `start` offset, rather than positioning each item independently with absolute positioning. This ensures items stay correctly positioned relative to each other even when some measurements are skipped.\n\n### `scrollBy`\n\n```tsx\nscrollBy: (\n  delta: number,\n  options?: {\n    behavior?: 'auto' | 'smooth'\n  }\n) => void\n```\n\nScrolls the virtualizer by the specified number of pixels relative to the current scroll position.\n\n### `getTotalSize`\n\n```tsx\ngetTotalSize: () => number\n```\n\nReturns the total size in pixels for the virtualized items. This measurement will incrementally change if you choose to dynamically measure your elements as they are rendered.\n\n### `measure`\n\n```tsx\nmeasure: () => void\n```\n\nResets any prev item measurements.\n\n### `measureElement`\n\n```tsx\nmeasureElement: (el: TItemElement | null) => void\n```\n\nMeasures the element using your configured `measureElement` virtualizer option. You are responsible for calling this in your virtualizer markup when the component is rendered (eg. using something like React's ref callback prop) also adding `data-index`\n\n```tsx\n <div\n  key={virtualRow.key}\n  data-index={virtualRow.index}\n  ref={virtualizer.measureElement}\n  style={...}\n>...</div>\n```\n\nBy default the `measureElement` virtualizer option is configured to measure elements with `getBoundingClientRect()`.\n\n### `resizeItem`\n\n```tsx\nresizeItem: (index: number, size: number) => void\n```\n\nChange the virtualized item's size manually. Use this function to manually set the size calculated for this index. Useful in occations when using some custom morphing transition and you know the morphed item's size beforehand.\n\nYou can also use this method with a throttled ResizeObserver instead of `Virtualizer.measureElement` to reduce re-rendering.\n\n> ⚠️ Please be aware that manually changing the size of an item when using `Virtualizer.measureElement` to monitor that item, will result in unpredictable behaviour as the `Virtualizer.measureElement` is also changing the size. However you can use one of resizeItem or measureElement in the same virtualizer instance but on different item indexes.\n\n### `scrollRect`\n\n```tsx\nscrollRect: Rect\n```\n\nCurrent `Rect` of the scroll element.\n\n### `shouldAdjustScrollPositionOnItemSizeChange`\n\n```tsx\nshouldAdjustScrollPositionOnItemSizeChange: undefined | ((item: VirtualItem, delta: number, instance: Virtualizer<TScrollElement, TItemElement>) => boolean)\n```\n\nThe shouldAdjustScrollPositionOnItemSizeChange method enables fine-grained control over the adjustment of scroll position when the size of dynamically rendered items differs from the estimated size. When jumping in the middle of the list and scrolling backward new elements may have a different size than the initially estimated size. This discrepancy can cause subsequent items to shift, potentially disrupting the user's scrolling experience, particularly when navigating backward through the list. \n\n### `isScrolling`\n\n```tsx\nisScrolling: boolean\n```\n\nBoolean flag indicating if list is currently being scrolled.\n\n### `scrollDirection`\n\n```tsx\nscrollDirection: 'forward' | 'backward' | null\n```\n\nThis option indicates the direction of scrolling, with possible values being 'forward' for scrolling downwards and 'backward' for scrolling upwards. The value is set to null when there is no active scrolling.\n\n### `scrollOffset`\n\n```tsx\nscrollOffset: number\n```\n\nThis option represents the current scroll position along the scrolling axis. It is measured in pixels from the starting point of the scrollable area.\n"
  },
  {
    "path": "docs/config.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/TanStack/tanstack.com/main/tanstack-docs-config.schema.json\",\n  \"docSearch\": {\n    \"appId\": \"\",\n    \"indexName\": \"\",\n    \"apiKey\": \"\"\n  },\n  \"sections\": [\n    {\n      \"label\": \"Getting Started\",\n      \"children\": [\n        { \"label\": \"Introduction\", \"to\": \"introduction\" },\n        { \"label\": \"Installation\", \"to\": \"installation\" }\n      ],\n      \"frameworks\": [\n        {\n          \"label\": \"react\",\n          \"children\": [\n            {\n              \"label\": \"React Virtual\",\n              \"to\": \"framework/react/react-virtual\"\n            }\n          ]\n        },\n        {\n          \"label\": \"angular\",\n          \"children\": [\n            {\n              \"label\": \"Angular Virtual\",\n              \"to\": \"framework/angular/angular-virtual\"\n            }\n          ]\n        },\n        {\n          \"label\": \"solid\",\n          \"children\": [\n            { \"label\": \"Solid Virtual\", \"to\": \"framework/solid/solid-virtual\" }\n          ]\n        },\n        {\n          \"label\": \"svelte\",\n          \"children\": [\n            {\n              \"label\": \"Svelte Virtual\",\n              \"to\": \"framework/svelte/svelte-virtual\"\n            }\n          ]\n        },\n        {\n          \"label\": \"vue\",\n          \"children\": [\n            {\n              \"label\": \"Vue Virtual\",\n              \"to\": \"framework/vue/vue-virtual\"\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"label\": \"Core APIs\",\n      \"children\": [\n        { \"label\": \"Virtualizer\", \"to\": \"api/virtualizer\" },\n        { \"label\": \"VirtualItem\", \"to\": \"api/virtual-item\" }\n      ]\n    },\n    {\n      \"label\": \"Examples\",\n      \"children\": [],\n      \"frameworks\": [\n        {\n          \"label\": \"angular\",\n          \"children\": [\n            {\n              \"to\": \"framework/angular/examples/fixed\",\n              \"label\": \"Fixed\"\n            },\n            {\n              \"to\": \"framework/angular/examples/variable\",\n              \"label\": \"Variable\"\n            },\n            {\n              \"to\": \"framework/angular/examples/dynamic\",\n              \"label\": \"Dynamic\"\n            },\n            {\n              \"to\": \"framework/angular/examples/padding\",\n              \"label\": \"Padding\"\n            },\n            {\n              \"to\": \"framework/angular/examples/sticky\",\n              \"label\": \"Sticky\"\n            },\n            {\n              \"to\": \"framework/angular/examples/infinite-scroll\",\n              \"label\": \"Infinite Scroll\"\n            },\n            {\n              \"to\": \"framework/angular/examples/smooth-scroll\",\n              \"label\": \"Smooth Scroll\"\n            },\n            {\n              \"to\": \"framework/angular/examples/table\",\n              \"label\": \"Table\"\n            },\n            {\n              \"to\": \"framework/angular/examples/window\",\n              \"label\": \"Window\"\n            }\n          ]\n        },\n        {\n          \"label\": \"react\",\n          \"children\": [\n            {\n              \"to\": \"framework/react/examples/fixed\",\n              \"label\": \"Fixed\"\n            },\n            {\n              \"to\": \"framework/react/examples/variable\",\n              \"label\": \"Variable\"\n            },\n            {\n              \"to\": \"framework/react/examples/dynamic\",\n              \"label\": \"Dynamic\"\n            },\n            {\n              \"to\": \"framework/react/examples/padding\",\n              \"label\": \"Padding\"\n            },\n            {\n              \"to\": \"framework/react/examples/sticky\",\n              \"label\": \"Sticky\"\n            },\n            {\n              \"to\": \"framework/react/examples/infinite-scroll\",\n              \"label\": \"Infinite Scroll\"\n            },\n            {\n              \"to\": \"framework/react/examples/smooth-scroll\",\n              \"label\": \"Smooth Scroll\"\n            },\n            {\n              \"to\": \"framework/react/examples/table\",\n              \"label\": \"Table\"\n            },\n            {\n              \"to\": \"framework/react/examples/window\",\n              \"label\": \"Window\"\n            }\n          ]\n        },\n        {\n          \"label\": \"svelte\",\n          \"children\": [\n            {\n              \"to\": \"framework/svelte/examples/fixed\",\n              \"label\": \"Fixed\"\n            },\n            {\n              \"to\": \"framework/svelte/examples/variable\",\n              \"label\": \"Variable\"\n            },\n            {\n              \"to\": \"framework/svelte/examples/dynamic\",\n              \"label\": \"Dynamic\"\n            },\n            {\n              \"to\": \"framework/svelte/examples/sticky\",\n              \"label\": \"Sticky\"\n            },\n            {\n              \"to\": \"framework/svelte/examples/infinite-scroll\",\n              \"label\": \"Infinite Scroll\"\n            },\n            {\n              \"to\": \"framework/svelte/examples/smooth-scroll\",\n              \"label\": \"Smooth Scroll\"\n            },\n            {\n              \"to\": \"framework/svelte/examples/table\",\n              \"label\": \"Table\"\n            }\n          ]\n        },\n        {\n          \"label\": \"vue\",\n          \"children\": [\n            {\n              \"to\": \"framework/vue/examples/fixed\",\n              \"label\": \"Fixed\"\n            },\n            {\n              \"to\": \"framework/vue/examples/variable\",\n              \"label\": \"Variable\"\n            },\n            {\n              \"to\": \"framework/vue/examples/dynamic\",\n              \"label\": \"Dynamic\"\n            },\n            {\n              \"to\": \"framework/vue/examples/sticky\",\n              \"label\": \"Sticky\"\n            },\n            {\n              \"to\": \"framework/vue/examples/infinite-scroll\",\n              \"label\": \"Infinite Scroll\"\n            },\n            {\n              \"to\": \"framework/vue/examples/smooth-scroll\",\n              \"label\": \"Smooth Scroll\"\n            },\n            {\n              \"to\": \"framework/vue/examples/table\",\n              \"label\": \"Table\"\n            },\n            {\n              \"to\": \"framework/vue/examples/padding\",\n              \"label\": \"Padding\"\n            },\n            {\n              \"to\": \"framework/vue/examples/scroll-padding\",\n              \"label\": \"Scroll Padding\"\n            }\n          ]\n        },\n        {\n          \"label\": \"lit\",\n          \"children\": [\n            {\n              \"to\": \"framework/lit/examples/fixed\",\n              \"label\": \"Fixed\"\n            },\n            {\n              \"to\": \"framework/lit/examples/dynamic\",\n              \"label\": \"Dynamic\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "docs/framework/angular/angular-virtual.md",
    "content": "---\ntitle: Angular Virtual\n---\n\nThe `@tanstack/angular-virtual` adapter is a wrapper around the core virtual logic.\n\n## `injectVirtualizer`\n\n```ts\nfunction injectVirtualizer<TScrollElement, TItemElement = unknown>(\n  options: PartialKeys<\n    Omit<VirtualizerOptions<TScrollElement, TItemElement>, 'getScrollElement'>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  > & { scrollElement: ElementRef<TScrollElement> | TScrollElement | undefined },\n): AngularVirtualizer<TScrollElement, TItemElement>\n```\n\nThis function returns an `AngularVirtualizer` instance configured to work with an HTML element as the scrollElement.\n\n## `injectWindowVirtualizer`\n\n```ts\nfunction injectWindowVirtualizer<TItemElement = unknown>(\n  options: PartialKeys<\n    VirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): AngularVirtualizer<Window, TItemElement>\n```\n\nThis function returns a window-based `AngularVirtualizer` instance configured to work with the window as the scrollElement.\n"
  },
  {
    "path": "docs/framework/lit/lit-virtual.md",
    "content": "---\ntitle: Lit Virtual\n---\n\nThe `@tanstack/lit-virtual` adapter is a wrapper around the core virtual logic.\n\n## `createVirtualizer`\n\n```tsx\n\nprivate virtualizerController = new VirtualizerController<TScrollElement, TItemElement = unknown>(\n    options: PartialKeys< VirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n)\n```\n\nThis class stands for a standard `Virtualizer` instance configured to work with an HTML element as the scrollElement.\nThis will create a Lit Controller which can be accessed in the element render method.\n\n```tsx\nrender() {\n    const virtualizer = this.virtualizerController.getVirtualizer();\n    const virtualItems = virtualizer.getVirtualItems();\n} \n)\n```\n\n## `createWindowVirtualizer`\n\n```tsx\nprivate windowVirtualizerController = new WindowVirtualizerController<TItemElement = unknown>(\n    options: PartialKeys< VirtualizerOptions<TItemElement>,\n    'getScrollElement' | 'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n```\n\nThis class stands of window-based `Virtualizer` instance configured to work with an HTML element as the scrollElement.\n"
  },
  {
    "path": "docs/framework/react/react-virtual.md",
    "content": "---\ntitle: React Virtual\n---\n\nThe `@tanstack/react-virtual` adapter is a wrapper around the core virtual logic.\n\n## `useVirtualizer`\n\n```tsx\nfunction useVirtualizer<TScrollElement, TItemElement = unknown>(\n  options: PartialKeys<\n    ReactVirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  >,\n): Virtualizer<TScrollElement, TItemElement>\n```\n\nThis function returns a standard `Virtualizer` instance configured to work with an HTML element as the scrollElement.\n\n## `useWindowVirtualizer`\n\n```tsx\nfunction useWindowVirtualizer<TItemElement = unknown>(\n  options: PartialKeys<\n    ReactVirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): Virtualizer<Window, TItemElement>\n```\n\nThis function returns a window-based `Virtualizer` instance configured to work with the window as the scrollElement.\n\n## React-Specific Options\n\n### `useFlushSync`\n\n```tsx\ntype ReactVirtualizerOptions<TScrollElement, TItemElement> = \n  VirtualizerOptions<TScrollElement, TItemElement> & {\n    useFlushSync?: boolean\n  }\n```\n\nBoth `useVirtualizer` and `useWindowVirtualizer` accept a `useFlushSync` option that controls whether React's `flushSync` is used for synchronous updates.\n\n- **Type**: `boolean`\n- **Default**: `true`\n- **Description**: When `true`, the virtualizer will use `flushSync` from `react-dom` to ensure synchronous rendering during scroll events. This provides the most accurate scrolling behavior but may impact performance in some scenarios.\n\n#### When to disable `useFlushSync`\n\nYou may want to set `useFlushSync: false` in the following scenarios:\n\n- **React 19 compatibility**: In React 19, you may see the following console warning when scrolling:\n  ```\n  flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.\n  ```\n  Setting `useFlushSync: false` will eliminate this warning by allowing React to batch updates naturally.\n- **Performance optimization**: If you experience performance issues with rapid scrolling on lower-end devices\n- **Testing environments**: When running tests that don't require synchronous DOM updates\n- **Non-critical lists**: When slight visual delays during scrolling are acceptable for better overall performance\n\n#### Example\n\n```tsx\nconst virtualizer = useVirtualizer({\n  count: 10000,\n  getScrollElement: () => parentRef.current,\n  estimateSize: () => 50,\n  useFlushSync: false, // Disable synchronous updates\n})\n```\n"
  },
  {
    "path": "docs/framework/solid/solid-virtual.md",
    "content": "---\ntitle: Solid Virtual\n---\n\nThe `@tanstack/solid-virtual` adapter is a wrapper around the core virtual logic.\n\n## `createVirtualizer`\n\n```tsx\nfunction createVirtualizer<TScrollElement, TItemElement = unknown>(\n  options: PartialKeys<\n    VirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  >,\n): Virtualizer<TScrollElement, TItemElement>\n```\n\nThis function returns a standard `Virtualizer` instance configured to work with an HTML element as the scrollElement.\n\n## `createWindowVirtualizer`\n\n```tsx\nfunction createWindowVirtualizer<TItemElement = unknown>(\n  options: PartialKeys<\n    VirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): Virtualizer<Window, TItemElement>\n```\n\nThis function returns a window-based `Virtualizer` instance configured to work with the window as the scrollElement.\n"
  },
  {
    "path": "docs/framework/svelte/svelte-virtual.md",
    "content": "---\ntitle: Svelte Virtual\n---\n\nThe `@tanstack/svelte-virtual` adapter is a wrapper around the core virtual logic.\n\n## `createVirtualizer`\n\n```tsx\nfunction createVirtualizer<TScrollElement, TItemElement = unknown>(\n  options: PartialKeys<\n    VirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  >,\n): Virtualizer<TScrollElement, TItemElement>\n```\n\nThis function returns a standard `Virtualizer` instance configured to work with an HTML element as the scrollElement.\n\n## `createWindowVirtualizer`\n\n```tsx\nfunction createWindowVirtualizer<TItemElement = unknown>(\n  options: PartialKeys<\n    VirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): Virtualizer<Window, TItemElement>\n```\n\nThis function returns a window-based `Virtualizer` instance configured to work with the window as the scrollElement.\n"
  },
  {
    "path": "docs/framework/vue/vue-virtual.md",
    "content": "---\ntitle: Vue Virtual\n---\n\nThe `@tanstack/vue-virtual` adapter is a wrapper around the core virtual logic.\n\n## `useVirtualizer`\n\n```tsx\nfunction useVirtualizer<TScrollElement, TItemElement = unknown>(\n  options: PartialKeys<\n    VirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  >,\n): Virtualizer<TScrollElement, TItemElement>\n```\n\nThis function returns a standard `Virtualizer` instance configured to work with an HTML element as the scrollElement.\n\n## `useWindowVirtualizer`\n\n```tsx\nfunction useWindowVirtualizer<TItemElement = unknown>(\n  options: PartialKeys<\n    VirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): Virtualizer<Window, TItemElement>\n```\n\nThis function returns a window-based `Virtualizer` instance configured to work with the window as the scrollElement.\n"
  },
  {
    "path": "docs/installation.md",
    "content": "---\ntitle: Installation\n---\n\nBefore we dig in to the API, let's get you set up!\n\nInstall your TanStack Virtual adapter as a dependency using your favorite npm package manager\n\n## React Virtual\n\n```bash\nnpm install @tanstack/react-virtual\n```\n\n## Solid Virtual\n\n```bash\nnpm install @tanstack/solid-virtual\n```\n\n## Svelte Virtual\n\n```bash\nnpm install @tanstack/svelte-virtual\n```\n\n## Vue Virtual\n\n```bash\nnpm install @tanstack/vue-virtual\n```\n\n## Lit Virtual\n\n```bash\nnpm install @tanstack/lit-virtual\n```\n\n## Angular Virtual\n\n```bash\nnpm install @tanstack/angular-virtual\n```\n\n## Virtual Core (no framework)\n\n```bash\nnpm install @tanstack/virtual-core\n```\n"
  },
  {
    "path": "docs/introduction.md",
    "content": "---\ntitle: Introduction\n---\n\nTanStack Virtual is a headless UI utility for virtualizing long lists of elements in JS/TS, React, Vue, Svelte, Solid, Lit, and Angular. It is not a component therefore does not ship with or render any markup or styles for you. While this requires a bit of markup and styles from you, you will retain 100% control over your styles, design and implementation.\n\n## The Virtualizer\n\nAt the heart of TanStack Virtual is the `Virtualizer`. Virtualizers can be oriented on either the vertical (default) or horizontal axes which makes it possible to achieve vertical, horizontal and even grid-like virtualization by combining the two axis configurations together.\n\nHere is just a quick example of what it looks like to virtualize a long list within a div using TanStack Virtual in React:\n\n```tsx\nimport { useVirtualizer } from '@tanstack/react-virtual';\n\nfunction App() {\n  // The scrollable element for your list\n  const parentRef = React.useRef(null)\n\n  // The virtualizer\n  const rowVirtualizer = useVirtualizer({\n    count: 10000,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 35,\n  })\n\n  return (\n    <>\n      {/* The scrollable element for your list */}\n      <div\n        ref={parentRef}\n        style={{\n          height: `400px`,\n          overflow: 'auto', // Make it scroll!\n        }}\n      >\n        {/* The large inner element to hold all of the items */}\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {/* Only the visible items in the virtualizer, manually positioned to be in view */}\n          {rowVirtualizer.getVirtualItems().map((virtualItem) => (\n            <div\n              key={virtualItem.key}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: `${virtualItem.size}px`,\n                transform: `translateY(${virtualItem.start}px)`,\n              }}\n            >\n              Row {virtualItem.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n```\n\nLet's dig into some more examples!\n"
  },
  {
    "path": "eslint.config.js",
    "content": "// @ts-check\n\nimport { tanstackConfig } from '@tanstack/eslint-config'\n\nexport default [\n  ...tanstackConfig,\n  {\n    name: 'tanstack/temp',\n    rules: {\n      '@typescript-eslint/naming-convention': 'off',\n      '@typescript-eslint/no-unnecessary-condition': 'off',\n      '@typescript-eslint/no-unsafe-function-type': 'off',\n      'no-self-assign': 'off',\n    },\n  },\n]\n"
  },
  {
    "path": "examples/angular/dynamic/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/dynamic/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/dynamic/README.md",
    "content": "# @tanstack/virtualExampleAngularDynamic\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/dynamic/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"newProjectRoot\": \"projects\",\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-dynamic\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-dynamic\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-dynamic:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-dynamic:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-dynamic:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/dynamic/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-dynamic\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/app.component.ts",
    "content": "import { Component } from '@angular/core'\nimport { RouterLink, RouterOutlet } from '@angular/router'\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [RouterLink, RouterOutlet],\n  template: `\n    <p>\n      These components are using <strong>dynamic</strong> sizes. This means that\n      each element's exact dimensions are unknown when rendered. An estimated\n      dimension is used to get an a initial measurement, then this measurement\n      is readjusted on the fly as each element is rendered.\n    </p>\n\n    <ul>\n      <li><a routerLink=\"./\">List</a></li>\n      <li><a routerLink=\"./window-list\">List - window as scroller</a></li>\n      <li><a routerLink=\"./columns\">Column</a></li>\n      <li><a routerLink=\"./grid\">Grid</a></li>\n    </ul>\n\n    <router-outlet />\n  `,\n  styles: [],\n})\nexport class AppComponent {}\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/app.config.ts",
    "content": "import { ApplicationConfig } from '@angular/core'\nimport { provideRouter } from '@angular/router'\n\nimport { routes } from './app.routes'\n\nexport const appConfig: ApplicationConfig = {\n  providers: [provideRouter(routes)],\n}\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/app.routes.ts",
    "content": "import { Routes } from '@angular/router'\nimport { RowVirtualizerDynamic } from './row-virtualizer-dynamic.component'\nimport { GridVirtualizerDynamic } from './grid-virtualizer-dynamic.component'\nimport { ColumnVirtualizerDynamic } from './column-virtualizer-dynamic.component'\nimport { RowVirtualizerDynamicWindow } from './row-virtualizer-dynamic-window.component'\n\nexport const routes: Routes = [\n  {\n    path: '',\n    component: RowVirtualizerDynamic,\n  },\n  {\n    path: 'window-list',\n    component: RowVirtualizerDynamicWindow,\n  },\n  {\n    path: 'columns',\n    component: ColumnVirtualizerDynamic,\n  },\n  {\n    path: 'grid',\n    component: GridVirtualizerDynamic,\n  },\n]\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/column-virtualizer-dynamic.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  effect,\n  viewChild,\n  viewChildren,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\nimport { sentences } from './utils'\n\n@Component({\n  standalone: true,\n  selector: 'column-virtualizer-dynamic',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Columns</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; height: 100%;\"\n        [style.width.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (col of virtualizer.getVirtualItems(); track col.index) {\n          <div\n            #virtualItem\n            [attr.data-index]=\"col.index\"\n            [class.list-item-even]=\"col.index % 2 === 0\"\n            [class.list-item-odd]=\"col.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; height: 100%;\"\n            [style.width.px]=\"sentences[col.index].length\"\n            [style.transform]=\"'translateX(' + col.start + 'px)'\"\n          >\n            <div>\n              <div>Column {{ col.index }}</div>\n              <div>{{ sentences[col.index] }}</div>\n            </div>\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 400px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class ColumnVirtualizerDynamic {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualItems = viewChildren<ElementRef<HTMLDivElement>>('virtualItem')\n\n  sentences = sentences\n\n  count = this.sentences.length\n\n  #measureItems = effect(\n    () =>\n      this.virtualItems().forEach((el) => {\n        this.virtualizer.measureElement(el.nativeElement)\n      }),\n    { allowSignalWrites: true },\n  )\n\n  virtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: this.count,\n    estimateSize: () => 100,\n    overscan: 5,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/grid-virtualizer-dynamic.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  afterNextRender,\n  computed,\n  effect,\n  signal,\n  viewChild,\n  viewChildren,\n} from '@angular/core'\nimport {\n  injectVirtualizer,\n  injectWindowVirtualizer,\n} from '@tanstack/angular-virtual'\nimport { generateColumns, generateData } from './utils'\n\n@Component({\n  standalone: true,\n  selector: 'grid-virtualizer-dynamic',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Grid</h3>\n    <div\n      #scrollElement\n      class=\"list scroll-container\"\n      style=\"border: 1px solid #c8c8c8\"\n    >\n      <div\n        style=\"position: relative;\"\n        [style.height.px]=\"rowVirtualizer.getTotalSize()\"\n      >\n        @for (row of rowVirtualizer.getVirtualItems(); track row.key) {\n          <div\n            [attr.data-index]=\"row.index\"\n            #virtualRow\n            style=\"position: absolute; top: 0; left: 0; display: flex;\"\n            [style.transform]=\"\n              'translateY(' +\n              (row.start - rowVirtualizer.options().scrollMargin) +\n              'px)'\n            \"\n          >\n            <div [style.width.px]=\"width()[0]\"></div>\n            @for (col of columnVirtualizer.getVirtualItems(); track col.key) {\n              <div\n                style=\"border-bottom: 1px solid #c8c8c8; border-right: 1px solid #c8c8c8; padding: 7px 12px\"\n                [style.minHeight.px]=\"row.index === 0 ? 50 : row.size\"\n                [style.width.px]=\"getColumnWidth(col.index)\"\n              >\n                <div>\n                  {{\n                    row.index === 0\n                      ? columns[col.index].name\n                      : data[row.index][col.index]\n                  }}\n                </div>\n              </div>\n            }\n            <div [style.width.px]=\"width()[1]\"></div>\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      overflow: auto;\n    }\n  `,\n})\nexport class GridVirtualizerDynamic {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n  columns = generateColumns(30)\n  data = generateData(this.columns)\n\n  parentOffset = signal(0)\n\n  constructor() {\n    afterNextRender(() =>\n      this.parentOffset.set(this.scrollElement()!.nativeElement.offsetTop),\n    )\n  }\n\n  getColumnWidth = (index: number) => this.columns[index].width\n\n  rowVirtualizer = injectWindowVirtualizer(() => ({\n    count: this.data.length,\n    estimateSize: () => 350,\n    overscan: 5,\n    scrollMargin: this.parentOffset(),\n  }))\n\n  columnVirtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: this.columns.length,\n    estimateSize: this.getColumnWidth,\n    overscan: 5,\n  }))\n\n  width = computed(\n    () => {\n      const virtualColumns = this.columnVirtualizer.getVirtualItems()\n      return virtualColumns.length > 0\n        ? [\n            virtualColumns[0].start,\n            this.columnVirtualizer.getTotalSize() -\n              virtualColumns[virtualColumns.length - 1].end,\n          ]\n        : [0, 0]\n    },\n    { equal: (a, b) => a[0] === b[0] && a[1] === b[1] },\n  )\n\n  virtualRows = viewChildren<ElementRef<HTMLDivElement>>('virtualRow')\n\n  #measureItems = effect(\n    () =>\n      this.virtualRows().forEach((el) => {\n        this.rowVirtualizer.measureElement(el.nativeElement)\n      }),\n    { allowSignalWrites: true },\n  )\n}\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/row-virtualizer-dynamic-window.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  afterNextRender,\n  effect,\n  signal,\n  untracked,\n  viewChild,\n  viewChildren,\n} from '@angular/core'\nimport { injectWindowVirtualizer } from '@tanstack/angular-virtual'\nimport { sentences } from './utils'\n\n@Component({\n  standalone: true,\n  selector: 'row-virtualizer-dynamic',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <div #scrollElement>\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        <div\n          style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n          [style.transform]=\"\n            'translateY(' +\n            (virtualizer.getVirtualItems()[0]\n              ? virtualizer.getVirtualItems()[0].start - parentOffset()\n              : 0) +\n            'px)'\n          \"\n        >\n          @for (row of virtualizer.getVirtualItems(); track row.index) {\n            <div\n              #virtualItem\n              [attr.data-index]=\"row.index\"\n              [class.list-item-even]=\"row.index % 2 === 0\"\n              [class.list-item-odd]=\"row.index % 2 !== 0\"\n            >\n              <div style=\"padding: 10px 0\">\n                <div>Row {{ row.index }}</div>\n                <div>{{ sentences[row.index] }}</div>\n              </div>\n            </div>\n          }\n        </div>\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 400px;\n      width: 400px;\n      overflow-y: auto;\n      contain: 'strict';\n    }\n  `,\n})\nexport class RowVirtualizerDynamicWindow {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  parentOffset = signal(0)\n\n  constructor() {\n    afterNextRender(() =>\n      this.parentOffset.set(this.scrollElement()!.nativeElement.offsetTop),\n    )\n  }\n\n  virtualItems = viewChildren<ElementRef<HTMLDivElement>>('virtualItem')\n\n  sentences = sentences\n\n  count = this.sentences.length\n\n  #measureItems = effect(\n    () =>\n      this.virtualItems().forEach((el) => {\n        this.virtualizer.measureElement(el.nativeElement)\n      }),\n    { allowSignalWrites: true },\n  )\n\n  virtualizer = injectWindowVirtualizer(() => ({\n    count: this.count,\n    estimateSize: () => 150,\n    scrollMargin: this.parentOffset(),\n  }))\n}\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/row-virtualizer-dynamic.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  effect,\n  viewChild,\n  viewChildren,\n} from '@angular/core'\nimport {\n  AngularVirtualizer,\n  injectVirtualizer,\n} from '@tanstack/angular-virtual'\nimport { sentences } from './utils'\n\n@Component({\n  standalone: true,\n  selector: 'row-virtualizer-dynamic',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Rows</h3>\n    <button type=\"button\" (click)=\"virtualizer.scrollToIndex(0)\">\n      scroll to the top\n    </button>\n    <button type=\"button\" (click)=\"virtualizer.scrollToIndex(count / 2)\">\n      scroll to the middle\n    </button>\n    <button type=\"button\" (click)=\"virtualizer.scrollToIndex(count - 1)\">\n      scroll to the end\n    </button>\n    <hr />\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        <div\n          style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n          [style.transform]=\"\n            'translateY(' +\n            (virtualizer.getVirtualItems()[0]\n              ? virtualizer.getVirtualItems()[0].start\n              : 0) +\n            'px)'\n          \"\n        >\n          @for (row of virtualizer.getVirtualItems(); track row.index) {\n            <div\n              #virtualItem\n              [attr.data-index]=\"row.index\"\n              [class.list-item-even]=\"row.index % 2 === 0\"\n              [class.list-item-odd]=\"row.index % 2 !== 0\"\n            >\n              <div style=\"padding: 10px 0\">\n                <div>Row {{ row.index }}</div>\n                <div>{{ sentences[row.index] }}</div>\n              </div>\n            </div>\n          }\n        </div>\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 400px;\n      width: 400px;\n      overflow-y: auto;\n      contain: 'strict';\n    }\n  `,\n})\nexport class RowVirtualizerDynamic {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualItems = viewChildren<ElementRef<HTMLDivElement>>('virtualItem')\n\n  sentences = sentences\n\n  count = this.sentences.length\n\n  #measureItems = effect(\n    () =>\n      this.virtualItems().forEach((el) => {\n        this.virtualizer.measureElement(el.nativeElement)\n      }),\n    { allowSignalWrites: true },\n  )\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: this.count,\n    estimateSize: () => 120,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/dynamic/src/app/utils.ts",
    "content": "import { faker } from '@faker-js/faker'\n\nexport const generateRandomNumber = (min: number, max: number) =>\n  faker.number.int({ min, max })\n\n// 1000 because 10000 takes many seconds\nexport const sentences = new Array(1000)\n  .fill(true)\n  .map(() => faker.lorem.sentence(generateRandomNumber(20, 70)))\n\ninterface Column {\n  key: string\n  name: string\n  width: number\n}\n\nexport const generateColumns = (count: number) => {\n  return new Array(count).fill(0).map((_, i) => {\n    const key: string = i.toString()\n    return {\n      key,\n      name: `Column ${i}`,\n      width: generateRandomNumber(75, 300),\n    }\n  })\n}\n\nexport const generateData = (columns: Column[], count = 300) => {\n  return new Array(count).fill(0).map((_, rowIndex) =>\n    columns.reduce<string[]>((acc, _curr, colIndex) => {\n      // simulate dynamic size cells\n      const val = faker.lorem.lines(((rowIndex + colIndex) % 10) + 1)\n      acc.push(val)\n      return acc\n    }, []),\n  )\n}\n"
  },
  {
    "path": "examples/angular/dynamic/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularDynamic</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/dynamic/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { appConfig } from './app/app.config'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/dynamic/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/dynamic/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/dynamic/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"],\n    \"paths\": {\n      \"@angular/*\": [\"./node_modules/@angular/*\"]\n    }\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/fixed/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/fixed/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/fixed/README.md",
    "content": "# @tanstack/virtualExampleAngularFixed\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/fixed/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-fixed\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-fixed\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-fixed:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-fixed:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-fixed:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/fixed/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-fixed\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/fixed/src/app/app.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\nimport { ColumnVirtualizerFixed } from './column-virtualizer-fixed.component'\nimport { GridVirtualizerFixed } from './grid-virtualizer-fixed.component'\nimport { RowVirtualizerFixed } from './row-virtualizer-fixed.component'\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [ColumnVirtualizerFixed, GridVirtualizerFixed, RowVirtualizerFixed],\n  template: `\n    <p>\n      These components are using <strong>fixed</strong> sizes. This means that\n      every element's dimensions are hard-coded to the same value and never\n      change.\n    </p>\n\n    <row-virtualizer-fixed />\n    <column-virtualizer-fixed />\n    <grid-virtualizer-fixed />\n  `,\n})\nexport class AppComponent {}\n"
  },
  {
    "path": "examples/angular/fixed/src/app/column-virtualizer-fixed.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'column-virtualizer-fixed',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Columns</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; height: 100%;\"\n        [style.width.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (col of virtualizer.getVirtualItems(); track col.index) {\n          <div\n            [attr.data-index]=\"col.index\"\n            [class.list-item-even]=\"col.index % 2 === 0\"\n            [class.list-item-odd]=\"col.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; height: 100%;\"\n            [style.width.px]=\"col.size\"\n            [style.transform]=\"'translateX(' + col.start + 'px)'\"\n          >\n            Col {{ col.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 100px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class ColumnVirtualizerFixed {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: () => 100,\n    overscan: 5,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/fixed/src/app/grid-virtualizer-fixed.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'grid-virtualizer-fixed',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Grid</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; height: 100%;\"\n        [style.width.px]=\"columnVirtualizer.getTotalSize()\"\n        [style.height.px]=\"rowVirtualizer.getTotalSize()\"\n      >\n        @for (\n          row of rowVirtualizer.getVirtualItems();\n          track row.index;\n          let rowEven = $even\n        ) {\n          @for (\n            col of columnVirtualizer.getVirtualItems();\n            track col.index;\n            let colEven = $even\n          ) {\n            <div\n              [attr.data-index]=\"col.index\"\n              [class]=\"\n                col.index % 2\n                  ? row.index % 2 === 0\n                    ? 'list-item-odd'\n                    : 'list-item-even'\n                  : row.index % 2\n                    ? 'list-item-odd'\n                    : 'list-item-even'\n              \"\n              style=\"position: absolute; top: 0; left: 0;\"\n              [style.height.px]=\"row.size\"\n              [style.width.px]=\"col.size\"\n              [style.transform]=\"\n                'translateX(' +\n                col.start +\n                'px)' +\n                'translateY(' +\n                row.start +\n                'px)'\n              \"\n            >\n              Cell {{ row.index }}, {{ col.index }}\n            </div>\n          }\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 500px;\n      width: 500px;\n      overflow: auto;\n    }\n  `,\n})\nexport class GridVirtualizerFixed {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  rowVirtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: () => 35,\n    overscan: 5,\n  }))\n\n  columnVirtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: () => 100,\n    overscan: 5,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/fixed/src/app/row-virtualizer-fixed.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'row-virtualizer-fixed',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Rows</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (row of virtualizer.getVirtualItems(); track row.index) {\n          <div\n            [attr.data-index]=\"row.index\"\n            [class.list-item-even]=\"row.index % 2 === 0\"\n            [class.list-item-odd]=\"row.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n            [style.height.px]=\"row.size\"\n            [style.transform]=\"'translateY(' + row.start + 'px)'\"\n          >\n            Row {{ row.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 200px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class RowVirtualizerFixed {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: () => 35,\n    overscan: 5,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/fixed/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularFixed</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/fixed/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/fixed/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n"
  },
  {
    "path": "examples/angular/fixed/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/fixed/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/infinite-scroll/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/infinite-scroll/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/infinite-scroll/README.md",
    "content": "# @tanstack/virtualExampleAngularInfiniteScroll\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/infinite-scroll/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-infinite-scroll\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-infinite-scroll\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-infinite-scroll:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-infinite-scroll:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-infinite-scroll:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/infinite-scroll/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-infinite-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@tanstack/angular-query-experimental\": \"5.80.7\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/infinite-scroll/src/app/app.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  computed,\n  effect,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\nimport {\n  QueryClient,\n  injectInfiniteQuery,\n  provideQueryClient,\n} from '@tanstack/angular-query-experimental'\n\nasync function fetchServerPage(\n  limit: number,\n  offset: number = 0,\n): Promise<{ rows: string[]; nextOffset: number }> {\n  const rows = new Array(limit)\n    .fill(0)\n    .map((e, i) => `Async loaded row #${i + offset * limit}`)\n\n  await new Promise((r) => setTimeout(r, 500))\n\n  return { rows, nextOffset: offset + 1 }\n}\n\n@Component({\n  selector: 'infinite-scroll',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <p>\n      This infinite scroll example uses Angular Query's injectInfiniteScroll\n      function to fetch infinite data from a posts endpoint and then a\n      rowVirtualizer is used along with a loader-row placed at the bottom of the\n      list to trigger the next page to load.\n    </p>\n    @if (query.isLoading()) {\n      <p>Loading...</p>\n    } @else if (query.isError()) {\n      <span>Error: {{ query.error()!.message }}</span>\n    } @else {\n      <div #scrollElement class=\"list scroll-container\">\n        <div\n          style=\"position: relative; width: 100%;\"\n          [style.height.px]=\"virtualizer.getTotalSize()\"\n        >\n          @for (row of virtualizer.getVirtualItems(); track row.index) {\n            <div\n              [class.list-item-even]=\"row.index % 2 === 0\"\n              [class.list-item-odd]=\"row.index % 2 !== 0\"\n              style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n              [style.height.px]=\"row.size\"\n              [style.transform]=\"'translateY(' + row.start + 'px)'\"\n            >\n              {{\n                row.index > allRows().length - 1\n                  ? query.hasNextPage()\n                    ? 'Loading more...'\n                    : 'Nothing more to load'\n                  : allRows()[row.index]\n              }}\n            </div>\n          }\n        </div>\n      </div>\n    }\n    @if (query.isFetching() && !query.isFetchingNextPage()) {\n      <div>Background Updating...</div>\n    }\n  `,\n  styles: `\n    .scroll-container {\n      height: 500px;\n      width: 100%;\n      overflow: auto;\n    }\n  `,\n  providers: [provideQueryClient(new QueryClient())],\n})\nexport class InfiniteScrollComponent {\n  query = injectInfiniteQuery(() => ({\n    queryKey: ['rows'],\n    queryFn: ({ pageParam }) => fetchServerPage(10, pageParam),\n    initialPageParam: 0,\n    getNextPageParam: (_lastGroup, groups) => groups.length,\n  }))\n\n  allRows = computed(\n    () => this.query.data()?.pages.flatMap((d) => d.rows) ?? [],\n  )\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: this.query.hasNextPage()\n      ? this.allRows().length + 1\n      : this.allRows().length,\n    estimateSize: () => 100,\n    overscan: 5,\n  }))\n\n  #fetchNextPage = effect(\n    () => {\n      const lastItem =\n        this.virtualizer.getVirtualItems()[\n          this.virtualizer.getVirtualItems().length - 1\n        ]\n      if (!lastItem) {\n        return\n      }\n      if (\n        lastItem.index >= this.allRows().length - 1 &&\n        this.query.hasNextPage() &&\n        !this.query.isFetchingNextPage()\n      ) {\n        this.query.fetchNextPage()\n      }\n    },\n    { allowSignalWrites: true },\n  )\n}\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [InfiniteScrollComponent],\n  template: '<infinite-scroll />',\n})\nexport class AppComponent {}\n"
  },
  {
    "path": "examples/angular/infinite-scroll/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularInfiniteScroll</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/infinite-scroll/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/infinite-scroll/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/infinite-scroll/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/infinite-scroll/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/padding/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/padding/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/padding/README.md",
    "content": "# @tanstack/virtualExampleAngularPadding\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/padding/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-padding\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-padding\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-padding:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-padding:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-padding:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/padding/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-padding\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/padding/src/app/app.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\nimport { ColumnVirtualizerPadding } from './column-virtualizer-padding.component'\nimport { GridVirtualizerPadding } from './grid-virtualizer-padding.component'\nimport { RowVirtualizerPadding } from './row-virtualizer-padding.component'\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [\n    ColumnVirtualizerPadding,\n    GridVirtualizerPadding,\n    RowVirtualizerPadding,\n  ],\n  template: `\n    <p>\n      These components are using <strong>dynamic</strong> sizes. This means that\n      each element's exact dimensions are unknown when rendered. An estimated\n      dimension is used to get an a initial measurement, then this measurement\n      is readjusted on the fly as each element is rendered. Each component has\n      padding at the beginning and end of its scroll container.\n    </p>\n\n    <row-virtualizer-padding [rows]=\"rows\" />\n    <column-virtualizer-padding [columns]=\"columns\" />\n    <grid-virtualizer-padding [columns]=\"columns\" [rows]=\"rows\" />\n  `,\n  styles: [],\n})\nexport class AppComponent {\n  rows = new Array(10000)\n    .fill(true)\n    .map(() => 25 + Math.round(Math.random() * 100))\n\n  columns = new Array(10000)\n    .fill(true)\n    .map(() => 75 + Math.round(Math.random() * 100))\n}\n"
  },
  {
    "path": "examples/angular/padding/src/app/column-virtualizer-padding.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  effect,\n  input,\n  viewChild,\n  viewChildren,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'column-virtualizer-padding',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Columns</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; height: 100%;\"\n        [style.width.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (col of virtualizer.getVirtualItems(); track col.index) {\n          <div\n            #virtualItem\n            [attr.data-index]=\"col.index\"\n            [class.list-item-even]=\"col.index % 2 === 0\"\n            [class.list-item-odd]=\"col.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; height: 100%;\"\n            [style.width.px]=\"columns()[col.index]\"\n            [style.transform]=\"'translateX(' + col.start + 'px)'\"\n          >\n            Column {{ col.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 400px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class ColumnVirtualizerPadding {\n  columns = input.required<number[]>()\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualItems = viewChildren<ElementRef<HTMLDivElement>>('virtualItem')\n\n  #measureItems = effect(\n    () =>\n      this.virtualItems().forEach((el) => {\n        this.virtualizer.measureElement(el.nativeElement)\n      }),\n    { allowSignalWrites: true },\n  )\n\n  virtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: this.columns().length,\n    estimateSize: () => 100,\n    overscan: 5,\n    paddingStart: 100,\n    paddingEnd: 100,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/padding/src/app/grid-virtualizer-padding.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  effect,\n  input,\n  signal,\n  viewChild,\n  viewChildren,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'grid-virtualizer-padding',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Grid</h3>\n    <button type=\"button\" (click)=\"toggleShow()\">Toggle</button>\n    <button\n      type=\"button\"\n      (click)=\"rowVirtualizer.scrollToIndex(rows().length / 2)\"\n    >\n      Scroll to the middle\n    </button>\n    <button\n      type=\"button\"\n      (click)=\"rowVirtualizer.scrollToIndex(rows().length - 1)\"\n    >\n      Scroll to the end\n    </button>\n    @if (show()) {\n      <div #scrollElement class=\"list scroll-container\">\n        <div\n          style=\"position: relative;\"\n          [style.height.px]=\"rowVirtualizer.getTotalSize()\"\n          [style.width.px]=\"columnVirtualizer.getTotalSize()\"\n        >\n          @for (row of rowVirtualizer.getVirtualItems(); track row.index) {\n            @for (col of columnVirtualizer.getVirtualItems(); track col.index) {\n              <div\n                #virtualItem\n                [attr.data-colindex]=\"col.index\"\n                [attr.data-rowindex]=\"row.index\"\n                [class]=\"\n                  col.index % 2\n                    ? row.index % 2 === 0\n                      ? 'list-item-odd'\n                      : 'list-item-even'\n                    : row.index % 2\n                      ? 'list-item-odd'\n                      : 'list-item-even'\n                \"\n                style=\"position: absolute; top: 0; left: 0;\"\n                [style.width.px]=\"columns()[col.index]\"\n                [style.height.px]=\"rows()[row.index]\"\n                [style.transform]=\"\n                  'translateX(' +\n                  col.start +\n                  'px) translateY(' +\n                  row.start +\n                  'px)'\n                \"\n              >\n                <div>Cell {{ row.index }}, {{ col.index }}</div>\n              </div>\n            }\n          }\n        </div>\n      </div>\n    }\n  `,\n  styles: `\n    .scroll-container {\n      height: 400px;\n      width: 500px;\n      overflow: auto;\n    }\n  `,\n})\nexport class GridVirtualizerPadding {\n  rows = input.required<number[]>()\n  columns = input.required<number[]>()\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  rowVirtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: this.rows().length,\n    estimateSize: (index) => this.rows()[index]!,\n    overscan: 5,\n    paddingStart: 200,\n    paddingEnd: 200,\n    indexAttribute: 'data-rowindex',\n  }))\n\n  columnVirtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: this.columns().length,\n    estimateSize: (index) => this.columns()[index]!,\n    overscan: 5,\n    paddingStart: 200,\n    paddingEnd: 200,\n    indexAttribute: 'data-colindex',\n  }))\n\n  virtualItems = viewChildren<ElementRef<HTMLDivElement>>('virtualItem')\n\n  #measureItems = effect(\n    () =>\n      this.virtualItems().forEach((el) => {\n        this.rowVirtualizer.measureElement(el.nativeElement)\n        this.columnVirtualizer.measureElement(el.nativeElement)\n      }),\n    { allowSignalWrites: true },\n  )\n\n  show = signal(true)\n\n  toggleShow() {\n    this.show.update((show) => !show)\n  }\n}\n"
  },
  {
    "path": "examples/angular/padding/src/app/row-virtualizer-padding.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  effect,\n  input,\n  viewChild,\n  viewChildren,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'row-virtualizer-padding',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Rows</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (row of virtualizer.getVirtualItems(); track row.index) {\n          <div\n            #virtualItem\n            [attr.data-index]=\"row.index\"\n            [class.list-item-even]=\"row.index % 2 === 0\"\n            [class.list-item-odd]=\"row.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n            [style.height.px]=\"rows()[row.index]\"\n            [style.transform]=\"'translateY(' + row.start + 'px)'\"\n          >\n            Row {{ row.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 200px;\n      width: 400px;\n      overflow-y: auto;\n    }\n  `,\n})\nexport class RowVirtualizerPadding {\n  rows = input.required<number[]>()\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualItems = viewChildren<ElementRef<HTMLDivElement>>('virtualItem')\n\n  #measureItems = effect(\n    () =>\n      this.virtualItems().forEach((el) => {\n        this.virtualizer.measureElement(el.nativeElement)\n      }),\n    { allowSignalWrites: true },\n  )\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: this.rows().length,\n    estimateSize: () => 50,\n    paddingStart: 100,\n    paddingEnd: 100,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/padding/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularPadding</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/padding/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/padding/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/padding/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/padding/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/smooth-scroll/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/smooth-scroll/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/smooth-scroll/README.md",
    "content": "# @tanstack/virtualExampleAngularSmoothScroll\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/smooth-scroll/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-smooth-scroll\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-smooth-scroll\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-smooth-scroll:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-smooth-scroll:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-smooth-scroll:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/smooth-scroll/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-smooth-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/smooth-scroll/src/app/app.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  signal,\n  viewChild,\n} from '@angular/core'\nimport { elementScroll, injectVirtualizer } from '@tanstack/angular-virtual'\n\nfunction easeInOutQuint(t: number) {\n  return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t\n}\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <p>\n      This smooth scroll example uses the <code>scrollToFn</code> to implement a\n      custom scrolling function for the methods like\n      <code>scrollToIndex</code> and <code>scrollToOffset</code>\n    </p>\n    <div>\n      <button (click)=\"scrollToRandomIndex()\">\n        Scroll To Random Index ({{ randomIndex() }})\n      </button>\n    </div>\n    <br />\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (row of virtualizer.getVirtualItems(); track row.index) {\n          <div\n            [class.list-item-even]=\"row.index % 2 === 0\"\n            [class.list-item-odd]=\"row.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n            [style.height.px]=\"row.size\"\n            [style.transform]=\"'translateY(' + row.start + 'px)'\"\n          >\n            Row {{ row.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 200px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class AppComponent {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  scrollingTime = signal(0)\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: () => 35,\n    overscan: 5,\n    scrollToFn: (offset, options, instance) => {\n      const duration = 1000\n      const start = this.scrollElement()!.nativeElement.scrollTop\n      const startTime = Date.now()\n      this.scrollingTime.set(startTime)\n\n      const run = () => {\n        if (this.scrollingTime() !== startTime) return\n        const now = Date.now()\n        const elapsed = now - startTime\n        const progress = easeInOutQuint(Math.min(elapsed / duration, 1))\n        const interpolated = start + (offset - start) * progress\n\n        if (elapsed < duration) {\n          elementScroll(interpolated, options, instance)\n          requestAnimationFrame(run)\n        } else {\n          elementScroll(interpolated, options, instance)\n        }\n      }\n      requestAnimationFrame(run)\n    },\n  }))\n\n  randomIndex = signal(Math.floor(Math.random() * 10000))\n\n  scrollToRandomIndex() {\n    this.virtualizer.scrollToIndex(this.randomIndex())\n    this.randomIndex.set(Math.floor(Math.random() * 10000))\n  }\n}\n"
  },
  {
    "path": "examples/angular/smooth-scroll/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularSmoothScroll</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/smooth-scroll/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/smooth-scroll/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/smooth-scroll/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/smooth-scroll/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/sticky/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/sticky/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/sticky/README.md",
    "content": "# @tanstack/virtualExampleAngularSticky\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/sticky/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-sticky\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-sticky\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-sticky:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-sticky:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-sticky:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/sticky/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-sticky\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/sticky/src/app/app.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  computed,\n  viewChild,\n} from '@angular/core'\nimport { faker } from '@faker-js/faker'\nimport {\n  injectVirtualizer,\n  defaultRangeExtractor,\n} from '@tanstack/angular-virtual'\n\nconst groupedNames: Record<string, string[]> = {}\n\nArray.from({ length: 1000 })\n  .map(() => faker.person.firstName())\n  .sort()\n  .forEach((name) => {\n    const char = name[0]\n    if (!groupedNames[char]) {\n      groupedNames[char] = []\n    }\n    groupedNames[char].push(name)\n  })\nconst groups = Object.keys(groupedNames)\nconst rows = groups.reduce(\n  (acc: string[], k) => [...acc, k, ...groupedNames[k]],\n  [],\n)\nconst stickyIndexes = groups.map((gn) => rows.findIndex((n) => n === gn))\nconst stickyIndexesSet = new Set(stickyIndexes)\nconst reversedStickyIndexes = [...stickyIndexes].reverse()\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (row of virtualizer.getVirtualItems(); track row.index) {\n          <div\n            [attr.data-index]=\"row.index\"\n            style=\"top: 0; left: 0; width: 100%; background: #fff\"\n            [style.zIndex]=\"isSticky(row.index) ? 1 : null\"\n            [style.borderBottom]=\"\n              isSticky(row.index) ? '1px solid #ddd' : 'none'\n            \"\n            [style.position]=\"isActiveSticky(row.index) ? 'sticky' : 'absolute'\"\n            [style.height.px]=\"row.size\"\n            [style.transform]=\"\n              isActiveSticky(row.index)\n                ? null\n                : 'translateY(' + row.start + 'px)'\n            \"\n          >\n            {{ rows[row.index] }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 300px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class AppComponent {\n  rows = rows\n\n  isSticky = (index: number) => stickyIndexesSet.has(index)\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: this.rows.length,\n    estimateSize: () => 50,\n    rangeExtractor: (range) => {\n      const next = new Set([\n        reversedStickyIndexes.find((index) => range.startIndex >= index)!,\n        ...defaultRangeExtractor(range),\n      ])\n      return [...next].sort((a, b) => a - b)\n    },\n  }))\n\n  activeStickyIndex = computed(() => {\n    return this.virtualizer.getVirtualItems()[0]?.index\n  })\n\n  isActiveSticky = (index: number) => this.activeStickyIndex() === index\n}\n"
  },
  {
    "path": "examples/angular/sticky/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularSticky</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/sticky/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/sticky/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/sticky/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/sticky/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/table/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/table/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/table/README.md",
    "content": "# @tanstack/virtualExampleAngularTable\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/table/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-table\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-table\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-table:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-table:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-table:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/table/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-table\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/angular-table\": \"8.21.3\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/table/src/app/app.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  computed,\n  signal,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\nimport {\n  ColumnDef,\n  createAngularTable,\n  getCoreRowModel,\n  getSortedRowModel,\n  SortingState,\n  FlexRenderDirective,\n  SortDirection,\n} from '@tanstack/angular-table'\nimport { makeData, type Person } from './make-data'\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [FlexRenderDirective],\n  template: `\n    <p>\n      For tables, the basis for the offset of the translate css function is from\n      the row's initial position itself. Because of this, we need to calculate\n      the translateY pixel count different and base it off the the index.\n    </p>\n    <br />\n\n    <div #scrollElement class=\"list scroll-container\">\n      <div [style.height.px]=\"virtualizer.getTotalSize()\">\n        <table>\n          <thead>\n            @for (\n              headerGroup of table.getHeaderGroups();\n              track headerGroup.id\n            ) {\n              <tr>\n                @for (header of headerGroup.headers; track header.id) {\n                  <th\n                    [attr.colspan]=\"header.colSpan\"\n                    [style.width.px]=\"header.getSize()\"\n                  >\n                    @if (!header.isPlaceholder) {\n                      <div\n                        [class.cursor-pointer]=\"header.column.getCanSort()\"\n                        [class.select-none]=\"header.column.getCanSort()\"\n                        (click)=\"\n                          header.column.getToggleSortingHandler()?.($event)\n                        \"\n                      >\n                        <ng-container\n                          *flexRender=\"\n                            header.column.columnDef.header;\n                            props: header.getContext();\n                            let headerText\n                          \"\n                        >\n                          <span>{{ headerText }}</span>\n                          {{ getSortIcon(header.column.getIsSorted()) }}\n                        </ng-container>\n                      </div>\n                    }\n                  </th>\n                }\n              </tr>\n            }\n          </thead>\n          <tbody>\n            @for (\n              virtualRow of virtualizer.getVirtualItems();\n              track data[virtualRow.index].id\n            ) {\n              <tr\n                [style.height.px]=\"virtualRow.size\"\n                [style.transform]=\"\n                  'translateY(' +\n                  (virtualRow.start - $index * virtualRow.size) +\n                  'px)'\n                \"\n              >\n                @for (\n                  cell of rows()[virtualRow.index].getVisibleCells();\n                  track cell.id\n                ) {\n                  <td>\n                    <ng-container\n                      *flexRender=\"\n                        cell.column.columnDef.cell;\n                        props: cell.getContext();\n                        let cellText\n                      \"\n                    >\n                      <span>{{ cellText }}</span>\n                    </ng-container>\n                  </td>\n                }\n              </tr>\n            }\n          </tbody>\n        </table>\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 600px;\n      overflow: auto;\n    }\n  `,\n})\nexport class AppComponent {\n  data = makeData(50_000)\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  sorting = signal<SortingState>([])\n\n  sortIcons = { asc: '🔼', desc: '🔽' }\n\n  getSortIcon(sorting: false | SortDirection) {\n    return sorting ? this.sortIcons[sorting] : null\n  }\n\n  columns: ColumnDef<Person>[] = [\n    {\n      accessorKey: 'id',\n      header: 'ID',\n      size: 60,\n    },\n    {\n      accessorKey: 'firstName',\n      cell: (info) => info.getValue(),\n    },\n    {\n      accessorFn: (row) => row.lastName,\n      id: 'lastName',\n      cell: (info) => info.getValue(),\n      header: 'Last Name',\n    },\n    {\n      accessorKey: 'age',\n      header: () => 'Age',\n      size: 50,\n    },\n    {\n      accessorKey: 'visits',\n      header: 'Visits',\n      size: 50,\n    },\n    {\n      accessorKey: 'status',\n      header: 'Status',\n    },\n    {\n      accessorKey: 'progress',\n      header: 'Profile Progress',\n      size: 80,\n    },\n    {\n      accessorKey: 'createdAt',\n      header: 'Created At',\n      cell: (info) => info.getValue<Date>().toLocaleString(),\n    },\n  ]\n\n  table = createAngularTable(() => ({\n    data: this.data,\n    columns: this.columns,\n    state: {\n      sorting: this.sorting(),\n    },\n    onSortingChange: (updaterOrValue) =>\n      typeof updaterOrValue === 'function'\n        ? this.sorting.update(updaterOrValue)\n        : this.sorting.set(updaterOrValue),\n    getCoreRowModel: getCoreRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    debugTable: true,\n  }))\n\n  rows = computed(() => this.table.getRowModel().rows)\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: this.data.length,\n    estimateSize: () => 34,\n    overscan: 20,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/table/src/app/make-data.ts",
    "content": "import { faker } from '@faker-js/faker'\n\nexport type Person = {\n  id: number\n  firstName: string\n  lastName: string\n  age: number\n  visits: number\n  progress: number\n  status: 'relationship' | 'complicated' | 'single'\n  createdAt: Date\n}\n\nconst range = (len: number) => {\n  const arr: number[] = []\n  for (let i = 0; i < len; i++) {\n    arr.push(i)\n  }\n  return arr\n}\n\nconst newPerson = (index: number): Person => {\n  return {\n    id: index + 1,\n    firstName: faker.person.firstName(),\n    lastName: faker.person.lastName(),\n    age: faker.number.int(40),\n    visits: faker.number.int(1000),\n    progress: faker.number.int(100),\n    createdAt: faker.datatype.datetime({ max: new Date().getTime() }),\n    status: faker.helpers.shuffle<Person['status']>([\n      'relationship',\n      'complicated',\n      'single',\n    ])[0]!,\n  }\n}\n\nexport function makeData(...lens: number[]) {\n  const makeDataLevel = (depth = 0): Person[] => {\n    const len = lens[depth]!\n    return range(len).map((d): Person => {\n      return {\n        ...newPerson(d),\n      }\n    })\n  }\n\n  return makeDataLevel()\n}\n"
  },
  {
    "path": "examples/angular/table/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularTable</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/table/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/table/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/table/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/table/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/variable/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/variable/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/variable/README.md",
    "content": "# @tanstack/virtualExampleAngularVariable\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/variable/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-variable\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-variable\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-variable:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-variable:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-variable:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/variable/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-variable\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/variable/src/app/app.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\nimport { ColumnVirtualizerVariable } from './column-virtualizer-variable.component'\nimport { GridVirtualizerVariable } from './grid-virtualizer-variable.component'\nimport { RowVirtualizerVariable } from './row-virtualizer-variable.component'\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [\n    ColumnVirtualizerVariable,\n    GridVirtualizerVariable,\n    RowVirtualizerVariable,\n  ],\n  template: `\n    <p>\n      These components are using <strong>variable</strong> sizes. This means\n      that each element has a unique, but knowable dimension at render time.\n    </p>\n\n    <row-virtualizer-variable [rows]=\"rows\" />\n    <column-virtualizer-variable [columns]=\"columns\" />\n    <grid-virtualizer-variable [columns]=\"columns\" [rows]=\"rows\" />\n  `,\n  styles: [],\n})\nexport class AppComponent {\n  rows = new Array(10000)\n    .fill(true)\n    .map(() => 25 + Math.round(Math.random() * 100))\n\n  columns = new Array(10000)\n    .fill(true)\n    .map(() => 75 + Math.round(Math.random() * 100))\n}\n"
  },
  {
    "path": "examples/angular/variable/src/app/column-virtualizer-variable.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  input,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'column-virtualizer-variable',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Columns</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; height: 100%;\"\n        [style.width.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (col of virtualizer.getVirtualItems(); track col.index) {\n          <div\n            [attr.data-index]=\"col.index\"\n            [class.list-item-even]=\"col.index % 2 === 0\"\n            [class.list-item-odd]=\"col.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; height: 100%;\"\n            [style.width.px]=\"col.size\"\n            [style.transform]=\"'translateX(' + col.start + 'px)'\"\n          >\n            Col {{ col.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 100px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class ColumnVirtualizerVariable {\n  columns = input.required<number[]>()\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: (index) => this.columns()[index]!,\n    overscan: 5,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/variable/src/app/grid-virtualizer-variable.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  input,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'grid-virtualizer-variable',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Grid</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; height: 100%;\"\n        [style.width.px]=\"columnVirtualizer.getTotalSize()\"\n        [style.height.px]=\"rowVirtualizer.getTotalSize()\"\n      >\n        @for (\n          row of rowVirtualizer.getVirtualItems();\n          track row.index;\n          let rowEven = $even\n        ) {\n          @for (\n            col of columnVirtualizer.getVirtualItems();\n            track col.index;\n            let colEven = $even\n          ) {\n            <div\n              [attr.data-index]=\"col.index\"\n              [class]=\"\n                col.index % 2\n                  ? row.index % 2 === 0\n                    ? 'list-item-odd'\n                    : 'list-item-even'\n                  : row.index % 2\n                    ? 'list-item-odd'\n                    : 'list-item-even'\n              \"\n              style=\"position: absolute; top: 0; left: 0;\"\n              [style.height.px]=\"row.size\"\n              [style.width.px]=\"col.size\"\n              [style.transform]=\"\n                'translateX(' +\n                col.start +\n                'px)' +\n                'translateY(' +\n                row.start +\n                'px)'\n              \"\n            >\n              Cell {{ row.index }}, {{ col.index }}\n            </div>\n          }\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 500px;\n      width: 500px;\n      overflow: auto;\n    }\n  `,\n})\nexport class GridVirtualizerVariable {\n  rows = input.required<number[]>()\n\n  columns = input.required<number[]>()\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  rowVirtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: (index) => this.rows()[index]!,\n    overscan: 5,\n  }))\n\n  columnVirtualizer = injectVirtualizer(() => ({\n    horizontal: true,\n    scrollElement: this.scrollElement(),\n    count: 10000,\n    estimateSize: (index) => this.columns()[index]!,\n    overscan: 5,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/variable/src/app/row-virtualizer-variable.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  input,\n  viewChild,\n} from '@angular/core'\nimport { injectVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  standalone: true,\n  selector: 'row-virtualizer-variable',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <h3 style=\"margin-top: 2rem\">Rows</h3>\n    <div #scrollElement class=\"list scroll-container\">\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (row of virtualizer.getVirtualItems(); track row.index) {\n          <div\n            [attr.data-index]=\"row.index\"\n            [class.list-item-even]=\"row.index % 2 === 0\"\n            [class.list-item-odd]=\"row.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n            [style.height.px]=\"row.size\"\n            [style.transform]=\"'translateY(' + row.start + 'px)'\"\n          >\n            Row {{ row.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 200px;\n      width: 400px;\n      overflow: auto;\n    }\n  `,\n})\nexport class RowVirtualizerVariable {\n  rows = input.required<number[]>()\n\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualizer = injectVirtualizer(() => ({\n    scrollElement: this.scrollElement(),\n    count: this.rows().length,\n    estimateSize: (index) => this.rows()[index]!,\n    overscan: 5,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/variable/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularVariable</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/variable/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/variable/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/variable/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/variable/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/angular/window/.devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Node.js\",\n  \"image\": \"mcr.microsoft.com/devcontainers/javascript-node:18\"\n}\n"
  },
  {
    "path": "examples/angular/window/.gitignore",
    "content": "# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.\n\n# Compiled output\n/dist\n/tmp\n/out-tsc\n/bazel-out\n\n# Node\n/node_modules\nnpm-debug.log\nyarn-error.log\n\n# IDEs and editors\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# Visual Studio Code\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# Miscellaneous\n/.angular/cache\n.sass-cache/\n/connect.lock\n/coverage\n/libpeerconnection.log\ntestem.log\n/typings\n\n# System files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "examples/angular/window/README.md",
    "content": "# @tanstack/virtualExampleAngularWindow\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n"
  },
  {
    "path": "examples/angular/window/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"cli\": {\n    \"packageManager\": \"pnpm\",\n    \"analytics\": false,\n    \"cache\": {\n      \"enabled\": false\n    }\n  },\n  \"projects\": {\n    \"@tanstack/virtual-example-angular-window\": {\n      \"projectType\": \"application\",\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"prefix\": \"app\",\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:application\",\n          \"options\": {\n            \"outputPath\": \"dist/tanstack/virtual-example-angular-window\",\n            \"index\": \"src/index.html\",\n            \"browser\": \"src/main.ts\",\n            \"polyfills\": [\"zone.js\"],\n            \"tsConfig\": \"tsconfig.app.json\",\n            \"assets\": [\"src/favicon.ico\"],\n            \"styles\": [\"src/styles.css\"],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"outputHashing\": \"all\"\n            },\n            \"development\": {\n              \"optimization\": false,\n              \"extractLicenses\": false,\n              \"sourceMap\": true\n            }\n          },\n          \"defaultConfiguration\": \"production\"\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"configurations\": {\n            \"production\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-window:build:production\"\n            },\n            \"development\": {\n              \"buildTarget\": \"@tanstack/virtual-example-angular-window:build:development\"\n            }\n          },\n          \"defaultConfiguration\": \"development\"\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"buildTarget\": \"@tanstack/virtual-example-angular-window:build\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/angular/window/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-example-angular-window\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"watch\": \"ng build --watch --configuration development\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"^18.1.0\",\n    \"@angular/common\": \"^18.1.0\",\n    \"@angular/compiler\": \"^18.1.0\",\n    \"@angular/core\": \"^18.1.0\",\n    \"@angular/forms\": \"^18.1.0\",\n    \"@angular/platform-browser\": \"^18.1.0\",\n    \"@angular/platform-browser-dynamic\": \"^18.1.0\",\n    \"@angular/router\": \"^18.1.0\",\n    \"@tanstack/angular-virtual\": \"^4.0.11\",\n    \"rxjs\": \"^7.8.2\",\n    \"tslib\": \"^2.8.1\",\n    \"zone.js\": \"0.15.1\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"^18.1.0\",\n    \"@angular/cli\": \"^18.1.0\",\n    \"@angular/compiler-cli\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  }\n}\n"
  },
  {
    "path": "examples/angular/window/src/app/app.component.ts",
    "content": "import {\n  ChangeDetectionStrategy,\n  Component,\n  ElementRef,\n  viewChild,\n} from '@angular/core'\nimport { injectWindowVirtualizer } from '@tanstack/angular-virtual'\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <p>\n      In many cases, when implementing a virtualizer with a window as the\n      scrolling element, developers often find the need to specify a\n      \"scrollMargin.\" The scroll margin is a crucial setting that defines the\n      space or gap between the start of the page and the edges of the list.\n    </p>\n    <h3>Window Scroller</h3>\n    <div #scrollElement class=\"list\">\n      <div\n        style=\"position: relative; width: 100%;\"\n        [style.height.px]=\"virtualizer.getTotalSize()\"\n      >\n        @for (row of virtualizer.getVirtualItems(); track row.key) {\n          <div\n            #virtualItem\n            [class.list-item-even]=\"row.index % 2 === 0\"\n            [class.list-item-odd]=\"row.index % 2 !== 0\"\n            style=\"position: absolute; top: 0; left: 0; width: 100%;\"\n            [style.height.px]=\"row.size\"\n            [style.transform]=\"\n              'translateY(' +\n              (row.start - virtualizer.options().scrollMargin) +\n              'px)'\n            \"\n          >\n            Row {{ row.index }}\n          </div>\n        }\n      </div>\n    </div>\n  `,\n  styles: `\n    .scroll-container {\n      height: 400px;\n      width: 400px;\n      overflow-y: auto;\n      contain: 'strict';\n    }\n  `,\n})\nexport class AppComponent {\n  scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n  virtualizer = injectWindowVirtualizer(() => ({\n    count: 10000,\n    estimateSize: () => 35,\n    overscan: 5,\n    scrollMargin: this.scrollElement()?.nativeElement.offsetTop,\n  }))\n}\n"
  },
  {
    "path": "examples/angular/window/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>@tanstack/virtualExampleAngularWindow</title>\n    <base href=\"/\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n  </head>\n  <body>\n    <app-root></app-root>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/angular/window/src/main.ts",
    "content": "import { bootstrapApplication } from '@angular/platform-browser'\nimport { AppComponent } from './app/app.component'\n\nbootstrapApplication(AppComponent).catch((err) => console.error(err))\n"
  },
  {
    "path": "examples/angular/window/src/styles.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\n.list-item-odd {\n  background-color: #fff;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/angular/window/tsconfig.app.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./out-tsc/app\",\n    \"types\": []\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src/**/*.d.ts\"]\n}\n"
  },
  {
    "path": "examples/angular/window/tsconfig.json",
    "content": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"strict\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"experimentalDecorators\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"target\": \"ES2022\",\n    \"module\": \"ES2022\",\n    \"useDefineForClassFields\": false,\n    \"lib\": [\"ES2022\", \"dom\"]\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true,\n    \"strictTemplates\": true\n  }\n}\n"
  },
  {
    "path": "examples/lit/dynamic/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/lit/dynamic/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `npm`\n- `npm run start` or `npm run start`\n"
  },
  {
    "path": "examples/lit/dynamic/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n    <my-app></my-app>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/lit/dynamic/package.json",
    "content": "{\n  \"name\": \"tanstack-lit-virtual-example-dynamic\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/lit-virtual\": \"^3.13.24\",\n    \"@tanstack/virtual-core\": \"^3.13.23\",\n    \"lit\": \"^3.3.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^24.5.2\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/lit/dynamic/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n"
  },
  {
    "path": "examples/lit/dynamic/src/main.ts",
    "content": "import { customElement, property } from 'lit/decorators.js'\nimport { Ref, createRef, ref } from 'lit/directives/ref.js'\nimport { html, LitElement } from 'lit'\nimport { faker } from '@faker-js/faker'\nimport { repeat } from 'lit/directives/repeat.js'\nimport {\n  VirtualizerController,\n  WindowVirtualizerController,\n} from '@tanstack/lit-virtual'\n\ninterface Column {\n  key: string\n  name: string\n  width: number\n}\n\nfunction randomNumber(min: number, max: number) {\n  return faker.number.int({ min, max })\n}\n\nconst sentences = new Array(10000)\n  .fill(true)\n  .map(() => faker.lorem.sentence(randomNumber(20, 70)))\n\nconst generateColumns = (count: number) => {\n  return new Array(count).fill(0).map((_, i) => {\n    const key: string = i.toString()\n    return {\n      key,\n      name: `Column ${i}`,\n      width: randomNumber(75, 300),\n    }\n  })\n}\n\nconst generateData = (columns: Column[], count = 300) => {\n  return new Array(count).fill(0).map((_, rowIndex) =>\n    columns.reduce<string[]>((acc, _curr, colIndex) => {\n      // simulate dynamic size cells\n      const val = faker.lorem.lines(((rowIndex + colIndex) % 10) + 1)\n\n      acc.push(val)\n\n      return acc\n    }, []),\n  )\n}\n\n@customElement('row-virtualizer-dynamic')\nclass RowVirtualizerDynamic extends LitElement {\n  private scrollElementRef: Ref<HTMLDivElement> = createRef()\n\n  private virtualizerController: VirtualizerController<HTMLDivElement, Element>\n\n  constructor() {\n    super()\n    this.virtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count: sentences.length,\n      estimateSize: () => 45,\n    })\n  }\n\n  render() {\n    const virtualizer = this.virtualizerController.getVirtualizer()\n    const virtualRows = virtualizer.getVirtualItems()\n    const count = sentences.length\n\n    return html`\n      <div>\n        <button @click=${() => virtualizer.scrollToIndex(0)}>\n          scroll to the top\n        </button>\n        <button @click=${() => virtualizer.scrollToIndex(count / 2)}>\n          scroll to the middle\n        </button>\n        <button @click=${() => virtualizer.scrollToIndex(count - 1)}>\n          scroll to the end\n        </button>\n        <div class=\"list scroll-container\" ${ref(this.scrollElementRef)}>\n          <div\n            style=\"position: relative; height: ${virtualizer.getTotalSize()}px; width: 100%;\"\n          >\n            <div\n              style=\"position:absolute;top:0;left:0;width:100%;transform:translateY(${virtualRows[0]\n                ? virtualRows[0].start\n                : 0}px);\"\n            >\n              ${repeat(\n                virtualRows,\n                (virtualRow) => virtualRow.key,\n                (virtualRow) =>\n                  html` <div\n                    data-index=\"${virtualRow.index}\"\n                    ${ref(virtualizer.measureElement)}\n                    class=\"${virtualRow.index % 2 === 0\n                      ? 'list-item-even'\n                      : 'list-item-odd'}\"\n                  >\n                    <div style=\"padding: 10px 0;\">\n                      <div>Row ${virtualRow.index}</div>\n                      <div>${sentences[virtualRow.index]}</div>\n                    </div>\n                  </div>`,\n              )}\n            </div>\n          </div>\n        </div>\n      </div>\n      <style>\n        .list {\n          border: 1px solid #e6e4dc;\n          max-width: 100%;\n        }\n\n        .list-item-even,\n        .list-item-odd {\n          display: flex;\n          align-items: center;\n          justify-content: center;\n        }\n\n        .list-item-even {\n          background-color: #e6e4dc;\n        }\n\n        .scroll-container {\n          height: 400px;\n          width: 400px;\n          overflow-y: auto;\n        }\n      </style>\n    `\n  }\n}\n\n@customElement('column-virtualizer-dynamic')\nclass ColumnVirtualizerDynamic extends LitElement {\n  private scrollElementRef: Ref<HTMLDivElement> = createRef()\n\n  private virtualizerController: VirtualizerController<HTMLDivElement, Element>\n\n  constructor() {\n    super()\n    this.virtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count: sentences.length,\n      estimateSize: () => 45,\n      horizontal: true,\n    })\n  }\n\n  render() {\n    const virtualizer = this.virtualizerController.getVirtualizer()\n    const virtualColumns = virtualizer.getVirtualItems()\n\n    return html`\n      <div>\n        <div class=\"list scroll-container\" ${ref(this.scrollElementRef)}>\n          <div\n            style=\"position: relative; width: ${virtualizer.getTotalSize()}px; height: 100%;\"\n          >\n            ${repeat(\n              virtualColumns,\n              (virtualColumn) => virtualColumn.key,\n              (virtualColumn) => html`\n                <div\n                  data-index=\"${virtualColumn.index}\"\n                  style=\"position:absolute;top:0;left:0;height:100%;transform:translateX(${virtualColumn.start}px)\"\n                  ${ref(virtualizer.measureElement)}\n                  class=\"${virtualColumn.index % 2 === 0\n                    ? 'list-item-even'\n                    : 'list-item-odd'}\"\n                >\n                  <div style=\"width:${sentences[virtualColumn.index].length}px\">\n                    <div>Column ${virtualColumn.index}</div>\n                    <div>${sentences[virtualColumn.index]}</div>\n                  </div>\n                </div>\n              `,\n            )}\n          </div>\n        </div>\n      </div>\n      <style>\n        *,\n        *:before,\n        *:after {\n          box-sizing: border-box;\n        }\n        .list {\n          border: 1px solid #e6e4dc;\n          max-width: 100%;\n        }\n\n        .list-item-even {\n          background-color: #e6e4dc;\n        }\n\n        .scroll-container {\n          height: 400px;\n          width: 400px;\n          overflow-y: auto;\n        }\n      </style>\n    `\n  }\n}\n\n@customElement('grid-virtualizer-dynamic')\nclass GridVirtualizerDynamic extends LitElement {\n  @property()\n  private data: string[][]\n\n  @property()\n  private columns: Column[]\n\n  private parentElementRef: Ref<HTMLDivElement> = createRef()\n  private virtualizerController: WindowVirtualizerController<Element>\n\n  private columnVirtualizerController: VirtualizerController<\n    HTMLDivElement,\n    Element\n  >\n\n  private getColumnWidth(index: number) {\n    return this.columns[index].width\n  }\n\n  connectedCallback() {\n    this.columnVirtualizerController = new VirtualizerController(this, {\n      horizontal: true,\n      count: this.columns.length,\n      getScrollElement: () => this.parentElementRef.value,\n      estimateSize: (index) => this.getColumnWidth(index),\n      overscan: 5,\n    })\n    this.virtualizerController = new WindowVirtualizerController(this, {\n      count: this.data.length,\n      estimateSize: () => 350,\n      overscan: 5,\n    })\n    super.connectedCallback()\n  }\n\n  render() {\n    const virtualizer = this.virtualizerController.getVirtualizer()\n    const columnVirtualizer = this.columnVirtualizerController.getVirtualizer()\n    const columnItems = columnVirtualizer.getVirtualItems()\n    const [before, after] =\n      columnItems.length > 0\n        ? [\n            columnItems[0].start,\n            columnVirtualizer.getTotalSize() -\n              columnItems[columnItems.length - 1].end,\n          ]\n        : [0, 0]\n\n    return html`\n      <div\n        ${ref(this.parentElementRef)}\n        style=\"overflow-y: auto;border: 1px solid #c8c8c8\"\n      >\n        <div\n          style=\"position: relative; height: ${virtualizer.getTotalSize()}px\"\n        >\n          ${repeat(\n            virtualizer.getVirtualItems(),\n            (row) => row.key,\n            (row) => html`\n              <div\n                data-index=\"${row.index}\"\n                ${ref(virtualizer.measureElement)}\n                style=\"position:absolute;top:0;left:0;transform:translateY(${row.start -\n                virtualizer.options.scrollMargin}px);display:flex\"\n              >\n                <div style=\"width:${before}px\"></div>\n                ${columnItems.map(\n                  (column) =>\n                    html`<div\n                      style=\"width: ${this.getColumnWidth(\n                        column.index,\n                      )}px;border-bottom: 1px solid #c8c8c8;border-right: 1px solid #c8c8c8; padding: 7px 12px;min-height: ${row.index ===\n                      0\n                        ? '50px'\n                        : `100px`}\"\n                    >\n                      ${row.index === 0\n                        ? html`<div>${this.columns[column.index].name}</div>`\n                        : html`<div>\n                            ${this.data[row.index][column.index]}\n                          </div>`}\n                    </div>`,\n                )}\n                <div style=\"width:${after}px\"></div>\n              </div>\n            `,\n          )}\n        </div>\n      </div>\n      <style></style>\n    `\n  }\n}\n\n@customElement('my-app')\nexport class MyApp extends LitElement {\n  protected render() {\n    const { pathname } = window.location\n\n    return html`\n      <div>\n        <p>\n          These components are using <strong>dynamic</strong> sizes. This means\n          that each element's exact dimensions are unknown when rendered. An\n          estimated dimension is used as the initial measurement, then this\n          measurement is readjusted on the fly as each element is rendered.\n        </p>\n        <nav>\n          <ul>\n            <li>\n              <a href=\"/\">List</a>\n            </li>\n            <li>\n              <a href=\"/window-list\">List - window as scroller</a>\n            </li>\n            <li>\n              <a href=\"/columns\">Column</a>\n            </li>\n            <li>\n              <a href=\"/grid\">Grid</a>\n            </li>\n          </ul>\n        </nav>\n\n        ${(() => {\n          switch (pathname) {\n            case '/':\n              return html`<row-virtualizer-dynamic></row-virtualizer-dynamic>`\n            case '/columns':\n              return html`<column-virtualizer-dynamic></column-virtualizer-dynamic>`\n            case '/grid': {\n              const columns = generateColumns(30)\n              const data = generateData(columns)\n              return html`<grid-virtualizer-dynamic\n                .columns=\"${columns}\"\n                .data=\"${data}\"\n              ></grid-virtualizer-dynamic>`\n            }\n            default:\n              return html`<div>Not found</div>`\n          }\n        })()}\n      </div>\n    `\n  }\n}\n"
  },
  {
    "path": "examples/lit/dynamic/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"outDir\": \"./build/types\",\n    \"target\": \"ESNext\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"node\",\n    \"experimentalDecorators\": true,\n    \"useDefineForClassFields\": false\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/lit/dynamic/vite.config.js",
    "content": "import { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [],\n})\n"
  },
  {
    "path": "examples/lit/fixed/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/lit/fixed/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `npm`\n- `npm run start` or `npm run start`\n"
  },
  {
    "path": "examples/lit/fixed/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n    <my-app></my-app>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/lit/fixed/package.json",
    "content": "{\n  \"name\": \"tanstack-lit-virtual-example-fixed\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/lit-virtual\": \"^3.13.24\",\n    \"@tanstack/virtual-core\": \"^3.13.23\",\n    \"lit\": \"^3.3.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^24.5.2\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/lit/fixed/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n"
  },
  {
    "path": "examples/lit/fixed/src/main.ts",
    "content": "import { customElement, property } from 'lit/decorators.js'\nimport { Ref, createRef, ref } from 'lit/directives/ref.js'\nimport { html, LitElement } from 'lit'\nimport { faker } from '@faker-js/faker'\nimport { repeat } from 'lit/directives/repeat.js'\nimport {\n  VirtualizerController,\n  WindowVirtualizerController,\n} from '@tanstack/lit-virtual'\n\ninterface Column {\n  key: string\n  name: string\n  width: number\n}\n\nfunction randomNumber(min: number, max: number) {\n  return faker.number.int({ min, max })\n}\n\nconst sentences = new Array(10000)\n  .fill(true)\n  .map(() => faker.lorem.sentence(randomNumber(20, 70)))\n\nconst generateColumns = (count: number) => {\n  return new Array(count).fill(0).map((_, i) => {\n    const key: string = i.toString()\n    return {\n      key,\n      name: `Column ${i}`,\n      width: randomNumber(75, 300),\n    }\n  })\n}\n\nconst generateData = (columns: Column[], count = 300) => {\n  return new Array(count).fill(0).map((_, rowIndex) =>\n    columns.reduce<string[]>((acc, _curr, colIndex) => {\n      // simulate dynamic size cells\n      const val = faker.lorem.lines(((rowIndex + colIndex) % 10) + 1)\n\n      acc.push(val)\n\n      return acc\n    }, []),\n  )\n}\n\n@customElement('row-virtualizer-fixed')\nclass RowVirtualizerFixed extends LitElement {\n  private scrollElementRef: Ref<HTMLDivElement> = createRef()\n\n  private virtualizerController: VirtualizerController<HTMLDivElement, Element>\n\n  constructor() {\n    super()\n    this.virtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count: 10000,\n      estimateSize: () => 35,\n      overscan: 5,\n    })\n  }\n\n  render() {\n    const virtualizer = this.virtualizerController.getVirtualizer()\n    const virtualRows = virtualizer.getVirtualItems()\n\n    return html`\n      <div>\n        <div class=\"list scroll-container\" ${ref(this.scrollElementRef)}>\n          <div\n            style=\"position: relative; height: ${virtualizer.getTotalSize()}px; width: 100%;\"\n          >\n            ${repeat(\n              virtualRows,\n              (virtualRow) => virtualRow.key,\n              (virtualRow) =>\n                html` <div\n                  class=\"${virtualRow.index % 2 === 0\n                    ? 'list-item-even'\n                    : 'list-item-odd'}\"\n                  style=\"position: absolute; left: 0; top: 0; width: 100%; height: ${virtualRow.size}px; transform: translateY(${virtualRow.start}px)\"\n                >\n                  Row ${virtualRow.index}\n                </div>`,\n            )}\n          </div>\n        </div>\n      </div>\n      <style>\n        .list {\n          border: 1px solid #e6e4dc;\n          max-width: 100%;\n        }\n\n        .list-item-even,\n        .list-item-odd {\n          display: flex;\n          align-items: center;\n          justify-content: center;\n        }\n\n        .list-item-even {\n          background-color: #e6e4dc;\n        }\n\n        .scroll-container {\n          height: 200px;\n          width: 400px;\n          overflow: auto;\n        }\n      </style>\n    `\n  }\n}\n\n@customElement('column-virtualizer-fixed')\nclass ColumnVirtualizerFixed extends LitElement {\n  private scrollElementRef: Ref<HTMLDivElement> = createRef()\n\n  private virtualizerController: VirtualizerController<HTMLDivElement, Element>\n\n  constructor() {\n    super()\n    this.virtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count: sentences.length,\n      estimateSize: () => 100,\n      horizontal: true,\n    })\n  }\n\n  render() {\n    const virtualizer = this.virtualizerController.getVirtualizer()\n    const virtualColumns = virtualizer.getVirtualItems()\n\n    return html`\n      <div>\n        <div class=\"list scroll-container\" ${ref(this.scrollElementRef)}>\n          <div\n            style=\"position: relative; height: 100%; width: ${virtualizer.getTotalSize()}px;\"\n          >\n            ${repeat(\n              virtualColumns,\n              (virtualColumn) => virtualColumn.key,\n              (virtualColumn) =>\n                html` <div\n                  class=\"${virtualColumn.index % 2 === 0\n                    ? 'list-item-even'\n                    : 'list-item-odd'}\"\n                  style=\"position: absolute; left: 0; top: 0; height: 100%; width: ${virtualColumn.size}px; transform: translateX(${virtualColumn.start}px)\"\n                >\n                  Column ${virtualColumn.index}\n                </div>`,\n            )}\n          </div>\n        </div>\n      </div>\n      <style>\n        .list {\n          border: 1px solid #e6e4dc;\n          max-width: 100%;\n        }\n\n        .list-item-even,\n        .list-item-odd {\n          display: flex;\n          align-items: center;\n          justify-content: center;\n        }\n\n        .list-item-even {\n          background-color: #e6e4dc;\n        }\n\n        .scroll-container {\n          height: 100px;\n          width: 400px;\n          overflow: auto;\n        }\n      </style>\n    `\n  }\n}\n\n@customElement('grid-virtualizer-fixed')\nclass GridVirtualizerFixed extends LitElement {\n  private scrollElementRef: Ref<HTMLDivElement> = createRef()\n\n  private rowVirtualizerController: VirtualizerController<\n    HTMLDivElement,\n    Element\n  >\n  private columnVirtualizerController: VirtualizerController<\n    HTMLDivElement,\n    Element\n  >\n\n  constructor() {\n    super()\n    this.rowVirtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count: sentences.length,\n      estimateSize: () => 35,\n      overscan: 5,\n    })\n\n    this.columnVirtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count: sentences.length,\n      estimateSize: () => 100,\n      horizontal: true,\n      overscan: 5,\n    })\n  }\n\n  render() {\n    const rowVirtualizer = this.rowVirtualizerController.getVirtualizer()\n    const columnVirtualizer = this.columnVirtualizerController.getVirtualizer()\n\n    return html`\n      <div>\n        <div class=\"list scroll-container\" ${ref(this.scrollElementRef)}>\n          <div\n            style=\"position: relative; height: ${rowVirtualizer.getTotalSize()}px; width: ${columnVirtualizer.getTotalSize()}px;\"\n          >\n            ${repeat(\n              rowVirtualizer.getVirtualItems(),\n              (virtualRow) => virtualRow.key,\n              (virtualRow) =>\n                repeat(\n                  columnVirtualizer.getVirtualItems(),\n                  (virtualColumn) => virtualColumn.key,\n                  (virtualColumn) => html`\n                    <div\n                      class=\"${virtualColumn.index % 2\n                        ? virtualRow.index % 2 === 0\n                          ? 'list-item-odd'\n                          : 'list-item-even'\n                        : virtualRow.index % 2\n                          ? 'list-item-odd'\n                          : 'list-item-even'}\"\n                      style=\"position: absolute;left: 0; top: 0; width: ${virtualColumn.size}px; height: ${virtualRow.size}px; transform: translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)\"\n                    >\n                      Cell ${virtualRow.index}, ${virtualColumn.index}\n                    </div>\n                  `,\n                ),\n            )}\n          </div>\n        </div>\n      </div>\n      <style>\n        .list {\n          border: 1px solid #e6e4dc;\n          max-width: 100%;\n        }\n\n        .list-item-even,\n        .list-item-odd {\n          display: flex;\n          align-items: center;\n          justify-content: center;\n        }\n\n        .list-item-even {\n          background-color: #e6e4dc;\n        }\n\n        .scroll-container {\n          height: 500px;\n          width: 500px;\n          overflow: auto;\n        }\n      </style>\n    `\n  }\n}\n\n@customElement('my-app')\nexport class MyApp extends LitElement {\n  protected render() {\n    const { pathname } = window.location\n\n    return html`\n      <div>\n        <p>\n          These components are using <strong>fixed</strong> sizes. This means\n          that every element's dimensions are hard-coded to the same value and\n          never change.\n        </p>\n        <br />\n        <br />\n\n        <h3>Rows</h3>\n        <row-virtualizer-fixed></row-virtualizer-fixed>\n        <br />\n        <br />\n        <h3>Columns</h3>\n        <column-virtualizer-fixed></column-virtualizer-fixed>\n        <br />\n        <br />\n        <h3>Grid</h3>\n        <grid-virtualizer-fixed></grid-virtualizer-fixed>\n        <br />\n        <br />\n      </div>\n    `\n  }\n}\n"
  },
  {
    "path": "examples/lit/fixed/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"outDir\": \"./build/types\",\n    \"target\": \"ESNext\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"node\",\n    \"experimentalDecorators\": true,\n    \"useDefineForClassFields\": false\n  },\n  \"files\": [\"src/main.ts\"],\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/lit/fixed/vite.config.js",
    "content": "import { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [],\n})\n"
  },
  {
    "path": "examples/react/dynamic/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/dynamic/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `npm`\n- `npm run start` or `npm run start`\n"
  },
  {
    "path": "examples/react/dynamic/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/dynamic/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-dynamic\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^24.5.2\",\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/dynamic/src/index.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n"
  },
  {
    "path": "examples/react/dynamic/src/main.tsx",
    "content": "import * as React from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { faker } from '@faker-js/faker'\n\nimport { useVirtualizer, useWindowVirtualizer } from '@tanstack/react-virtual'\n\nimport './index.css'\n\nconst randomNumber = (min: number, max: number) =>\n  faker.number.int({ min, max })\n\nconst sentences = new Array(10000)\n  .fill(true)\n  .map(() => faker.lorem.sentence(randomNumber(20, 70)))\n\nfunction RowVirtualizerDynamic() {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const [enabled, setEnabled] = React.useState(true)\n\n  const count = sentences.length\n  const virtualizer = useVirtualizer({\n    count,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 45,\n    enabled,\n  })\n\n  React.useEffect(() => {\n    virtualizer.scrollToIndex(count - 1, { align: 'end' })\n  }, [])\n\n  const items = virtualizer.getVirtualItems()\n\n  return (\n    <div>\n      <button\n        onClick={() => {\n          virtualizer.scrollToIndex(0)\n        }}\n      >\n        scroll to the top\n      </button>\n      <span style={{ padding: '0 4px' }} />\n      <button\n        onClick={() => {\n          virtualizer.scrollToIndex(count / 2, { behavior: 'smooth' })\n        }}\n      >\n        scroll to the middle\n      </button>\n      <span style={{ padding: '0 4px' }} />\n      <button\n        onClick={() => {\n          virtualizer.scrollToIndex(count - 1)\n        }}\n      >\n        scroll to the end\n      </button>\n      <span style={{ padding: '0 4px' }} />\n      <button\n        onClick={() => {\n          setEnabled((prev) => !prev)\n        }}\n      >\n        turn {enabled ? 'off' : 'on'} virtualizer\n      </button>\n      <hr />\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: 400,\n          width: 400,\n          overflowY: 'auto',\n          contain: 'strict',\n          overflowAnchor: 'none',\n        }}\n      >\n        <div\n          style={{\n            height: virtualizer.getTotalSize(),\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          <div\n            style={{\n              position: 'absolute',\n              top: 0,\n              left: 0,\n              width: '100%',\n              transform: `translateY(${items[0]?.start ?? 0}px)`,\n            }}\n          >\n            {items.map((virtualRow) => (\n              <div\n                key={virtualRow.key}\n                data-index={virtualRow.index}\n                ref={virtualizer.measureElement}\n                className={\n                  virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n                }\n              >\n                <div style={{ padding: '10px 0' }}>\n                  <div>Row {virtualRow.index}</div>\n                  <div>{sentences[virtualRow.index]}</div>\n                </div>\n              </div>\n            ))}\n          </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n\nfunction ColumnVirtualizerDynamic() {\n  const parentRef = React.useRef<HTMLDivElement | null>(null)\n\n  const virtualizer = useVirtualizer({\n    horizontal: true,\n    count: sentences.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 45,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{ width: 400, height: 400, overflowY: 'auto' }}\n      >\n        <div\n          style={{\n            width: virtualizer.getTotalSize(),\n            height: '100%',\n            position: 'relative',\n          }}\n        >\n          {virtualizer.getVirtualItems().map((virtualColumn) => (\n            <div\n              key={virtualColumn.key}\n              data-index={virtualColumn.index}\n              ref={virtualizer.measureElement}\n              className={\n                virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n              }\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                height: '100%',\n                transform: `translateX(${virtualColumn.start}px)`,\n              }}\n            >\n              <div style={{ width: sentences[virtualColumn.index].length }}>\n                <div>Column {virtualColumn.index}</div>\n                <div>{sentences[virtualColumn.index]}</div>\n              </div>\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\ninterface Column {\n  key: string\n  name: string\n  width: number\n}\n\nfunction GridVirtualizerDynamic({\n  columns,\n  data,\n}: {\n  data: Array<Array<string>>\n  columns: Array<Column>\n}) {\n  const parentRef = React.useRef<HTMLDivElement | null>(null)\n\n  const parentOffsetRef = React.useRef(0)\n\n  React.useLayoutEffect(() => {\n    parentOffsetRef.current = parentRef.current?.offsetTop ?? 0\n  }, [])\n\n  const getColumnWidth = (index: number) => columns[index].width\n\n  const virtualizer = useWindowVirtualizer({\n    count: data.length,\n    estimateSize: () => 350,\n    overscan: 5,\n    scrollMargin: parentOffsetRef.current,\n  })\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: columns.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: getColumnWidth,\n    overscan: 5,\n  })\n  const columnItems = columnVirtualizer.getVirtualItems()\n  const [before, after] =\n    columnItems.length > 0\n      ? [\n          columnItems[0].start,\n          columnVirtualizer.getTotalSize() -\n            columnItems[columnItems.length - 1].end,\n        ]\n      : [0, 0]\n\n  return (\n    <div\n      ref={parentRef}\n      style={{ overflowY: 'auto', border: '1px solid #c8c8c8' }}\n    >\n      <div\n        style={{\n          height: virtualizer.getTotalSize(),\n          position: 'relative',\n        }}\n      >\n        {virtualizer.getVirtualItems().map((row) => {\n          return (\n            <div\n              key={row.key}\n              data-index={row.index}\n              ref={virtualizer.measureElement}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                transform: `translateY(${\n                  row.start - virtualizer.options.scrollMargin\n                }px)`,\n                display: 'flex',\n              }}\n            >\n              <div style={{ width: `${before}px` }} />\n              {columnItems.map((column) => {\n                return (\n                  <div\n                    key={column.key}\n                    style={{\n                      minHeight: row.index === 0 ? 50 : row.size,\n                      width: getColumnWidth(column.index),\n                      borderBottom: '1px solid #c8c8c8',\n                      borderRight: '1px solid #c8c8c8',\n                      padding: '7px 12px',\n                    }}\n                  >\n                    {row.index === 0 ? (\n                      <div>{columns[column.index].name}</div>\n                    ) : (\n                      <div>{data[row.index][column.index]}</div>\n                    )}\n                  </div>\n                )\n              })}\n              <div style={{ width: `${after}px` }} />\n            </div>\n          )\n        })}\n      </div>\n    </div>\n  )\n}\n\nconst generateColumns = (count: number) => {\n  return new Array(count).fill(0).map((_, i) => {\n    const key: string = i.toString()\n    return {\n      key,\n      name: `Column ${i}`,\n      width: randomNumber(75, 300),\n    }\n  })\n}\n\nconst generateData = (columns: Array<Column>, count = 300) => {\n  return new Array(count).fill(0).map((_, rowIndex) =>\n    columns.reduce<Array<string>>((acc, _curr, colIndex) => {\n      // simulate dynamic size cells\n      const val = faker.lorem.lines(((rowIndex + colIndex) % 10) + 1)\n\n      acc.push(val)\n\n      return acc\n    }, []),\n  )\n}\n\nfunction RowVirtualizerExperimental() {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n  const innerRef = React.useRef<HTMLDivElement>(null)\n  const rowRefsMap = React.useRef(new Map<number, HTMLDivElement>())\n\n  const [enabled, setEnabled] = React.useState(true)\n\n  const count = sentences.length\n  const virtualizer = useVirtualizer({\n    count,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 45,\n    enabled,\n    onChange: (instance) => {\n      innerRef.current!.style.height = `${instance.getTotalSize()}px`\n      instance.getVirtualItems().forEach((virtualRow) => {\n        const rowRef = rowRefsMap.current.get(virtualRow.index)\n        if (!rowRef) return\n        rowRef.style.transform = `translateY(${virtualRow.start}px)`\n      })\n    },\n  })\n\n  const indexes = virtualizer.getVirtualIndexes()\n\n  React.useEffect(() => {\n    virtualizer.measure()\n  }, [])\n\n  return (\n    <div>\n      <button\n        onClick={() => {\n          virtualizer.scrollToIndex(0)\n        }}\n      >\n        scroll to the top\n      </button>\n      <span style={{ padding: '0 4px' }} />\n      <button\n        onClick={() => {\n          virtualizer.scrollToIndex(count / 2)\n        }}\n      >\n        scroll to the middle\n      </button>\n      <span style={{ padding: '0 4px' }} />\n      <button\n        onClick={() => {\n          virtualizer.scrollToIndex(count - 1)\n        }}\n      >\n        scroll to the end\n      </button>\n      <span style={{ padding: '0 4px' }} />\n      <button\n        onClick={() => {\n          setEnabled((prev) => !prev)\n        }}\n      >\n        turn {enabled ? 'off' : 'on'} virtualizer\n      </button>\n      <hr />\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: 400,\n          width: 400,\n          overflowY: 'auto',\n          contain: 'strict',\n        }}\n      >\n        <div\n          ref={innerRef}\n          style={{\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {indexes.map((index) => (\n            <div\n              key={index}\n              data-index={index}\n              ref={(el) => {\n                if (el) {\n                  virtualizer.measureElement(el)\n                  rowRefsMap.current.set(index, el)\n                }\n              }}\n              className={index % 2 ? 'ListItemOdd' : 'ListItemEven'}\n            >\n              <div style={{ padding: '10px 0' }}>\n                <div>Row {index}</div>\n                <div>{sentences[index]}</div>\n              </div>\n            </div>\n          ))}\n        </div>\n      </div>\n    </div>\n  )\n}\n\nfunction App() {\n  const pathname = location.pathname\n  return (\n    <div>\n      <p>\n        These components are using <strong>dynamic</strong> sizes. This means\n        that each element's exact dimensions are unknown when rendered. An\n        estimated dimension is used as the initial measurement, then this\n        measurement is readjusted on the fly as each element is rendered.\n      </p>\n      <nav>\n        <ul>\n          <li>\n            <a href=\"/\">List</a>\n          </li>\n          <li>\n            <a href=\"/columns\">Column</a>\n          </li>\n          <li>\n            <a href=\"/grid\">Grid</a>\n          </li>\n          <li>\n            <a href=\"/experimental\">Experimental</a>\n          </li>\n        </ul>\n      </nav>\n      {(() => {\n        switch (pathname) {\n          case '/':\n            return <RowVirtualizerDynamic />\n          case '/columns':\n            return <ColumnVirtualizerDynamic />\n          case '/grid': {\n            const columns = generateColumns(30)\n            const data = generateData(columns)\n            return <GridVirtualizerDynamic columns={columns} data={data} />\n          }\n          case '/experimental':\n            return <RowVirtualizerExperimental />\n          default:\n            return <div>Not found</div>\n        }\n      })()}\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nconst container = document.getElementById('root')!\nconst root = createRoot(container)\nconst { StrictMode } = React\n\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/react/dynamic/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/dynamic/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/fixed/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/fixed/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/fixed/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/fixed/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-fixed\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^24.5.2\",\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/fixed/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/react/fixed/src/main.tsx",
    "content": "import * as React from 'react'\nimport * as ReactDOM from 'react-dom/client'\n\nimport './index.css'\n\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nfunction App() {\n  return (\n    <div>\n      <p>\n        These components are using <strong>fixed</strong> sizes. This means that\n        every element's dimensions are hard-coded to the same value and never\n        change.\n      </p>\n      <br />\n      <br />\n\n      <h3>Rows</h3>\n      <RowVirtualizerFixed />\n      <br />\n      <br />\n      <h3>Columns</h3>\n      <ColumnVirtualizerFixed />\n      <br />\n      <br />\n      <h3>Grid</h3>\n      <GridVirtualizerFixed />\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nfunction RowVirtualizerFixed() {\n  const parentRef = React.useRef(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: 10000,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 35,\n    overscan: 5,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `200px`,\n          width: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <div\n              key={virtualRow.index}\n              className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: `${virtualRow.size}px`,\n                transform: `translateY(${virtualRow.start}px)`,\n              }}\n            >\n              Row {virtualRow.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction ColumnVirtualizerFixed() {\n  const parentRef = React.useRef(null)\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: 10000,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 100,\n    overscan: 5,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          width: `400px`,\n          height: `100px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            width: `${columnVirtualizer.getTotalSize()}px`,\n            height: '100%',\n            position: 'relative',\n          }}\n        >\n          {columnVirtualizer.getVirtualItems().map((virtualColumn) => (\n            <div\n              key={virtualColumn.index}\n              className={\n                virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n              }\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                height: '100%',\n                width: `${virtualColumn.size}px`,\n                transform: `translateX(${virtualColumn.start}px)`,\n              }}\n            >\n              Column {virtualColumn.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction GridVirtualizerFixed() {\n  const parentRef = React.useRef(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: 10000,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 35,\n    overscan: 5,\n  })\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: 10000,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 100,\n    overscan: 5,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `500px`,\n          width: `500px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: `${columnVirtualizer.getTotalSize()}px`,\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <React.Fragment key={virtualRow.key}>\n              {columnVirtualizer.getVirtualItems().map((virtualColumn) => (\n                <div\n                  key={virtualColumn.key}\n                  className={\n                    virtualColumn.index % 2\n                      ? virtualRow.index % 2 === 0\n                        ? 'ListItemOdd'\n                        : 'ListItemEven'\n                      : virtualRow.index % 2\n                        ? 'ListItemOdd'\n                        : 'ListItemEven'\n                  }\n                  style={{\n                    position: 'absolute',\n                    top: 0,\n                    left: 0,\n                    width: `${virtualColumn.size}px`,\n                    height: `${virtualRow.size}px`,\n                    transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,\n                  }}\n                >\n                  Cell {virtualRow.index}, {virtualColumn.index}\n                </div>\n              ))}\n            </React.Fragment>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\n// eslint-disable-next-line\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n)\n"
  },
  {
    "path": "examples/react/fixed/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/fixed/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/infinite-scroll/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/infinite-scroll/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/infinite-scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n    <script type=\"module\" src=\"https://cdn.skypack.dev/twind/shim\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/infinite-scroll/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-infinite-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview --port 3001\",\n    \"start\": \"vite\"\n  },\n  \"dependencies\": {\n    \"@tanstack/react-query\": \"^5.80.7\",\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/infinite-scroll/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/react/infinite-scroll/src/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom'\nimport {\n  QueryClient,\n  QueryClientProvider,\n  useInfiniteQuery,\n} from '@tanstack/react-query'\n\nimport './index.css'\n\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nconst queryClient = new QueryClient()\n\nasync function fetchServerPage(\n  limit: number,\n  offset: number = 0,\n): Promise<{ rows: Array<string>; nextOffset: number }> {\n  const rows = new Array(limit)\n    .fill(0)\n    .map((_, i) => `Async loaded row #${i + offset * limit}`)\n\n  await new Promise((r) => setTimeout(r, 500))\n\n  return { rows, nextOffset: offset + 1 }\n}\n\nfunction App() {\n  const {\n    status,\n    data,\n    error,\n    isFetching,\n    isFetchingNextPage,\n    fetchNextPage,\n    hasNextPage,\n  } = useInfiniteQuery({\n    queryKey: ['projects'],\n    queryFn: (ctx) => fetchServerPage(10, ctx.pageParam),\n    getNextPageParam: (lastGroup) => lastGroup.nextOffset,\n    initialPageParam: 0,\n  })\n\n  const allRows = data ? data.pages.flatMap((d) => d.rows) : []\n\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: hasNextPage ? allRows.length + 1 : allRows.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 100,\n    overscan: 5,\n  })\n\n  React.useEffect(() => {\n    const [lastItem] = [...rowVirtualizer.getVirtualItems()].reverse()\n\n    if (!lastItem) {\n      return\n    }\n\n    if (\n      lastItem.index >= allRows.length - 1 &&\n      hasNextPage &&\n      !isFetchingNextPage\n    ) {\n      fetchNextPage()\n    }\n  }, [\n    hasNextPage,\n    fetchNextPage,\n    allRows.length,\n    isFetchingNextPage,\n    rowVirtualizer.getVirtualItems(),\n  ])\n\n  return (\n    <div>\n      <p>\n        This infinite scroll example uses React Query's useInfiniteScroll hook\n        to fetch infinite data from a posts endpoint and then a rowVirtualizer\n        is used along with a loader-row placed at the bottom of the list to\n        trigger the next page to load.\n      </p>\n\n      <br />\n      <br />\n\n      {status === 'pending' ? (\n        <p>Loading...</p>\n      ) : status === 'error' ? (\n        <span>Error: {error.message}</span>\n      ) : (\n        <div\n          ref={parentRef}\n          className=\"List\"\n          style={{\n            height: `500px`,\n            width: `100%`,\n            overflow: 'auto',\n          }}\n        >\n          <div\n            style={{\n              height: `${rowVirtualizer.getTotalSize()}px`,\n              width: '100%',\n              position: 'relative',\n            }}\n          >\n            {rowVirtualizer.getVirtualItems().map((virtualRow) => {\n              const isLoaderRow = virtualRow.index > allRows.length - 1\n              const post = allRows[virtualRow.index]\n\n              return (\n                <div\n                  key={virtualRow.index}\n                  className={\n                    virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n                  }\n                  style={{\n                    position: 'absolute',\n                    top: 0,\n                    left: 0,\n                    width: '100%',\n                    height: `${virtualRow.size}px`,\n                    transform: `translateY(${virtualRow.start}px)`,\n                  }}\n                >\n                  {isLoaderRow\n                    ? hasNextPage\n                      ? 'Loading more...'\n                      : 'Nothing more to load'\n                    : post}\n                </div>\n              )\n            })}\n          </div>\n        </div>\n      )}\n      <div>\n        {isFetching && !isFetchingNextPage ? 'Background Updating...' : null}\n      </div>\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nReactDOM.render(\n  <React.StrictMode>\n    <QueryClientProvider client={queryClient}>\n      <App />\n    </QueryClientProvider>\n  </React.StrictMode>,\n  document.getElementById('root'),\n)\n"
  },
  {
    "path": "examples/react/infinite-scroll/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/infinite-scroll/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/padding/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/padding/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/padding/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n    <script type=\"module\" src=\"https://cdn.skypack.dev/twind/shim\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/padding/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-padding\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview --port 3001\",\n    \"start\": \"vite\"\n  },\n  \"dependencies\": {\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/padding/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/react/padding/src/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom'\n\nimport './index.css'\n\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nconst rows = new Array(10000)\n  .fill(true)\n  .map(() => 25 + Math.round(Math.random() * 100))\n\nconst columns = new Array(10000)\n  .fill(true)\n  .map(() => 75 + Math.round(Math.random() * 100))\n\nfunction App() {\n  return (\n    <div>\n      <p>\n        These components are using <strong>dynamic</strong> sizes. This means\n        that each element's exact dimensions are unknown when rendered. An\n        estimated dimension is used as the initial measurement, then this\n        measurement is readjusted on the fly as each element is rendered.\n      </p>\n      <br />\n      <br />\n\n      <h3>Rows</h3>\n      <RowVirtualizerDynamic rows={rows} />\n      <br />\n      <br />\n      <h3>Columns</h3>\n      <ColumnVirtualizerDynamic columns={columns} />\n      <br />\n      <br />\n      <h3>Grid</h3>\n      <GridVirtualizerDynamic rows={rows} columns={columns} />\n    </div>\n  )\n}\n\nfunction RowVirtualizerDynamic({ rows }: { rows: Array<number> }) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: rows.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n    paddingStart: 100,\n    paddingEnd: 100,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `200px`,\n          width: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <div\n              key={virtualRow.key}\n              data-index={virtualRow.index}\n              ref={rowVirtualizer.measureElement}\n              className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: `${rows[virtualRow.index]}px`,\n                transform: `translateY(${virtualRow.start}px)`,\n              }}\n            >\n              Row {virtualRow.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction ColumnVirtualizerDynamic({ columns }: { columns: Array<number> }) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: columns.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n    paddingStart: 100,\n    paddingEnd: 100,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          width: `400px`,\n          height: `100px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            width: `${columnVirtualizer.getTotalSize()}px`,\n            height: '100%',\n            position: 'relative',\n          }}\n        >\n          {columnVirtualizer.getVirtualItems().map((virtualColumn) => (\n            <div\n              key={virtualColumn.key}\n              data-index={virtualColumn.index}\n              ref={columnVirtualizer.measureElement}\n              className={\n                virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n              }\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                height: '100%',\n                width: `${columns[virtualColumn.index]}px`,\n                transform: `translateX(${virtualColumn.start}px)`,\n              }}\n            >\n              Column {virtualColumn.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction GridVirtualizerDynamic({\n  rows,\n  columns,\n}: {\n  rows: Array<number>\n  columns: Array<number>\n}) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: rows.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n    paddingStart: 200,\n    paddingEnd: 200,\n    indexAttribute: 'data-row-index',\n  })\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: columns.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n    paddingStart: 200,\n    paddingEnd: 200,\n    indexAttribute: 'data-column-index',\n  })\n\n  const [show, setShow] = React.useState(true)\n\n  const halfWay = Math.floor(rows.length / 2)\n\n  return (\n    <>\n      <button onClick={() => setShow((old) => !old)}>Toggle</button>\n      <button onClick={() => rowVirtualizer.scrollToIndex(halfWay)}>\n        Scroll to index {halfWay}\n      </button>\n      <button onClick={() => rowVirtualizer.scrollToIndex(rows.length - 1)}>\n        Scroll to index {rows.length - 1}\n      </button>\n      {show ? (\n        <div\n          ref={parentRef}\n          className=\"List\"\n          style={{\n            height: `400px`,\n            width: `500px`,\n            overflow: 'auto',\n          }}\n        >\n          <div\n            style={{\n              height: `${rowVirtualizer.getTotalSize()}px`,\n              width: `${columnVirtualizer.getTotalSize()}px`,\n              position: 'relative',\n            }}\n          >\n            {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n              <React.Fragment key={virtualRow.key}>\n                {columnVirtualizer.getVirtualItems().map((virtualColumn) => (\n                  <div\n                    key={virtualColumn.key}\n                    data-row-index={virtualRow.index}\n                    data-column-index={virtualColumn.index}\n                    ref={(el) => {\n                      rowVirtualizer.measureElement(el)\n                      columnVirtualizer.measureElement(el)\n                    }}\n                    className={\n                      virtualColumn.index % 2\n                        ? virtualRow.index % 2 === 0\n                          ? 'ListItemOdd'\n                          : 'ListItemEven'\n                        : virtualRow.index % 2\n                          ? 'ListItemOdd'\n                          : 'ListItemEven'\n                    }\n                    style={{\n                      position: 'absolute',\n                      top: 0,\n                      left: 0,\n                      width: `${columns[virtualColumn.index]}px`,\n                      height: `${rows[virtualRow.index]}px`,\n                      transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,\n                    }}\n                  >\n                    Cell {virtualRow.index}, {virtualColumn.index}\n                  </div>\n                ))}\n              </React.Fragment>\n            ))}\n          </div>\n        </div>\n      ) : null}\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </>\n  )\n}\n\nReactDOM.render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n  document.getElementById('root'),\n)\n"
  },
  {
    "path": "examples/react/padding/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/padding/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/scroll-padding/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/scroll-padding/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/scroll-padding/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n    <script type=\"module\" src=\"https://cdn.skypack.dev/twind/shim\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/scroll-padding/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-scroll-padding\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview --port 3001\",\n    \"start\": \"vite\"\n  },\n  \"dependencies\": {\n    \"@react-hookz/web\": \"^25.1.1\",\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/scroll-padding/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\n.List table {\n  background-color: #fff;\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n  border-collapse: collapse;\n\n  display: flex;\n  flex-direction: column;\n  align-items: stretch;\n  position: relative;\n}\n\n.List thead {\n  display: flex;\n  flex-direction: column;\n  background-color: #fff;\n\n  position: sticky;\n  top: 0;\n  z-index: 1;\n}\n\n.List thead tr {\n  height: 70px;\n}\n\n.List tr {\n  display: flex;\n  flex-direction: row;\n}\n\n.List td,\n.List th {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  justify-content: center;\n  width: 180px;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n"
  },
  {
    "path": "examples/react/scroll-padding/src/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom'\n\nimport './index.css'\n\nimport { useMeasure } from '@react-hookz/web'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nfunction App() {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n  const [theadSize, theadRef] = useMeasure<HTMLTableSectionElement>()\n\n  const rowVirtualizer = useVirtualizer({\n    count: 10000,\n    getScrollElement: () => parentRef.current,\n    estimateSize: React.useCallback(() => 35, []),\n    overscan: 5,\n    paddingStart: theadSize?.height ?? 0,\n    scrollPaddingStart: theadSize?.height ?? 0,\n  })\n\n  return (\n    <>\n      <div className=\"flex gap-2\">\n        <button\n          onClick={() => {\n            rowVirtualizer.scrollToIndex(40)\n          }}\n          className=\"border border-black\"\n        >\n          Scroll to index 40\n        </button>\n        <button\n          onClick={() => {\n            rowVirtualizer.scrollToIndex(20)\n          }}\n          className=\"border border-black\"\n        >\n          Then scroll to index 20\n        </button>\n      </div>\n\n      <br />\n      <br />\n\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `200px`,\n          width: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <table\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n          }}\n        >\n          <thead ref={theadRef}>\n            <tr>\n              <th>Index</th>\n              <th>Key</th>\n            </tr>\n          </thead>\n          <tbody>\n            {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n              <tr\n                key={virtualRow.index}\n                className={\n                  virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n                }\n                style={{\n                  position: 'absolute',\n                  top: 0,\n                  left: 0,\n                  width: '100%',\n                  height: `${virtualRow.size}px`,\n                  transform: `translateY(${virtualRow.start}px)`,\n                }}\n              >\n                <td>#{virtualRow.index}</td>\n                <td>{virtualRow.key}</td>\n              </tr>\n            ))}\n          </tbody>\n        </table>\n      </div>\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </>\n  )\n}\n\nReactDOM.render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n  document.getElementById('root'),\n)\n"
  },
  {
    "path": "examples/react/scroll-padding/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/scroll-padding/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/smooth-scroll/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/smooth-scroll/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/smooth-scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n    <script type=\"module\" src=\"https://cdn.skypack.dev/twind/shim\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/smooth-scroll/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-smooth-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview --port 3001\",\n    \"start\": \"vite\"\n  },\n  \"dependencies\": {\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/smooth-scroll/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/react/smooth-scroll/src/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom'\n\nimport './index.css'\n\nimport { elementScroll, useVirtualizer } from '@tanstack/react-virtual'\nimport type { VirtualizerOptions } from '@tanstack/react-virtual'\n\nfunction easeInOutQuint(t: number) {\n  return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t\n}\n\nfunction App() {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n  const scrollingRef = React.useRef<number>()\n\n  const scrollToFn: VirtualizerOptions<any, any>['scrollToFn'] =\n    React.useCallback((offset, canSmooth, instance) => {\n      const duration = 1000\n      const start = parentRef.current?.scrollTop || 0\n      const startTime = (scrollingRef.current = Date.now())\n\n      const run = () => {\n        if (scrollingRef.current !== startTime) return\n        const now = Date.now()\n        const elapsed = now - startTime\n        const progress = easeInOutQuint(Math.min(elapsed / duration, 1))\n        const interpolated = start + (offset - start) * progress\n\n        if (elapsed < duration) {\n          elementScroll(interpolated, canSmooth, instance)\n          requestAnimationFrame(run)\n        } else {\n          elementScroll(interpolated, canSmooth, instance)\n        }\n      }\n\n      requestAnimationFrame(run)\n    }, [])\n\n  const rowVirtualizer = useVirtualizer({\n    count: 10000,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 35,\n    overscan: 5,\n    scrollToFn,\n  })\n\n  const randomIndex = Math.floor(Math.random() * 10000)\n\n  return (\n    <div>\n      <p>\n        This smooth scroll example uses the <code>`scrollToFn`</code> to\n        implement a custom scrolling function for the methods like{' '}\n        <code>`scrollToIndex`</code> and <code>`scrollToOffset`</code>\n      </p>\n\n      <br />\n      <br />\n\n      <div>\n        <button onClick={() => rowVirtualizer.scrollToIndex(randomIndex)}>\n          Scroll To Random Index ({randomIndex})\n        </button>\n      </div>\n\n      <br />\n      <br />\n\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `200px`,\n          width: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <div\n              key={virtualRow.index}\n              className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: `${virtualRow.size}px`,\n                transform: `translateY(${virtualRow.start}px)`,\n              }}\n            >\n              Row {virtualRow.index}\n            </div>\n          ))}\n        </div>\n      </div>\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nReactDOM.render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n  document.getElementById('root'),\n)\n"
  },
  {
    "path": "examples/react/smooth-scroll/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/smooth-scroll/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/sticky/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/sticky/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/sticky/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n    <script type=\"module\" src=\"https://cdn.skypack.dev/twind/shim\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/sticky/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-sticky\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview --port 3001\",\n    \"start\": \"vite\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"lodash\": \"^4.17.21\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/lodash\": \"^4.17.17\",\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/sticky/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/react/sticky/src/main.tsx",
    "content": "import './index.css'\nimport * as React from 'react'\nimport ReactDOM from 'react-dom'\nimport { faker } from '@faker-js/faker'\nimport { findIndex, groupBy } from 'lodash'\nimport { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual'\nimport type { Range } from '@tanstack/react-virtual'\n\nconst groupedNames = groupBy(\n  Array.from({ length: 1000 })\n    .map(() => faker.person.firstName())\n    .sort(),\n  (name) => name[0],\n)\nconst groups = Object.keys(groupedNames)\nconst rows = groups.reduce<Array<string>>(\n  (acc, k) => [...acc, k, ...groupedNames[k]],\n  [],\n)\n\nconst App = () => {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const activeStickyIndexRef = React.useRef(0)\n\n  const stickyIndexes = React.useMemo(\n    () => groups.map((gn) => findIndex(rows, (n) => n === gn)),\n    [],\n  )\n\n  const isSticky = (index: number) => stickyIndexes.includes(index)\n\n  const isActiveSticky = (index: number) =>\n    activeStickyIndexRef.current === index\n\n  const rowVirtualizer = useVirtualizer({\n    count: rows.length,\n    estimateSize: () => 50,\n    getScrollElement: () => parentRef.current,\n    rangeExtractor: React.useCallback(\n      (range: Range) => {\n        activeStickyIndexRef.current =\n          [...stickyIndexes]\n            .reverse()\n            .find((index) => range.startIndex >= index) ?? 0\n\n        const next = new Set([\n          activeStickyIndexRef.current,\n          ...defaultRangeExtractor(range),\n        ])\n\n        return [...next].sort((a, b) => a - b)\n      },\n      [stickyIndexes],\n    ),\n  })\n\n  return (\n    <div>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `300px`,\n          width: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <div\n              key={virtualRow.index}\n              className={'ListItem'}\n              style={{\n                ...(isSticky(virtualRow.index)\n                  ? {\n                      background: '#fff',\n                      borderBottom: '1px solid #ddd',\n                      zIndex: 1,\n                    }\n                  : {}),\n                ...(isActiveSticky(virtualRow.index)\n                  ? {\n                      position: 'sticky',\n                    }\n                  : {\n                      position: 'absolute',\n                      transform: `translateY(${virtualRow.start}px)`,\n                    }),\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: `${virtualRow.size}px`,\n              }}\n            >\n              {rows[virtualRow.index]}\n            </div>\n          ))}\n        </div>\n      </div>\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nReactDOM.render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n  document.getElementById('root'),\n)\n"
  },
  {
    "path": "examples/react/sticky/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/sticky/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/table/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/table/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `npm`\n- `npm run start` or `npm run start`\n"
  },
  {
    "path": "examples/react/table/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/table/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-table\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview --port 3001\",\n    \"start\": \"vite\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/react-table\": \"^8.21.3\",\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/table/src/index.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.container {\n  height: 600px;\n  overflow: auto;\n}\n"
  },
  {
    "path": "examples/react/table/src/main.tsx",
    "content": "import * as React from 'react'\nimport { createRoot } from 'react-dom/client'\n\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport {\n  flexRender,\n  getCoreRowModel,\n  getSortedRowModel,\n  useReactTable,\n} from '@tanstack/react-table'\nimport { makeData } from './makeData'\nimport type { ColumnDef, Row, SortingState } from '@tanstack/react-table'\nimport type { Person } from './makeData'\nimport './index.css'\n\nfunction ReactTableVirtualized() {\n  const [sorting, setSorting] = React.useState<SortingState>([])\n\n  const columns = React.useMemo<Array<ColumnDef<Person>>>(\n    () => [\n      {\n        accessorKey: 'id',\n        header: 'ID',\n        size: 60,\n      },\n      {\n        accessorKey: 'firstName',\n        cell: (info) => info.getValue(),\n      },\n      {\n        accessorFn: (row) => row.lastName,\n        id: 'lastName',\n        cell: (info) => info.getValue(),\n        header: () => <span>Last Name</span>,\n      },\n      {\n        accessorKey: 'age',\n        header: () => 'Age',\n        size: 50,\n      },\n      {\n        accessorKey: 'visits',\n        header: () => <span>Visits</span>,\n        size: 50,\n      },\n      {\n        accessorKey: 'status',\n        header: 'Status',\n      },\n      {\n        accessorKey: 'progress',\n        header: 'Profile Progress',\n        size: 80,\n      },\n      {\n        accessorKey: 'createdAt',\n        header: 'Created At',\n        cell: (info) => info.getValue<Date>().toLocaleString(),\n      },\n    ],\n    [],\n  )\n\n  const [data, setData] = React.useState(() => makeData(50_000))\n\n  const table = useReactTable({\n    data,\n    columns,\n    state: {\n      sorting,\n    },\n    onSortingChange: setSorting,\n    getCoreRowModel: getCoreRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    debugTable: true,\n  })\n\n  const { rows } = table.getRowModel()\n\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const virtualizer = useVirtualizer({\n    count: rows.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 34,\n    overscan: 20,\n  })\n\n  return (\n    <div ref={parentRef} className=\"container\">\n      <div style={{ height: `${virtualizer.getTotalSize()}px` }}>\n        <table>\n          <thead>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <tr key={headerGroup.id}>\n                {headerGroup.headers.map((header) => {\n                  return (\n                    <th\n                      key={header.id}\n                      colSpan={header.colSpan}\n                      style={{ width: header.getSize() }}\n                    >\n                      {header.isPlaceholder ? null : (\n                        <div\n                          {...{\n                            className: header.column.getCanSort()\n                              ? 'cursor-pointer select-none'\n                              : '',\n                            onClick: header.column.getToggleSortingHandler(),\n                          }}\n                        >\n                          {flexRender(\n                            header.column.columnDef.header,\n                            header.getContext(),\n                          )}\n                          {{\n                            asc: ' 🔼',\n                            desc: ' 🔽',\n                          }[header.column.getIsSorted() as string] ?? null}\n                        </div>\n                      )}\n                    </th>\n                  )\n                })}\n              </tr>\n            ))}\n          </thead>\n          <tbody>\n            {virtualizer.getVirtualItems().map((virtualRow, index) => {\n              const row = rows[virtualRow.index]\n              return (\n                <tr\n                  key={row.id}\n                  style={{\n                    height: `${virtualRow.size}px`,\n                    transform: `translateY(${\n                      virtualRow.start - index * virtualRow.size\n                    }px)`,\n                  }}\n                >\n                  {row.getVisibleCells().map((cell) => {\n                    return (\n                      <td key={cell.id}>\n                        {flexRender(\n                          cell.column.columnDef.cell,\n                          cell.getContext(),\n                        )}\n                      </td>\n                    )\n                  })}\n                </tr>\n              )\n            })}\n          </tbody>\n        </table>\n      </div>\n    </div>\n  )\n}\n\nfunction App() {\n  return (\n    <div>\n      <p>\n        For tables, the basis for the offset of the translate css function is\n        from the row's initial position itself. Because of this, we need to\n        calculate the translateY pixel count differently and base it off the\n        index.\n      </p>\n      <ReactTableVirtualized />\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nconst container = document.getElementById('root')\nconst root = createRoot(container!)\nconst { StrictMode } = React\n\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/react/table/src/makeData.ts",
    "content": "import { faker } from '@faker-js/faker'\n\nexport type Person = {\n  id: number\n  firstName: string\n  lastName: string\n  age: number\n  visits: number\n  progress: number\n  status: 'relationship' | 'complicated' | 'single'\n  createdAt: Date\n}\n\nconst range = (len: number) => {\n  const arr: number[] = []\n  for (let i = 0; i < len; i++) {\n    arr.push(i)\n  }\n  return arr\n}\n\nconst newPerson = (index: number): Person => {\n  return {\n    id: index + 1,\n    firstName: faker.person.firstName(),\n    lastName: faker.person.lastName(),\n    age: faker.number.int(40),\n    visits: faker.number.int(1000),\n    progress: faker.number.int(100),\n    createdAt: faker.datatype.datetime({ max: new Date().getTime() }),\n    status: faker.helpers.shuffle<Person['status']>([\n      'relationship',\n      'complicated',\n      'single',\n    ])[0]!,\n  }\n}\n\nexport function makeData(...lens: number[]) {\n  const makeDataLevel = (depth = 0): Person[] => {\n    const len = lens[depth]!\n    return range(len).map((d): Person => {\n      return {\n        ...newPerson(d),\n      }\n    })\n  }\n\n  return makeDataLevel()\n}\n"
  },
  {
    "path": "examples/react/table/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/table/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/variable/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/variable/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/variable/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n    <script type=\"module\" src=\"https://cdn.skypack.dev/twind/shim\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/variable/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-variable\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview --port 3001\",\n    \"start\": \"vite\"\n  },\n  \"dependencies\": {\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/variable/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/react/variable/src/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom'\n\nimport './index.css'\n\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nconst rows = new Array(10000)\n  .fill(true)\n  .map(() => 25 + Math.round(Math.random() * 100))\n\nconst columns = new Array(10000)\n  .fill(true)\n  .map(() => 75 + Math.round(Math.random() * 100))\n\nfunction App() {\n  return (\n    <div>\n      <p>\n        These components are using <strong>variable</strong> sizes. This means\n        that each element has a unique, but knowable dimension at render time.\n      </p>\n      <br />\n      <br />\n\n      <h3>Rows</h3>\n      <RowVirtualizerVariable rows={rows} />\n      <br />\n      <br />\n      <h3>Columns</h3>\n      <ColumnVirtualizerVariable columns={columns} />\n      <br />\n      <br />\n      <h3>Grid</h3>\n      <GridVirtualizerVariable rows={rows} columns={columns} />\n      <br />\n      <br />\n      <h3>Masonry (vertical)</h3>\n      <MasonryVerticalVirtualizerVariable rows={rows} />\n      <br />\n      <br />\n      <h3>Masonry (horizontal)</h3>\n      <MasonryHorizontalVirtualizerVariable rows={rows} />\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nfunction RowVirtualizerVariable({ rows }: { rows: Array<number> }) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: rows.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: (i) => rows[i],\n    overscan: 5,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `200px`,\n          width: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <div\n              key={virtualRow.index}\n              className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: `${rows[virtualRow.index]}px`,\n                transform: `translateY(${virtualRow.start}px)`,\n              }}\n            >\n              Row {virtualRow.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction ColumnVirtualizerVariable({ columns }: { columns: Array<number> }) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: columns.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: (i) => columns[i],\n    overscan: 5,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          width: `400px`,\n          height: `100px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            width: `${columnVirtualizer.getTotalSize()}px`,\n            height: '100%',\n            position: 'relative',\n          }}\n        >\n          {columnVirtualizer.getVirtualItems().map((virtualColumn) => (\n            <div\n              key={virtualColumn.index}\n              className={\n                virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n              }\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                height: '100%',\n                width: `${columns[virtualColumn.index]}px`,\n                transform: `translateX(${virtualColumn.start}px)`,\n              }}\n            >\n              Column {virtualColumn.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction GridVirtualizerVariable({\n  rows,\n  columns,\n}: {\n  rows: Array<number>\n  columns: Array<number>\n}) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: rows.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: (i) => rows[i],\n    overscan: 5,\n  })\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: columns.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: (i) => columns[i],\n    overscan: 5,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `400px`,\n          width: `500px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: `${columnVirtualizer.getTotalSize()}px`,\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <React.Fragment key={virtualRow.index}>\n              {columnVirtualizer.getVirtualItems().map((virtualColumn) => (\n                <div\n                  key={virtualColumn.index}\n                  className={\n                    virtualColumn.index % 2\n                      ? virtualRow.index % 2 === 0\n                        ? 'ListItemOdd'\n                        : 'ListItemEven'\n                      : virtualRow.index % 2\n                        ? 'ListItemOdd'\n                        : 'ListItemEven'\n                  }\n                  style={{\n                    position: 'absolute',\n                    top: 0,\n                    left: 0,\n                    width: `${columns[virtualColumn.index]}px`,\n                    height: `${rows[virtualRow.index]}px`,\n                    transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,\n                  }}\n                >\n                  Cell {virtualRow.index}, {virtualColumn.index}\n                </div>\n              ))}\n            </React.Fragment>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction MasonryVerticalVirtualizerVariable({ rows }: { rows: Array<number> }) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: rows.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: (i) => rows[i],\n    overscan: 5,\n    lanes: 4,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          height: `200px`,\n          width: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            height: `${rowVirtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((virtualRow) => (\n            <div\n              key={virtualRow.index}\n              className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: `${virtualRow.lane * 25}%`,\n                width: '25%',\n                height: `${rows[virtualRow.index]}px`,\n                transform: `translateY(${virtualRow.start}px)`,\n              }}\n            >\n              Row {virtualRow.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction MasonryHorizontalVirtualizerVariable({\n  rows,\n}: {\n  rows: Array<number>\n}) {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: columns.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: (i) => columns[i],\n    overscan: 5,\n    lanes: 4,\n  })\n\n  return (\n    <>\n      <div\n        ref={parentRef}\n        className=\"List\"\n        style={{\n          width: `500px`,\n          height: `400px`,\n          overflow: 'auto',\n        }}\n      >\n        <div\n          style={{\n            width: `${columnVirtualizer.getTotalSize()}px`,\n            height: '100%',\n            position: 'relative',\n          }}\n        >\n          {columnVirtualizer.getVirtualItems().map((virtualColumn) => (\n            <div\n              key={virtualColumn.index}\n              className={\n                virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\n              }\n              style={{\n                position: 'absolute',\n                top: `${virtualColumn.lane * 25}%`,\n                left: 0,\n                height: '25%',\n                width: `${columns[virtualColumn.index]}px`,\n                transform: `translateX(${virtualColumn.start}px)`,\n              }}\n            >\n              Column {virtualColumn.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nReactDOM.render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n  document.getElementById('root'),\n)\n"
  },
  {
    "path": "examples/react/variable/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/variable/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/react/window/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "examples/react/window/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run start` or `yarn start`\n"
  },
  {
    "path": "examples/react/window/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react/window/package.json",
    "content": "{\n  \"name\": \"tanstack-react-virtual-example-window\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/react-virtual\": \"^3.13.23\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^24.5.2\",\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/react/window/src/index.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/react/window/src/main.tsx",
    "content": "import * as React from 'react'\nimport * as ReactDOM from 'react-dom/client'\n\nimport './index.css'\n\nimport { useWindowVirtualizer } from '@tanstack/react-virtual'\n\nfunction Example() {\n  const listRef = React.useRef<HTMLDivElement | null>(null)\n  const listOffsetRef = React.useRef(0)\n\n  React.useLayoutEffect(() => {\n    listOffsetRef.current = listRef.current?.offsetTop ?? 0\n  }, [])\n\n  const virtualizer = useWindowVirtualizer({\n    count: 10000,\n    estimateSize: () => 35,\n    overscan: 5,\n    scrollMargin: listOffsetRef.current,\n  })\n\n  return (\n    <>\n      <div ref={listRef} className=\"List\">\n        <div\n          style={{\n            height: `${virtualizer.getTotalSize()}px`,\n            width: '100%',\n            position: 'relative',\n          }}\n        >\n          {virtualizer.getVirtualItems().map((item) => (\n            <div\n              key={item.key}\n              className={item.index % 2 ? 'ListItemOdd' : 'ListItemEven'}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                width: '100%',\n                height: `${item.size}px`,\n                transform: `translateY(${\n                  item.start - virtualizer.options.scrollMargin\n                }px)`,\n              }}\n            >\n              Row {item.index}\n            </div>\n          ))}\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction App() {\n  return (\n    <div>\n      <p>\n        In many cases, when implementing a virtualizer with a window as the\n        scrolling element, developers often find the need to specify a\n        \"scrollMargin.\" The scroll margin is a crucial setting that defines the\n        space or gap between the start of the page and the edges of the list.\n      </p>\n      <br />\n      <br />\n      <h3>Window scroller</h3>\n      <Example />\n      <br />\n      <br />\n      {process.env.NODE_ENV === 'development' ? (\n        <p>\n          <strong>Notice:</strong> You are currently running React in\n          development mode. Rendering performance will be slightly degraded\n          until this application is built for production.\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n)\n"
  },
  {
    "path": "examples/react/window/tsconfig.json",
    "content": "{\n  \"composite\": true,\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"Bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/react/window/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/svelte/dynamic/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/svelte/dynamic/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/svelte/dynamic/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/svelte/dynamic/package.json",
    "content": "{\n  \"name\": \"tanstack-svelte-virtual-example-dynamic\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"check\": \"svelte-check --tsconfig ./tsconfig.json\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/svelte-virtual\": \"^3.13.23\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"@tsconfig/svelte\": \"^5.0.4\",\n    \"svelte\": \"^4.2.20\",\n    \"svelte-check\": \"^4.2.1\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/svelte/dynamic/src/App.svelte",
    "content": "<script lang=\"ts\">\n  import RowVirtualizerDynamic from './RowVirtualizerDynamic.svelte'\n  import RowVirtualizerDynamicWindow from './RowVirtualizerDynamicWindow.svelte'\n  import ColumnVirtualizerDynamic from './ColumnVirtualizerDynamic.svelte'\n  import GridVirtualizerDynamic from './GridVirtualizerDynamic.svelte'\n\n  const pathname = window.location.pathname\n</script>\n\n<main>\n  <p>\n    These components are using <strong>dynamic</strong> sizes. This means that each\n    element's exact dimensions are unknown when rendered. An estimated dimension is\n    used as the initial measurement, then this measurement is readjusted on the fly\n    as each element is rendered.\n  </p>\n  <nav>\n    <ul>\n      <li>\n        <a href=\"/\">List</a>\n      </li>\n      <li>\n        <a href=\"/window-list\">List - window as scroller</a>\n      </li>\n      <li>\n        <a href=\"/columns\">Column</a>\n      </li>\n      <li>\n        <a href=\"/grid\">Grid</a>\n      </li>\n    </ul>\n  </nav>\n  {#if pathname === '/'}\n    <RowVirtualizerDynamic />\n  {:else if pathname === '/window-list'}\n    <RowVirtualizerDynamicWindow />\n  {:else if pathname === '/columns'}\n    <ColumnVirtualizerDynamic />\n  {:else if pathname === '/grid'}\n    <GridVirtualizerDynamic />\n  {:else}\n    <p>Not Found</p>\n  {/if}\n</main>\n"
  },
  {
    "path": "examples/svelte/dynamic/src/ColumnVirtualizerDynamic.svelte",
    "content": "<script lang=\"ts\">\n  import { faker } from '@faker-js/faker'\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n  let virtualItemEls: HTMLDivElement[] = []\n\n  function randomNumber(min: number, max: number) {\n    return faker.number.int({ min, max })\n  }\n\n  const sentences = new Array(10000)\n    .fill(true)\n    .map(() => faker.lorem.sentence(randomNumber(20, 70)))\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    horizontal: true,\n    count: sentences.length,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 45,\n  })\n\n  $: {\n    if (virtualItemEls.length)\n      virtualItemEls.forEach((el) => $virtualizer.measureElement(el))\n  }\n</script>\n\n<div class=\"list scroll-container\" bind:this={virtualListEl}>\n  <div\n    style=\"position: relative; height: 100%; width: {$virtualizer.getTotalSize()}px;\"\n  >\n    {#each $virtualizer.getVirtualItems() as col, idx (col.index)}\n      <div\n        bind:this={virtualItemEls[idx]}\n        data-index={col.index}\n        class:list-item-even={col.index % 2 === 0}\n        class:list-item-odd={col.index % 2 === 1}\n        style=\"position: absolute; top: 0; left: 0; height: 100%; transform: translateX({col.start}px);\"\n      >\n        <div style=\"width: {sentences[col.index].length}px\">\n          <div>Column {col.index}</div>\n          <div>{sentences[col.index]}</div>\n        </div>\n      </div>\n    {/each}\n  </div>\n</div>\n\n<style>\n  .scroll-container {\n    height: 400px;\n    width: 400px;\n    overflow: auto;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/dynamic/src/GridVirtualizerDynamic.svelte",
    "content": "<script lang=\"ts\">\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n\n  $: rowVirtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    count: 10000,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 35,\n    overscan: 5,\n  })\n\n  $: columnVirtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    horizontal: true,\n    count: 10000,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 100,\n    overscan: 5,\n  })\n</script>\n\n<div class=\"list scroll-container\" bind:this={virtualListEl}>\n  <div\n    style=\"position: relative; height: {$rowVirtualizer.getTotalSize()}px; width: {$columnVirtualizer.getTotalSize()}px;\"\n  >\n    {#each $rowVirtualizer.getVirtualItems() as row (row.index)}\n      {#each $columnVirtualizer.getVirtualItems() as col (col.index)}\n        <div\n          class={col.index % 2\n            ? row.index % 2 === 0\n              ? 'list-item-odd'\n              : 'list-item-even'\n            : row.index % 2\n              ? 'list-item-odd'\n              : 'list-item-even'}\n          style=\"position: absolute; top: 0; left: 0; width: {col.size}px; height: {row.size}px; transform: translateX({col.start}px) translateY({row.start}px);\"\n        >\n          Cell {row.index}, {col.index}\n        </div>\n      {/each}\n    {/each}\n  </div>\n</div>\n\n<style>\n  .scroll-container {\n    height: 500px;\n    width: 500px;\n    overflow: auto;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/dynamic/src/RowVirtualizerDynamic.svelte",
    "content": "<script lang=\"ts\">\n  import { faker } from '@faker-js/faker'\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n  let virtualItemEls: HTMLDivElement[] = []\n\n  function randomNumber(min: number, max: number) {\n    return faker.number.int({ min, max })\n  }\n\n  const sentences = new Array(10000)\n    .fill(true)\n    .map(() => faker.lorem.sentence(randomNumber(20, 70)))\n\n  const count = sentences.length\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    count,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 45,\n  })\n\n  $: items = $virtualizer.getVirtualItems()\n\n  $: {\n    if (virtualItemEls.length)\n      virtualItemEls.forEach((el) => $virtualizer.measureElement(el))\n  }\n</script>\n\n<div>\n  <button\n    on:click={() => {\n      $virtualizer.scrollToIndex(0)\n    }}\n  >\n    scroll to the top\n  </button>\n  <span style=\"padding: 0 4px;\" />\n  <button\n    on:click={() => {\n      $virtualizer.scrollToIndex(count / 2)\n    }}\n  >\n    scroll to the middle\n  </button>\n  <span style=\"padding: 0 4px;\" />\n  <button\n    on:click={() => {\n      $virtualizer.scrollToIndex(count - 1)\n    }}\n  >\n    scroll to the end\n  </button>\n  <hr />\n  <div class=\"list scroll-container\" bind:this={virtualListEl}>\n    <div\n      style=\"position: relative; height: {$virtualizer.getTotalSize()}px; width: 100%;\"\n    >\n      <div\n        style=\"position: absolute; top: 0; left: 0; width: 100%; transform: translateY({items[0]\n          ? items[0].start\n          : 0}px);\"\n      >\n        {#each items as row, idx (row.index)}\n          <div\n            bind:this={virtualItemEls[idx]}\n            data-index={row.index}\n            class:list-item-even={row.index % 2 === 0}\n            class:list-item-odd={row.index % 2 === 1}\n          >\n            <div style=\"padding: 10px 0;\">\n              <div>Row {row.index}</div>\n              <div>{sentences[row.index]}</div>\n            </div>\n          </div>\n        {/each}\n      </div>\n    </div>\n  </div>\n</div>\n\n<style>\n  .scroll-container {\n    height: 400px;\n    width: 400px;\n    overflow-y: auto;\n    contain: 'strict';\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/dynamic/src/RowVirtualizerDynamicWindow.svelte",
    "content": "<script lang=\"ts\">\n  import { faker } from '@faker-js/faker'\n  import { createWindowVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n  let virtualItemEls: HTMLDivElement[] = []\n\n  function randomNumber(min: number, max: number) {\n    return faker.number.int({ min, max })\n  }\n\n  const sentences = new Array(10000)\n    .fill(true)\n    .map(() => faker.lorem.sentence(randomNumber(20, 70)))\n\n  const count = sentences.length\n\n  $: virtualizer = createWindowVirtualizer<HTMLDivElement>({\n    count,\n    scrollMargin: virtualListEl?.offsetTop ?? 0,\n    estimateSize: () => 45,\n  })\n\n  $: items = $virtualizer.getVirtualItems()\n\n  $: {\n    if (virtualItemEls.length)\n      virtualItemEls.forEach((el) => $virtualizer.measureElement(el))\n  }\n</script>\n\n<div>\n  <div class=\"list scroll-container\" bind:this={virtualListEl}>\n    <div\n      style=\"position: relative; height: {$virtualizer.getTotalSize()}px; width: 100%;\"\n    >\n      <div\n        style=\"position: absolute; top: 0; left: 0; width: 100%; transform: translateY({items[0]\n          ? items[0].start - $virtualizer.options.scrollMargin\n          : 0}px);\"\n      >\n        {#each items as row, idx (row.index)}\n          <div\n            bind:this={virtualItemEls[idx]}\n            data-index={row.index}\n            class:list-item-even={row.index % 2 === 0}\n            class:list-item-odd={row.index % 2 === 1}\n          >\n            <div style=\"padding: 10px 0;\">\n              <div>Row {row.index}</div>\n              <div>{sentences[row.index]}</div>\n            </div>\n          </div>\n        {/each}\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "examples/svelte/dynamic/src/app.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/svelte/dynamic/src/main.ts",
    "content": "import './app.css'\nimport App from './App.svelte'\n\nconst app = new App({\n  target: document.getElementById('app')!,\n})\n\nexport default app\n"
  },
  {
    "path": "examples/svelte/dynamic/src/vite-env.d.ts",
    "content": "/// <reference types=\"svelte\" />\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/svelte/dynamic/svelte.config.js",
    "content": "import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n\nexport default {\n  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n}\n"
  },
  {
    "path": "examples/svelte/dynamic/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/svelte/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"resolveJsonModule\": true,\n    /**\n     * Typecheck JS in `.svelte` and `.js` files by default.\n     * Disable checkJs if you'd like to use dynamic types in JS.\n     * Note that setting allowJs false does not prevent the use\n     * of JS in `.svelte` files.\n     */\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"isolatedModules\": true\n  },\n  \"include\": [\"src/**/*.d.ts\", \"src/**/*.ts\", \"src/**/*.js\", \"src/**/*.svelte\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/svelte/dynamic/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\"\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/svelte/dynamic/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport { svelte } from '@sveltejs/vite-plugin-svelte'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [svelte()],\n})\n"
  },
  {
    "path": "examples/svelte/fixed/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/svelte/fixed/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/svelte/fixed/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/svelte/fixed/package.json",
    "content": "{\n  \"name\": \"tanstack-svelte-virtual-example-fixed\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"check\": \"svelte-check --tsconfig ./tsconfig.json\"\n  },\n  \"dependencies\": {\n    \"@tanstack/svelte-virtual\": \"^3.13.23\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"@tsconfig/svelte\": \"^5.0.4\",\n    \"svelte\": \"^4.2.20\",\n    \"svelte-check\": \"^4.2.1\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/svelte/fixed/src/App.svelte",
    "content": "<script lang=\"ts\">\n  import RowVirtualizerFixed from './RowVirtualizerFixed.svelte'\n  import ColumnVirtualizerFixed from './ColumnVirtualizerFixed.svelte'\n  import GridVirtualizerFixed from './GridVirtualizerFixed.svelte'\n</script>\n\n<main>\n  <p>\n    These components are using <strong>fixed</strong> sizes. This means that every\n    element's dimensions are hard-coded to the same value and never change.\n  </p>\n  <br />\n  <br />\n\n  <h3>Rows</h3>\n  <RowVirtualizerFixed />\n  <br />\n  <br />\n  <h3>Columns</h3>\n  <ColumnVirtualizerFixed />\n  <br />\n  <br />\n  <h3>Grid</h3>\n  <GridVirtualizerFixed />\n</main>\n"
  },
  {
    "path": "examples/svelte/fixed/src/ColumnVirtualizerFixed.svelte",
    "content": "<script lang=\"ts\">\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    horizontal: true,\n    count: 10000,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 100,\n    overscan: 5,\n  })\n</script>\n\n<div class=\"list scroll-container\" bind:this={virtualListEl}>\n  <div\n    style=\"position: relative; height: 100%; width: {$virtualizer.getTotalSize()}px;\"\n  >\n    {#each $virtualizer.getVirtualItems() as col (col.index)}\n      <div\n        class:list-item-even={col.index % 2 === 0}\n        class:list-item-odd={col.index % 2 === 1}\n        style=\"position: absolute; top: 0; left: 0; width: {col.size}px; height: 100%; transform: translateX({col.start}px);\"\n      >\n        Column {col.index}\n      </div>\n    {/each}\n  </div>\n</div>\n\n<style>\n  .scroll-container {\n    height: 100px;\n    width: 400px;\n    overflow: auto;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/fixed/src/GridVirtualizerFixed.svelte",
    "content": "<script lang=\"ts\">\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n\n  $: rowVirtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    count: 10000,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 35,\n    overscan: 5,\n  })\n\n  $: columnVirtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    horizontal: true,\n    count: 10000,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 100,\n    overscan: 5,\n  })\n</script>\n\n<div class=\"list scroll-container\" bind:this={virtualListEl}>\n  <div\n    style=\"position: relative; height: {$rowVirtualizer.getTotalSize()}px; width: {$columnVirtualizer.getTotalSize()}px;\"\n  >\n    {#each $rowVirtualizer.getVirtualItems() as row (row.index)}\n      {#each $columnVirtualizer.getVirtualItems() as col (col.index)}\n        <div\n          class={col.index % 2\n            ? row.index % 2 === 0\n              ? 'list-item-odd'\n              : 'list-item-even'\n            : row.index % 2\n              ? 'list-item-odd'\n              : 'list-item-even'}\n          style=\"position: absolute; top: 0; left: 0; width: {col.size}px; height: {row.size}px; transform: translateX({col.start}px) translateY({row.start}px);\"\n        >\n          Cell {row.index}, {col.index}\n        </div>\n      {/each}\n    {/each}\n  </div>\n</div>\n\n<style>\n  .scroll-container {\n    height: 500px;\n    width: 500px;\n    overflow: auto;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/fixed/src/RowVirtualizerFixed.svelte",
    "content": "<script lang=\"ts\">\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    count: 10000,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 35,\n    overscan: 5,\n  })\n</script>\n\n<div class=\"list scroll-container\" bind:this={virtualListEl}>\n  <div\n    style=\"position: relative; height: {$virtualizer.getTotalSize()}px; width: 100%;\"\n  >\n    {#each $virtualizer.getVirtualItems() as row (row.index)}\n      <div\n        class:list-item-even={row.index % 2 === 0}\n        class:list-item-odd={row.index % 2 === 1}\n        style=\"position: absolute; top: 0; left: 0; width: 100%; height: {row.size}px; transform: translateY({row.start}px);\"\n      >\n        Row {row.index}\n      </div>\n    {/each}\n  </div>\n</div>\n\n<style>\n  .scroll-container {\n    height: 200px;\n    width: 400px;\n    overflow: auto;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/fixed/src/app.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/svelte/fixed/src/main.ts",
    "content": "import './app.css'\nimport App from './App.svelte'\n\nconst app = new App({\n  target: document.getElementById('app')!,\n})\n\nexport default app\n"
  },
  {
    "path": "examples/svelte/fixed/src/vite-env.d.ts",
    "content": "/// <reference types=\"svelte\" />\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/svelte/fixed/svelte.config.js",
    "content": "import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n\nexport default {\n  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n}\n"
  },
  {
    "path": "examples/svelte/fixed/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/svelte/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"resolveJsonModule\": true,\n    /**\n     * Typecheck JS in `.svelte` and `.js` files by default.\n     * Disable checkJs if you'd like to use dynamic types in JS.\n     * Note that setting allowJs false does not prevent the use\n     * of JS in `.svelte` files.\n     */\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"isolatedModules\": true\n  },\n  \"include\": [\"src/**/*.d.ts\", \"src/**/*.ts\", \"src/**/*.js\", \"src/**/*.svelte\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/svelte/fixed/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\"\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/svelte/fixed/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport { svelte } from '@sveltejs/vite-plugin-svelte'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [svelte()],\n})\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/package.json",
    "content": "{\n  \"name\": \"tanstack-svelte-virtual-example-infinite-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"check\": \"svelte-check --tsconfig ./tsconfig.json\"\n  },\n  \"dependencies\": {\n    \"@tanstack/svelte-query\": \"^5.80.7\",\n    \"@tanstack/svelte-virtual\": \"^3.13.23\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"@tsconfig/svelte\": \"^5.0.4\",\n    \"svelte\": \"^4.2.20\",\n    \"svelte-check\": \"^4.2.1\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/src/App.svelte",
    "content": "<script lang=\"ts\">\n  import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query'\n  import InfiniteRows from './InfiniteRows.svelte'\n\n  const queryClient = new QueryClient()\n</script>\n\n<QueryClientProvider client={queryClient}>\n  <InfiniteRows />\n</QueryClientProvider>\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/src/InfiniteRows.svelte",
    "content": "<script lang=\"ts\">\n  import { createInfiniteQuery } from '@tanstack/svelte-query'\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n\n  let virtualListEl: HTMLDivElement\n\n  const query = createInfiniteQuery({\n    queryKey: ['projects'],\n    queryFn: ({ pageParam }) => fetchServerPage(10, pageParam),\n    initialPageParam: 1,\n    getNextPageParam: (_lastGroup, groups) => groups.length,\n  })\n\n  $: allRows =\n    ($query.data && $query.data.pages.flatMap((page) => page.rows)) || []\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    count: 0,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 100,\n    overscan: 5,\n  })\n\n  $: {\n    $virtualizer.setOptions({\n      count: $query.hasNextPage ? allRows.length + 1 : allRows.length,\n    })\n\n    const [lastItem] = [...$virtualizer.getVirtualItems()].reverse()\n\n    if (\n      lastItem &&\n      lastItem.index > allRows.length - 1 &&\n      $query.hasNextPage &&\n      !$query.isFetchingNextPage\n    ) {\n      $query.fetchNextPage()\n    }\n  }\n\n  async function fetchServerPage(\n    limit: number,\n    offset: number = 0,\n  ): Promise<{ rows: string[]; nextOffset: number }> {\n    const rows = new Array(limit)\n      .fill(0)\n      .map((_e, i) => `Async loaded row #${i + (offset - 1) * limit}`)\n\n    await new Promise((r) => setTimeout(r, 500))\n\n    return { rows, nextOffset: offset + 1 }\n  }\n</script>\n\n<main>\n  <p>\n    This infinite scroll example uses Svelte Query's createInfiniteQuery\n    function to fetch infinite data from a posts endpoint and then a\n    rowVirtualizer is used along with a loader-row placed at the bottom of the\n    list to trigger the next page to load.\n  </p>\n  <br />\n  <br />\n\n  {#if $query.isLoading}\n    Loading...\n  {:else if $query.isError}\n    <span>Error: {$query.error.message}</span>\n  {:else if $query.isSuccess}\n    <div class=\"list scroll-container\" bind:this={virtualListEl}>\n      <div\n        style=\"position: relative; height: {$virtualizer.getTotalSize()}px; width: 100%;\"\n      >\n        {#each $virtualizer.getVirtualItems() as row (row.index)}\n          <div\n            class:list-item-even={row.index % 2 === 0}\n            class:list-item-odd={row.index % 2 === 1}\n            style=\"position: absolute; top: 0; left: 0; width: 100%; height: {row.size}px; transform: translateY({row.start}px);\"\n          >\n            {#if row.index > allRows.length - 1}\n              {#if $query.hasNextPage}\n                Loading more...\n              {:else}\n                Nothing more to load\n              {/if}\n            {:else}\n              {allRows[row.index]}\n            {/if}\n          </div>\n        {/each}\n      </div>\n    </div>\n  {/if}\n  {#if $query.isFetching && !$query.isFetchingNextPage}\n    <p>Background updating...</p>\n  {/if}\n</main>\n\n<style>\n  .scroll-container {\n    height: 500px;\n    width: 100%;\n    overflow: auto;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/src/app.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/src/main.ts",
    "content": "import './app.css'\nimport App from './App.svelte'\n\nconst app = new App({\n  target: document.getElementById('app')!,\n})\n\nexport default app\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/src/vite-env.d.ts",
    "content": "/// <reference types=\"svelte\" />\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/svelte.config.js",
    "content": "import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n\nexport default {\n  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n}\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/svelte/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"resolveJsonModule\": true,\n    /**\n     * Typecheck JS in `.svelte` and `.js` files by default.\n     * Disable checkJs if you'd like to use dynamic types in JS.\n     * Note that setting allowJs false does not prevent the use\n     * of JS in `.svelte` files.\n     */\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"isolatedModules\": true\n  },\n  \"include\": [\"src/**/*.d.ts\", \"src/**/*.ts\", \"src/**/*.js\", \"src/**/*.svelte\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\"\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/svelte/infinite-scroll/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport { svelte } from '@sveltejs/vite-plugin-svelte'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [svelte()],\n})\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/package.json",
    "content": "{\n  \"name\": \"tanstack-svelte-virtual-example-smooth-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"check\": \"svelte-check --tsconfig ./tsconfig.json\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/svelte-virtual\": \"^3.13.23\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"@tsconfig/svelte\": \"^5.0.4\",\n    \"svelte\": \"^4.2.20\",\n    \"svelte-check\": \"^4.2.1\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/src/App.svelte",
    "content": "<script lang=\"ts\">\n  import {\n    createVirtualizer,\n    elementScroll,\n    type VirtualizerOptions,\n  } from '@tanstack/svelte-virtual'\n  import { onMount } from 'svelte'\n\n  let virtualListEl: HTMLDivElement\n  let time = Date.now()\n  let randomIndex = Math.floor(Math.random() * 10000)\n  let scrollToFn: VirtualizerOptions<any, any>['scrollToFn'] = () => {}\n\n  function easeInOutQuint(t: number) {\n    return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t\n  }\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    count: 10000,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 35,\n    overscan: 5,\n    scrollToFn,\n  })\n\n  onMount(() => {\n    scrollToFn = (offset, canSmooth, instance) => {\n      const duration = 1000\n      const start = virtualListEl.scrollTop\n      const startTime = (time = Date.now())\n\n      function run() {\n        if (time !== startTime) return\n        const now = Date.now()\n        const elapsed = now - startTime\n        const progress = easeInOutQuint(Math.min(elapsed / duration, 1))\n        const interpolated = start + (offset - start) * progress\n\n        if (elapsed < duration) {\n          elementScroll(interpolated, canSmooth, instance)\n          requestAnimationFrame(run)\n        } else {\n          elementScroll(interpolated, canSmooth, instance)\n        }\n      }\n\n      requestAnimationFrame(run)\n    }\n  })\n</script>\n\n<main>\n  <p>\n    This smooth scroll example uses the <code>`scrollToFn`</code> to implement a\n    custom scrolling function for the methods like\n    <code>`scrollToIndex`</code> and <code>`scrollToOffset`</code>\n  </p>\n  <br />\n  <br />\n\n  <div>\n    <button\n      on:click={() => {\n        $virtualizer.scrollToIndex(randomIndex)\n        randomIndex = Math.floor(Math.random() * 10000)\n      }}\n    >\n      Scroll To Random Index ({randomIndex})\n    </button>\n  </div>\n\n  <br />\n  <br />\n\n  <div class=\"list scroll-container\" bind:this={virtualListEl}>\n    <div\n      style=\"position: relative; height: {$virtualizer.getTotalSize()}px; width: 100%;\"\n    >\n      {#each $virtualizer.getVirtualItems() as row (row.index)}\n        <div\n          class:list-item-even={row.index % 2 === 0}\n          class:list-item-odd={row.index % 2 === 1}\n          style=\"position: absolute; top: 0; left: 0; width: 100%; height: {row.size}px; transform: translateY({row.start}px);\"\n        >\n          Row {row.index}\n        </div>\n      {/each}\n    </div>\n  </div>\n</main>\n\n<style>\n  .scroll-container {\n    height: 200px;\n    width: 400px;\n    overflow: auto;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/src/app.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/src/main.ts",
    "content": "import './app.css'\nimport App from './App.svelte'\n\nconst app = new App({\n  target: document.getElementById('app')!,\n})\n\nexport default app\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/src/vite-env.d.ts",
    "content": "/// <reference types=\"svelte\" />\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/svelte.config.js",
    "content": "import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n\nexport default {\n  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n}\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/svelte/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"resolveJsonModule\": true,\n    /**\n     * Typecheck JS in `.svelte` and `.js` files by default.\n     * Disable checkJs if you'd like to use dynamic types in JS.\n     * Note that setting allowJs false does not prevent the use\n     * of JS in `.svelte` files.\n     */\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"isolatedModules\": true\n  },\n  \"include\": [\"src/**/*.d.ts\", \"src/**/*.ts\", \"src/**/*.js\", \"src/**/*.svelte\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\"\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/svelte/smooth-scroll/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport { svelte } from '@sveltejs/vite-plugin-svelte'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [svelte()],\n})\n"
  },
  {
    "path": "examples/svelte/sticky/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/svelte/sticky/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/svelte/sticky/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/svelte/sticky/package.json",
    "content": "{\n  \"name\": \"tanstack-svelte-virtual-example-sticky\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"check\": \"svelte-check --tsconfig ./tsconfig.json\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/svelte-virtual\": \"^3.13.23\",\n    \"lodash\": \"^4.17.21\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"@tsconfig/svelte\": \"^5.0.4\",\n    \"svelte\": \"^4.2.20\",\n    \"svelte-check\": \"^4.2.1\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/svelte/sticky/src/App.svelte",
    "content": "<script lang=\"ts\">\n  import { faker } from '@faker-js/faker'\n  import { findIndex, groupBy } from 'lodash'\n  import {\n    createVirtualizer,\n    defaultRangeExtractor,\n    type Range,\n  } from '@tanstack/svelte-virtual'\n\n  const groupedNames = groupBy(\n    Array.from({ length: 1000 })\n      .map(() => faker.person.firstName())\n      .sort(),\n    (name: any) => name[0],\n  )\n  const groups = Object.keys(groupedNames)\n  const rows = groups.reduce((acc, k) => [...acc, k, ...groupedNames[k]], [])\n  const stickyIndexes = groups.map((gn) =>\n    findIndex(rows, (n: any) => n === gn),\n  )\n\n  let virtualListEl: HTMLDivElement\n  let activeStickyIndex: number = 0\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({\n    count: rows.length,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 50,\n    overscan: 5,\n  })\n\n  $: {\n    function rangeExtractor(range: Range): number[] {\n      activeStickyIndex = [...stickyIndexes]\n        .reverse()\n        .find((index) => range.startIndex >= index)\n\n      const next = new Set([activeStickyIndex, ...defaultRangeExtractor(range)])\n\n      return [...next].sort((a, b) => a - b)\n    }\n    $virtualizer.setOptions({ rangeExtractor })\n  }\n\n  function isSticky(index: number) {\n    return stickyIndexes.includes(index)\n  }\n  $: isActiveSticky = (index: number) => activeStickyIndex === index\n</script>\n\n<main>\n  <div class=\"list scroll-container\" bind:this={virtualListEl}>\n    <div\n      style=\"position: relative; height: {$virtualizer.getTotalSize()}px; width: 100%;\"\n    >\n      {#each $virtualizer.getVirtualItems() as row (row.index)}\n        <div\n          class:sticky={isSticky(row.index)}\n          class:active={isActiveSticky(row.index)}\n          style={`top: 0; left: 0; width: 100%; height: ${row.size}px; ${\n            !isActiveSticky(row.index)\n              ? `position: absolute; transform: translateY(${row.start}px);`\n              : ''\n          }`}\n        >\n          {rows[row.index]}\n        </div>\n      {/each}\n    </div>\n  </div>\n</main>\n\n<style>\n  .scroll-container {\n    height: 200px;\n    width: 400px;\n    overflow: auto;\n  }\n  .sticky {\n    position: absolute;\n    background: #fff;\n    border-bottom: 1px solid #ddd;\n    z-index: 1;\n  }\n  .sticky.active {\n    position: sticky;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/sticky/src/app.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/svelte/sticky/src/main.ts",
    "content": "import './app.css'\nimport App from './App.svelte'\n\nconst app = new App({\n  target: document.getElementById('app')!,\n})\n\nexport default app\n"
  },
  {
    "path": "examples/svelte/sticky/src/vite-env.d.ts",
    "content": "/// <reference types=\"svelte\" />\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/svelte/sticky/svelte.config.js",
    "content": "import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n\nexport default {\n  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n}\n"
  },
  {
    "path": "examples/svelte/sticky/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/svelte/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"resolveJsonModule\": true,\n    /**\n     * Typecheck JS in `.svelte` and `.js` files by default.\n     * Disable checkJs if you'd like to use dynamic types in JS.\n     * Note that setting allowJs false does not prevent the use\n     * of JS in `.svelte` files.\n     */\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"isolatedModules\": true\n  },\n  \"include\": [\"src/**/*.d.ts\", \"src/**/*.ts\", \"src/**/*.js\", \"src/**/*.svelte\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/svelte/sticky/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\"\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/svelte/sticky/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport { svelte } from '@sveltejs/vite-plugin-svelte'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [svelte()],\n})\n"
  },
  {
    "path": "examples/svelte/table/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/svelte/table/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/svelte/table/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/svelte/table/package.json",
    "content": "{\n  \"name\": \"tanstack-svelte-virtual-example-table\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"check\": \"svelte-check --tsconfig ./tsconfig.json\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/svelte-table\": \"^8.21.3\",\n    \"@tanstack/svelte-virtual\": \"^3.13.23\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"@tsconfig/svelte\": \"^5.0.4\",\n    \"svelte\": \"^4.2.20\",\n    \"svelte-check\": \"^4.2.1\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "examples/svelte/table/src/App.svelte",
    "content": "<script lang=\"ts\">\n  import {\n    createSvelteTable,\n    getCoreRowModel,\n    getSortedRowModel,\n    type ColumnDef,\n    type SortingState,\n    type Updater,\n    flexRender,\n    type TableOptions,\n  } from '@tanstack/svelte-table'\n  import { createVirtualizer } from '@tanstack/svelte-virtual'\n  import { makeData, type Person } from './makeData'\n  import { writable } from 'svelte/store'\n\n  let virtualListEl: HTMLDivElement\n  let sorting: SortingState = []\n\n  function setSorting(updater: Updater<SortingState>) {\n    if (updater instanceof Function) sorting = updater(sorting)\n    else sorting = updater\n\n    tableOptions.update((opts) => ({\n      ...opts,\n      state: {\n        ...opts.state,\n        sorting,\n      },\n    }))\n  }\n\n  const columns: ColumnDef<Person>[] = [\n    {\n      accessorKey: 'id',\n      header: 'ID',\n      size: 60,\n    },\n    {\n      accessorKey: 'firstName',\n      header: 'First Name',\n      cell: (info) => info.getValue(),\n    },\n    {\n      accessorFn: (row) => row.lastName,\n      id: 'lastName',\n      header: 'Last Name',\n      cell: (info) => info.getValue(),\n    },\n    {\n      accessorKey: 'age',\n      header: 'Age',\n      size: 50,\n    },\n    {\n      accessorKey: 'visits',\n      header: 'Visits',\n      size: 50,\n    },\n    {\n      accessorKey: 'status',\n      header: 'Status',\n    },\n    {\n      accessorKey: 'progress',\n      header: 'Profile Progress',\n      size: 80,\n    },\n    {\n      accessorKey: 'createdAt',\n      header: 'Created At',\n      cell: (info) => info.getValue<Date>().toLocaleString(),\n    },\n  ]\n\n  const tableOptions = writable<TableOptions<Person>>({\n    data: makeData(50_000),\n    columns,\n    state: {\n      sorting,\n    },\n    onSortingChange: setSorting,\n    getCoreRowModel: getCoreRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    debugTable: true,\n  })\n\n  const table = createSvelteTable(tableOptions)\n\n  $: rows = $table.getRowModel().rows\n\n  $: virtualizer = createVirtualizer<HTMLDivElement, HTMLTableRowElement>({\n    count: rows.length,\n    getScrollElement: () => virtualListEl,\n    estimateSize: () => 34,\n    overscan: 20,\n  })\n</script>\n\n<main>\n  <p>\n    For tables, the basis for the offset of the translate css function is from\n    the row's initial position itself. Because of this, we need to calculate the\n    translateY pixel count differently and base it off the index.\n  </p>\n\n  <div class=\"list scroll-container\" bind:this={virtualListEl}>\n    <div style=\"position: relative; height: {$virtualizer.getTotalSize()}px;\">\n      <table>\n        <thead>\n          {#each $table.getHeaderGroups() as headerGroup (headerGroup.id)}\n            <tr>\n              {#each headerGroup.headers as header (header.id)}\n                <th\n                  colspan={header.colSpan}\n                  style=\"width: {header.getSize()}px;\"\n                >\n                  {#if !header.isPlaceholder}\n                    <button\n                      class:sortable-header={header.column.getCanSort()}\n                      disabled={!header.column.getCanSort()}\n                      on:click={header.column.getToggleSortingHandler()}\n                    >\n                      <svelte:component\n                        this={flexRender(\n                          header.column.columnDef.header,\n                          header.getContext(),\n                        )}\n                      />\n                      {#if header.column.getIsSorted()}\n                        {header.column.getIsSorted() === 'desc' ? ' 🔽' : ' 🔼'}\n                      {/if}\n                    </button>\n                  {/if}\n                </th>\n              {/each}\n            </tr>\n          {/each}\n        </thead>\n        <tbody>\n          {#each $virtualizer.getVirtualItems() as row, idx (row.index)}\n            <tr\n              style=\"height: {row.size}px; transform: translateY({row.start -\n                idx * row.size}px);\"\n            >\n              {#each rows[row.index].getVisibleCells() as cell (cell.id)}\n                <td>\n                  <svelte:component\n                    this={flexRender(\n                      cell.column.columnDef.cell,\n                      cell.getContext(),\n                    )}\n                  />\n                </td>\n              {/each}\n            </tr>\n          {/each}\n        </tbody>\n      </table>\n    </div>\n  </div>\n</main>\n\n<style>\n  button {\n    background: none;\n    border: none;\n    padding: 0;\n    margin: 0;\n    font: inherit;\n    color: inherit;\n    cursor: pointer;\n    outline: inherit;\n  }\n\n  .scroll-container {\n    height: 600px;\n    width: 100%;\n    overflow: auto;\n  }\n  .sortable-header {\n    cursor: pointer;\n    user-select: none;\n  }\n</style>\n"
  },
  {
    "path": "examples/svelte/table/src/app.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.list {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.list-item-even,\n.list-item-odd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.list-item-even {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/svelte/table/src/main.ts",
    "content": "import './app.css'\nimport App from './App.svelte'\n\nconst app = new App({\n  target: document.getElementById('app')!,\n})\n\nexport default app\n"
  },
  {
    "path": "examples/svelte/table/src/makeData.ts",
    "content": "import { faker } from '@faker-js/faker'\n\nexport type Person = {\n  id: number\n  firstName: string\n  lastName: string\n  age: number\n  visits: number\n  progress: number\n  status: 'relationship' | 'complicated' | 'single'\n  createdAt: Date\n}\n\nconst range = (len: number) => {\n  const arr: number[] = []\n  for (let i = 0; i < len; i++) {\n    arr.push(i)\n  }\n  return arr\n}\n\nconst newPerson = (index: number): Person => {\n  return {\n    id: index + 1,\n    firstName: faker.person.firstName(),\n    lastName: faker.person.lastName(),\n    age: faker.number.int(40),\n    visits: faker.number.int(1000),\n    progress: faker.number.int(100),\n    createdAt: faker.datatype.datetime({ max: new Date().getTime() }),\n    status: faker.helpers.shuffle<Person['status']>([\n      'relationship',\n      'complicated',\n      'single',\n    ])[0]!,\n  }\n}\n\nexport function makeData(...lens: number[]) {\n  const makeDataLevel = (depth = 0): Person[] => {\n    const len = lens[depth]!\n    return range(len).map((d): Person => {\n      return {\n        ...newPerson(d),\n      }\n    })\n  }\n\n  return makeDataLevel()\n}\n"
  },
  {
    "path": "examples/svelte/table/src/vite-env.d.ts",
    "content": "/// <reference types=\"svelte\" />\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/svelte/table/svelte.config.js",
    "content": "import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n\nexport default {\n  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess\n  // for more information about preprocessors\n  preprocess: vitePreprocess(),\n}\n"
  },
  {
    "path": "examples/svelte/table/tsconfig.json",
    "content": "{\n  \"extends\": \"@tsconfig/svelte/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"resolveJsonModule\": true,\n    /**\n     * Typecheck JS in `.svelte` and `.js` files by default.\n     * Disable checkJs if you'd like to use dynamic types in JS.\n     * Note that setting allowJs false does not prevent the use\n     * of JS in `.svelte` files.\n     */\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"isolatedModules\": true\n  },\n  \"include\": [\"src/**/*.d.ts\", \"src/**/*.ts\", \"src/**/*.js\", \"src/**/*.svelte\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/svelte/table/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\"\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/svelte/table/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport { svelte } from '@sveltejs/vite-plugin-svelte'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [svelte()],\n})\n"
  },
  {
    "path": "examples/vue/dynamic/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/dynamic/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/dynamic/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/dynamic/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-dynamic\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/dynamic/src/App.vue",
    "content": "<template>\n  <div>\n    <p>\n      These components are using <strong>dynamic</strong> sizes. This means that\n      each element's exact dimensions are unknown when rendered. An estimated\n      dimension is used as the initial measurement, then this measurement is\n      readjusted on the fly as each element is rendered.\n    </p>\n    <nav>\n      <ul>\n        <li>\n          <a href=\"/\">List</a>\n        </li>\n        <li>\n          <a href=\"/window-list\">List - window as scroller</a>\n        </li>\n        <li>\n          <a href=\"/columns\">Column</a>\n        </li>\n        <li>\n          <a href=\"/grid\">Grid</a>\n        </li>\n      </ul>\n    </nav>\n    <RowVirtualizerDynamic v-if=\"pathname === '/'\" />\n    <RowVirtualizerDynamicWindow v-else-if=\"pathname === '/window-list'\" />\n    <ColumnVirtualizerDynamic v-else-if=\"pathname === '/columns'\" />\n    <GridVirtualizerDynamic v-else-if=\"pathname === '/grid'\" />\n    <div v-else>Not found</div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport RowVirtualizerDynamic from './components/RowVirtualizerDynamic.vue'\nimport RowVirtualizerDynamicWindow from './components/RowVirtualizerDynamicWindow.vue'\nimport ColumnVirtualizerDynamic from './components/ColumnVirtualizerDynamic.vue'\nimport GridVirtualizerDynamic from './components/GridVirtualizerDynamic.vue'\n\nconst pathname = computed(() => window.location.pathname)\n</script>\n"
  },
  {
    "path": "examples/vue/dynamic/src/components/ColumnVirtualizerDynamic.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"width: 400px; height: 400px; overflow-y: auto\"\n  >\n    <div\n      :style=\"{\n        width: `${totalSize}px`,\n        height: '100%',\n        position: 'relative',\n      }\"\n    >\n      <div\n        v-for=\"virtualColumn in virtualColumns\"\n        :key=\"virtualColumn.key\"\n        :data-index=\"virtualColumn.index\"\n        ref=\"virtualItemEls\"\n        :class=\"virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          height: '100%',\n          transform: `translateX(${virtualColumn.start}px)`,\n        }\"\n      >\n        <div :style=\"{ width: `${sentences[virtualColumn.index].length}px` }\">\n          <div>Column {{ virtualColumn.index }}</div>\n          <div>{{ sentences[virtualColumn.index] }}</div>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUpdated, ref, shallowRef } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\nimport { generateSentences } from './utils'\n\nconst sentences = generateSentences()\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: sentences.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 45,\n})\n\nconst virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => columnVirtualizer.value.getTotalSize())\n\nconst virtualItemEls = shallowRef([])\n\nfunction measureAll() {\n  columnVirtualizer.value.measureElement(null)\n  virtualItemEls.value.forEach((el) => {\n    if (el) columnVirtualizer.value.measureElement(el)\n  })\n}\n\nonMounted(measureAll)\nonUpdated(measureAll)\n</script>\n"
  },
  {
    "path": "examples/vue/dynamic/src/components/GridVirtualizerDynamic.vue",
    "content": "<template>\n  <div ref=\"parentRef\" style=\"overflow-y: auto; border: 1px solid #c8c8c8\">\n    <div\n      :style=\"{\n        height: `${totalSize}px`,\n        position: 'relative',\n      }\"\n    >\n      <template v-for=\"virtualRow in virtualRows\" :key=\"virtualRow.key\">\n        <div\n          :data-index=\"virtualRow.index\"\n          ref=\"virtualItemEls\"\n          :style=\"{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            transform: `translateY(${\n              virtualRow.start - rowVirtualizer.options.scrollMargin\n            }px)`,\n            display: 'flex',\n          }\"\n        >\n          <div :style=\"{ width: `${width[0]}px` }\" />\n          <div\n            v-for=\"virtualColumn in virtualColumns\"\n            :key=\"virtualColumn.key\"\n            :style=\"{\n              minHeight: virtualRow.index === 0 ? 50 : virtualRow.size,\n              width: `${getColumnWidth(virtualColumn.index)}px`,\n              borderBottom: '1px solid #c8c8c8',\n              borderRight: '1px solid #c8c8c8',\n              padding: '7px 12px',\n            }\"\n          >\n            <div v-if=\"virtualRow.index === 0\">\n              {{ columns[virtualColumn.index].name }}\n            </div>\n            <div v-else>\n              {{ data[virtualRow.index][virtualColumn.index] }}\n            </div>\n          </div>\n          <div :style=\"{ width: `${width[1]}px` }\" />\n        </div>\n      </template>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUpdated, ref, shallowRef } from 'vue'\nimport { useVirtualizer, useWindowVirtualizer } from '@tanstack/vue-virtual'\nimport { generateColumns, generateData } from './utils'\n\nconst columns = generateColumns(30)\nconst data = generateData(columns)\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst parentOffsetRef = ref(0)\n\nonMounted(() => {\n  parentOffsetRef.value = parentRef.value?.offsetTop ?? 0\n})\n\nconst rowVirtualizerOptions = computed(() => {\n  return {\n    count: data.length,\n    estimateSize: () => 350,\n    overscan: 5,\n    scrollMargin: parentOffsetRef.value,\n  }\n})\n\nconst rowVirtualizer = useWindowVirtualizer(rowVirtualizerOptions)\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n\nconst getColumnWidth = (index: number) => columns[index].width\n\nconst columnVirtualizerOptions = computed(() => {\n  return {\n    horizontal: true,\n    count: columns.length,\n    getScrollElement: () => parentRef.value,\n    estimateSize: getColumnWidth,\n    overscan: 5,\n  }\n})\n\nconst columnVirtualizer = useVirtualizer(columnVirtualizerOptions)\n\nconst virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems())\n\nconst width = computed(() => {\n  return virtualColumns.value.length > 0\n    ? [\n        virtualColumns.value[0].start,\n        columnVirtualizer.value.getTotalSize() -\n          virtualColumns.value[virtualColumns.value.length - 1].end,\n      ]\n    : [0, 0]\n})\n\nconst virtualItemEls = shallowRef([])\n\nfunction measureAll() {\n  rowVirtualizer.value.measureElement(null)\n  virtualItemEls.value.forEach((el) => {\n    if (el) rowVirtualizer.value.measureElement(el)\n  })\n}\n\nonMounted(measureAll)\nonUpdated(measureAll)\n</script>\n"
  },
  {
    "path": "examples/vue/dynamic/src/components/RowVirtualizerDynamic.vue",
    "content": "<template>\n  <div>\n    <button @click=\"rowVirtualizer.scrollToIndex(0)\">scroll to the top</button>\n    <span style=\"padding: 0 4px\"></span>\n    <button @click=\"rowVirtualizer.scrollToIndex(sentences.length / 2)\">\n      scroll to the middle\n    </button>\n    <span style=\"padding: 0 4px\"></span>\n    <button @click=\"rowVirtualizer.scrollToIndex(sentences.length - 1)\">\n      scroll to the end\n    </button>\n    <hr />\n    <div\n      ref=\"parentRef\"\n      class=\"List\"\n      style=\"height: 400px; width: 400px; overflow-y: auto; contain: strict\"\n    >\n      <div\n        :style=\"{\n          height: `${totalSize}px`,\n          width: '100%',\n          position: 'relative',\n        }\"\n      >\n        <div\n          :style=\"{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            width: '100%',\n            transform: `translateY(${virtualRows[0]?.start ?? 0}px)`,\n          }\"\n        >\n          <div\n            v-for=\"virtualRow in virtualRows\"\n            :key=\"virtualRow.key\"\n            :data-index=\"virtualRow.index\"\n            ref=\"virtualItemEls\"\n            :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n          >\n            <div style=\"padding: 10px 0\">\n              <div>Row {{ virtualRow.index }}</div>\n              <div>{{ sentences[virtualRow.index] }}</div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUpdated, ref, shallowRef } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\nimport { generateSentences } from './utils'\n\nconst sentences = generateSentences()\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: sentences.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 55,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n\nconst virtualItemEls = shallowRef([])\n\nfunction measureAll() {\n  rowVirtualizer.value.measureElement(null)\n  virtualItemEls.value.forEach((el) => {\n    if (el) rowVirtualizer.value.measureElement(el)\n  })\n}\n\nonMounted(measureAll)\nonUpdated(measureAll)\n</script>\n"
  },
  {
    "path": "examples/vue/dynamic/src/components/RowVirtualizerDynamicWindow.vue",
    "content": "<template>\n  <div ref=\"parentRef\" class=\"List\">\n    <div\n      :style=\"{\n        height: `${totalSize}px`,\n        width: '100%',\n        position: 'relative',\n      }\"\n    >\n      <div\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: '100%',\n          transform: `translateY(${\n            virtualRows[0]?.start - rowVirtualizer.options.scrollMargin ?? 0\n          }px)`,\n        }\"\n      >\n        <div\n          v-for=\"virtualRow in virtualRows\"\n          :key=\"virtualRow.key\"\n          :data-index=\"virtualRow.index\"\n          ref=\"virtualItemEls\"\n          :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        >\n          <div style=\"padding: 10px 0\">\n            <div>Row {{ virtualRow.index }}</div>\n            <div>{{ sentences[virtualRow.index] }}</div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUpdated, ref, shallowRef } from 'vue'\nimport { useWindowVirtualizer } from '@tanstack/vue-virtual'\nimport { generateSentences } from './utils'\n\nconst sentences = generateSentences()\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst parentOffsetRef = ref(0)\n\nonMounted(() => {\n  parentOffsetRef.value = parentRef.value?.offsetTop ?? 0\n})\n\nconst rowVirtualizerOptions = computed(() => {\n  return {\n    count: sentences.length,\n    estimateSize: () => 45,\n    scrollMargin: parentOffsetRef.value,\n  }\n})\n\nconst rowVirtualizer = useWindowVirtualizer(rowVirtualizerOptions)\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n\nconst virtualItemEls = shallowRef([])\n\nfunction measureAll() {\n  rowVirtualizer.value.measureElement(null)\n  virtualItemEls.value.forEach((el) => {\n    if (el) rowVirtualizer.value.measureElement(el)\n  })\n}\n\nonMounted(measureAll)\nonUpdated(measureAll)\n</script>\n"
  },
  {
    "path": "examples/vue/dynamic/src/components/utils.ts",
    "content": "import { faker } from '@faker-js/faker'\n\ninterface Column {\n  key: string\n  name: string\n  width: number\n}\n\nexport const generateRandomNumber = (min: number, max: number) =>\n  faker.number.int({ min, max })\n\nexport const generateSentences = () =>\n  new Array(10000)\n    .fill(true)\n    .map(() => faker.lorem.sentence(generateRandomNumber(20, 70)))\n\nexport const generateColumns = (count: number) => {\n  return new Array(count).fill(0).map((_, i) => {\n    const key: string = i.toString()\n    return {\n      key,\n      name: `Column ${i}`,\n      width: generateRandomNumber(75, 300),\n    }\n  })\n}\n\nexport const generateData = (columns: Column[], count = 300) => {\n  return new Array(count).fill(0).map((_, rowIndex) =>\n    columns.reduce<string[]>((acc, _curr, colIndex) => {\n      // simulate dynamic size cells\n      const val = faker.lorem.lines(((rowIndex + colIndex) % 10) + 1)\n\n      acc.push(val)\n\n      return acc\n    }, []),\n  )\n}\n"
  },
  {
    "path": "examples/vue/dynamic/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/dynamic/src/style.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n"
  },
  {
    "path": "examples/vue/dynamic/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/dynamic/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true,\n    \"noImplicitAny\": false // Remove\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/dynamic/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/dynamic/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/fixed/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/fixed/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/fixed/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/fixed/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-fixed\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/fixed/src/App.vue",
    "content": "<template>\n  <div>\n    <p>\n      These components are using <strong>fixed</strong> sizes. This means that\n      every element's dimensions are hard-coded to the same value and never\n      change.\n    </p>\n    <br />\n    <br />\n\n    <h3>Rows</h3>\n    <RowVirtualizerFixed />\n    <br />\n    <br />\n    <h3>Columns</h3>\n    <ColumnVirtualizerFixed />\n    <br />\n    <br />\n    <h3>Grid</h3>\n    <GridVirtualizerFixed />\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport RowVirtualizerFixed from './components/RowVirtualizerFixed.vue'\nimport ColumnVirtualizerFixed from './components/ColumnVirtualizerFixed.vue'\nimport GridVirtualizerFixed from './components/GridVirtualizerFixed.vue'\n</script>\n"
  },
  {
    "path": "examples/vue/fixed/src/components/ColumnVirtualizerFixed.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 200px; width: 400px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        height: '100%',\n        width: `${totalSize}px`,\n        position: 'relative',\n      }\"\n    >\n      <div\n        v-for=\"virtualColumn in virtualColumns\"\n        :key=\"virtualColumn.index\"\n        :class=\"virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: `${virtualColumn.size}px`,\n          height: '100%',\n          transform: `translateX(${virtualColumn.start}px)`,\n        }\"\n      >\n        Column {{ virtualColumn.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: 10000,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 100,\n  overscan: 5,\n})\n\nconst virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => columnVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/fixed/src/components/GridVirtualizerFixed.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 500px; width: 500px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        height: `${totalSizeRows}px`,\n        width: `${totalSizeColumns}px`,\n        position: 'relative',\n      }\"\n    >\n      <template v-for=\"virtualRow in virtualRows\" :key=\"virtualRow.index\">\n        <div\n          v-for=\"virtualColumn in columnVirtualizer.getVirtualItems()\"\n          :key=\"virtualColumn.index\"\n          :class=\"\n            virtualColumn.index % 2\n              ? virtualRow.index % 2 === 0\n                ? 'ListItemOdd'\n                : 'ListItemEven'\n              : virtualRow.index % 2\n                ? 'ListItemOdd'\n                : 'ListItemEven'\n          \"\n          :style=\"{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            width: `${virtualColumn.size}px`,\n            height: `${virtualRow.size}px`,\n            transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,\n          }\"\n        >\n          Cell {{ virtualRow.index }}, {{ virtualColumn.index }}\n        </div>\n      </template>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: 10000,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 35,\n  overscan: 5,\n})\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: 10000,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 100,\n  overscan: 5,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\nconst totalSizeRows = computed(() => rowVirtualizer.value.getTotalSize())\n\nconst totalSizeColumns = computed(() => columnVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/fixed/src/components/RowVirtualizerFixed.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 200px; width: 400px; overflow: auto\"\n  >\n    <div\n      :style=\"{ height: `${totalSize}px`, width: '100%', position: 'relative' }\"\n    >\n      <div\n        v-for=\"virtualRow in virtualRows\"\n        :key=\"virtualRow.index\"\n        :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: '100%',\n          height: `${virtualRow.size}px`,\n          transform: `translateY(${virtualRow.start}px)`,\n        }\"\n      >\n        Row {{ virtualRow.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: 10000,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 35,\n  overscan: 5,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/fixed/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/fixed/src/style.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/vue/fixed/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/fixed/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/fixed/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/fixed/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/infinite-scroll/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/infinite-scroll/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/infinite-scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/infinite-scroll/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-infinite-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/vue-query\": \"^5.80.7\",\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/infinite-scroll/src/App.vue",
    "content": "<script setup lang=\"ts\">\nimport { computed, ref, watchEffect } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\nimport { useInfiniteQuery } from '@tanstack/vue-query'\n\nconst fetchServerPage = async (\n  limit: number,\n  offset: number = 0,\n): Promise<{ rows: string[]; nextOffset: number }> => {\n  const rows = new Array(limit)\n    .fill(0)\n    .map((e, i) => `Async loaded row #${i + offset * limit}`)\n\n  await new Promise((r) => setTimeout(r, 500))\n\n  return { rows, nextOffset: offset + 1 }\n}\n\nconst {\n  status,\n  data,\n  error,\n  isFetching,\n  isFetchingNextPage,\n  fetchNextPage,\n  hasNextPage,\n} = useInfiniteQuery({\n  queryKey: ['projects'],\n  queryFn: (ctx) => fetchServerPage(10, ctx.pageParam),\n  getNextPageParam: (_lastGroup, groups) => groups.length,\n})\n\nconst allRows = computed(() =>\n  data.value ? data.value.pages.flatMap((d) => d.rows) : [],\n)\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizerOptions = computed(() => {\n  return {\n    count: hasNextPage ? allRows.value.length + 1 : allRows.value.length,\n    getScrollElement: () => parentRef.value,\n    estimateSize: () => 100,\n    overscan: 5,\n  }\n})\n\nconst rowVirtualizer = useVirtualizer(rowVirtualizerOptions)\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n\nwatchEffect(() => {\n  const [lastItem] = [...virtualRows.value].reverse()\n\n  if (!lastItem) {\n    return\n  }\n\n  if (\n    lastItem.index >= allRows.value.length - 1 &&\n    hasNextPage.value &&\n    !isFetchingNextPage.value\n  ) {\n    fetchNextPage()\n  }\n})\n</script>\n\n<template>\n  <div>\n    <p>\n      This infinite scroll example uses Vue Query's useInfiniteScroll composable\n      to fetch infinite data from a posts endpoint and then a rowVirtualizer is\n      used along with a loader-row placed at the bottom of the list to trigger\n      the next page to load.\n    </p>\n\n    <br />\n    <br />\n    <p v-if=\"status === 'loading'\">Loading...</p>\n    <p v-else-if=\"status === 'error'\">Error: {{ (error as Error).message }}</p>\n    <div\n      v-else\n      ref=\"parentRef\"\n      class=\"List\"\n      style=\"height: 500px; width: 100%; overflow: auto\"\n    >\n      <div\n        :style=\"{\n          height: `${totalSize}px`,\n          width: '100%',\n          position: 'relative',\n        }\"\n      >\n        <div\n          v-for=\"virtualRow in virtualRows\"\n          :key=\"virtualRow.key\"\n          :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n          :style=\"{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            width: '100%',\n            height: `${virtualRow.size}px`,\n            transform: `translateY(${virtualRow.start}px)`,\n          }\"\n        >\n          <template v-if=\"virtualRow.index > allRows.length - 1\">\n            {{ hasNextPage ? 'Loading more...' : 'Nothing more to load' }}\n          </template>\n          <template v-else>\n            {{ allRows[virtualRow.index] }}\n          </template>\n        </div>\n      </div>\n    </div>\n    <div v-if=\"isFetching && !isFetchingNextPage\">Background Updating...</div>\n  </div>\n</template>\n"
  },
  {
    "path": "examples/vue/infinite-scroll/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport { VueQueryPlugin } from '@tanstack/vue-query'\nimport './style.css'\nimport App from './App.vue'\n\nconst app = createApp(App)\napp.use(VueQueryPlugin)\napp.mount('#app')\n"
  },
  {
    "path": "examples/vue/infinite-scroll/src/style.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/vue/infinite-scroll/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/infinite-scroll/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/infinite-scroll/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/infinite-scroll/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/padding/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/padding/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/padding/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/padding/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-padding\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/padding/src/App.vue",
    "content": "<template>\n  <div>\n    <p>\n      Examples with additional <strong>padding</strong> applied to the start and\n      the end of the scrolling area.\n    </p>\n    <br />\n    <br />\n\n    <h3>Rows</h3>\n    <RowVirtualizerPadding :rows=\"rows\" />\n    <br />\n    <br />\n    <h3>Columns</h3>\n    <ColumnVirtualizerPadding :columns=\"columns\" />\n    <br />\n    <br />\n    <h3>Grid</h3>\n    <GridVirtualizerPadding :rows=\"rows\" :columns=\"columns\" />\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nconst rows = new Array(10000)\n  .fill(true)\n  .map(() => 25 + Math.round(Math.random() * 100))\n\nconst columns = new Array(10000)\n  .fill(true)\n  .map(() => 75 + Math.round(Math.random() * 100))\n\nimport RowVirtualizerPadding from './components/RowVirtualizerPadding.vue'\nimport ColumnVirtualizerPadding from './components/ColumnVirtualizerPadding.vue'\nimport GridVirtualizerPadding from './components/GridVirtualizerPadding.vue'\n</script>\n"
  },
  {
    "path": "examples/vue/padding/src/components/ColumnVirtualizerPadding.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"width: 400px; height: 100px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        height: '100%',\n        width: `${totalSize}px`,\n        position: 'relative',\n      }\"\n    >\n      <div\n        v-for=\"virtualColumn in virtualColumns\"\n        :key=\"virtualColumn.index\"\n        :class=\"virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: `${virtualColumn.size}px`,\n          height: '100%',\n          transform: `translateX(${virtualColumn.start}px)`,\n        }\"\n      >\n        Column {{ virtualColumn.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  columns: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: props.columns.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 50,\n  paddingStart: 100,\n  paddingEnd: 100,\n})\n\nconst virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => columnVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/padding/src/components/GridVirtualizerPadding.vue",
    "content": "<template>\n  <button @click=\"show = !show\">Toggle</button>\n  <button @click=\"rowVirtualizer.scrollToIndex(halfWay)\">\n    Scroll to index {{ halfWay }}\n  </button>\n  <button @click=\"rowVirtualizer.scrollToIndex(rows.length - 1)\">\n    Scroll to index {{ rows.length - 1 }}\n  </button>\n  <br />\n  <br />\n\n  <div\n    v-if=\"show\"\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 400px; width: 500px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        height: `${totalSizeRows}px`,\n        width: `${totalSizeColumns}px`,\n        position: 'relative',\n      }\"\n    >\n      <template v-for=\"virtualRow in virtualRows\" :key=\"virtualRow.index\">\n        <div\n          v-for=\"virtualColumn in columnVirtualizer.getVirtualItems()\"\n          :key=\"virtualColumn.index\"\n          :class=\"\n            virtualColumn.index % 2\n              ? virtualRow.index % 2 === 0\n                ? 'ListItemOdd'\n                : 'ListItemEven'\n              : virtualRow.index % 2\n                ? 'ListItemOdd'\n                : 'ListItemEven'\n          \"\n          :style=\"{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            width: `${virtualColumn.size}px`,\n            height: `${virtualRow.size}px`,\n            transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,\n          }\"\n        >\n          Cell {{ virtualRow.index }}, {{ virtualColumn.index }}\n        </div>\n      </template>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  rows: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n  columns: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\nconst show = ref(true)\n\nconst halfWay = computed(() => Math.floor(props.rows.length / 2))\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: props.rows.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 50,\n  paddingStart: 200,\n  paddingEnd: 200,\n})\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: props.columns.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 50,\n  paddingStart: 200,\n  paddingEnd: 200,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\nconst totalSizeRows = computed(() => rowVirtualizer.value.getTotalSize())\n\nconst totalSizeColumns = computed(() => columnVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/padding/src/components/RowVirtualizerPadding.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 200px; width: 400px; overflow: auto\"\n  >\n    <div\n      :style=\"{ height: `${totalSize}px`, width: '100%', position: 'relative' }\"\n    >\n      <div\n        v-for=\"virtualRow in virtualRows\"\n        :key=\"virtualRow.index\"\n        :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: '100%',\n          height: `${virtualRow.size}px`,\n          transform: `translateY(${virtualRow.start}px)`,\n        }\"\n      >\n        Row {{ virtualRow.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  rows: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: props.rows.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: () => 50,\n  paddingStart: 100,\n  paddingEnd: 100,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/padding/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/padding/src/style.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/vue/padding/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/padding/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/padding/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/padding/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/scroll-padding/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/scroll-padding/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/scroll-padding/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/scroll-padding/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-scroll-padding\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"@vueuse/core\": \"^12.8.2\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/scroll-padding/src/App.vue",
    "content": "<template>\n  <div>\n    <div>\n      <button @click=\"rowVirtualizer.scrollToIndex(40)\">\n        Scroll to index 40\n      </button>\n      <button @click=\"rowVirtualizer.scrollToIndex(20)\">\n        Then scroll to index 20\n      </button>\n    </div>\n\n    <br />\n    <br />\n\n    <div\n      ref=\"parentRef\"\n      class=\"List\"\n      style=\"height: 200px; width: 400px; overflow: auto\"\n    >\n      <table :style=\"{ height: `${totalSize}px`, width: '100%' }\">\n        <thead ref=\"theadRef\">\n          <tr>\n            <th>Index</th>\n            <th>Key</th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr\n            v-for=\"virtualRow in virtualRows\"\n            :key=\"virtualRow.index\"\n            :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n            :style=\"{\n              position: 'absolute',\n              top: 0,\n              left: 0,\n              width: '100%',\n              height: `${virtualRow.size}px`,\n              transform: `translateY(${virtualRow.start}px)`,\n            }\"\n          >\n            <td>#{{ virtualRow.index }}</td>\n            <td>{{ virtualRow.key }}</td>\n          </tr>\n        </tbody>\n      </table>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport { useElementSize } from '@vueuse/core'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst theadRef = ref<HTMLElement | null>(null)\n\nconst { height } = useElementSize(theadRef)\n\nconst rowVirtualizerOptions = computed(() => {\n  return {\n    count: 10000,\n    getScrollElement: () => parentRef.value,\n    estimateSize: () => 35,\n    overscan: 5,\n    paddingStart: height.value ?? 0,\n    scrollPaddingStart: height.value ?? 0,\n  }\n})\n\nconst rowVirtualizer = useVirtualizer(rowVirtualizerOptions)\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/scroll-padding/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/scroll-padding/src/style.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\n.List table {\n  background-color: #fff;\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n  border-collapse: collapse;\n\n  display: flex;\n  flex-direction: column;\n  align-items: stretch;\n  position: relative;\n}\n\n.List thead {\n  display: flex;\n  flex-direction: column;\n  background-color: #fff;\n\n  position: sticky;\n  top: 0;\n  z-index: 1;\n}\n\n.List thead tr {\n  height: 70px;\n}\n\n.List tr {\n  display: flex;\n  flex-direction: row;\n}\n\n.List td,\n.List th {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  justify-content: center;\n  width: 180px;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n"
  },
  {
    "path": "examples/vue/scroll-padding/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/scroll-padding/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/scroll-padding/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/scroll-padding/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/smooth-scroll/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/smooth-scroll/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/smooth-scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/smooth-scroll/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-smooth-scroll\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/smooth-scroll/src/App.vue",
    "content": "<template>\n  <div>\n    <p>\n      This smooth scroll example uses the <code>`scrollToFn`</code> to implement\n      a custom scrolling function for the methods like\n      <code>`scrollToIndex`</code> and <code>`scrollToOffset`</code>\n    </p>\n\n    <br />\n    <br />\n\n    <div>\n      <button\n        @click=\"\n          (rowVirtualizer.scrollToIndex(randomIndex),\n          (randomIndex = generateRandomIndex()))\n        \"\n      >\n        Scroll To Random Index ({{ randomIndex }})\n      </button>\n    </div>\n\n    <br />\n    <br />\n\n    <div\n      ref=\"parentRef\"\n      class=\"List\"\n      :style=\"{\n        height: `200px`,\n        width: `400px`,\n        overflow: 'auto',\n      }\"\n    >\n      <div\n        :style=\"{\n          height: `${totalSize}px`,\n          width: '100%',\n          position: 'relative',\n        }\"\n      >\n        <div\n          v-for=\"virtualRow in virtualRows\"\n          :key=\"virtualRow.key\"\n          :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n          :style=\"{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            width: '100%',\n            height: `${virtualRow.size}px`,\n            transform: `translateY(${virtualRow.start}px)`,\n          }\"\n        >\n          Row {{ virtualRow.index }}\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport {\n  elementScroll,\n  useVirtualizer,\n  VirtualizerOptions,\n} from '@tanstack/vue-virtual'\n\nconst easeInOutQuint = (t: number) => {\n  return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t\n}\n\nconst generateRandomIndex = () => Math.floor(Math.random() * 10000)\n\nconst randomIndex = ref(generateRandomIndex())\n\nconst parentRef = ref<HTMLElement | null>(null)\nconst scrollingRef = ref<number>()\n\nconst scrollToFn: VirtualizerOptions<any, any>['scrollToFn'] = (\n  offset,\n  canSmooth,\n  instance,\n) => {\n  const duration = 1000\n  const start = parentRef.value?.scrollTop || 0\n  const startTime = (scrollingRef.value = Date.now())\n\n  const run = () => {\n    if (scrollingRef.value !== startTime) return\n    const now = Date.now()\n    const elapsed = now - startTime\n    const progress = easeInOutQuint(Math.min(elapsed / duration, 1))\n    const interpolated = start + (offset - start) * progress\n\n    if (elapsed < duration) {\n      elementScroll(interpolated, canSmooth, instance)\n      requestAnimationFrame(run)\n    } else {\n      elementScroll(interpolated, canSmooth, instance)\n    }\n  }\n\n  requestAnimationFrame(run)\n}\n\nconst rowVirtualizerOptions = computed(() => {\n  return {\n    count: 10000,\n    getScrollElement: () => parentRef.value,\n    estimateSize: () => 35,\n    overscan: 5,\n    scrollToFn,\n  }\n})\n\nconst rowVirtualizer = useVirtualizer(rowVirtualizerOptions)\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/smooth-scroll/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/smooth-scroll/src/style.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/vue/smooth-scroll/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/smooth-scroll/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/smooth-scroll/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/smooth-scroll/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/sticky/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/sticky/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/sticky/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/sticky/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-sticky\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"lodash\": \"^4.17.21\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@types/lodash\": \"^4.17.17\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/sticky/src/App.vue",
    "content": "<template>\n  <div>\n    <div\n      ref=\"parentRef\"\n      class=\"List\"\n      style=\"height: 300px; width: 400px; overflow: auto\"\n    >\n      <div\n        :style=\"{\n          height: `${totalSize}px`,\n          width: '100%',\n          position: 'relative',\n        }\"\n      >\n        <div\n          v-for=\"virtualRow in virtualRows\"\n          :key=\"virtualRow.index\"\n          :class=\"['ListItem', { Sticky: isSticky(virtualRow.index) }]\"\n          :style=\"{\n            ...(isSticky(virtualRow.index)\n              ? {\n                  background: '#fff',\n                  borderBottom: '1px solid #ddd',\n                  zIndex: 1,\n                }\n              : {}),\n            ...(isActiveSticky(virtualRow.index)\n              ? { position: 'sticky' }\n              : {\n                  position: 'absolute',\n                  transform: `translateY(${virtualRow.start}px)`,\n                }),\n            top: 0,\n            left: 0,\n            width: '100%',\n            height: `${virtualRow.size}px`,\n          }\"\n        >\n          {{ rows[virtualRow.index] }}\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { faker } from '@faker-js/faker'\nimport { findIndex, groupBy } from 'lodash'\nimport { useVirtualizer, defaultRangeExtractor } from '@tanstack/vue-virtual'\n\nconst groupedNames = groupBy(\n  Array.from({ length: 1000 })\n    .map(() => faker.person.firstName())\n    .sort(),\n  (name: string[]) => name[0],\n)\nconst groups = Object.keys(groupedNames)\n\nconst rows = groups.reduce<string[]>(\n  (acc, k) => [...acc, k, ...groupedNames[k]],\n  [],\n)\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst activeStickyIndexRef = ref(0)\n\nconst stickyIndexes = computed(() =>\n  groups.map((gn) => findIndex(rows, (n: string) => n === gn)),\n)\n\nconst isSticky = (index: number) => stickyIndexes.value.includes(index)\n\nconst isActiveSticky = (index: number) => activeStickyIndexRef.value === index\n\nconst rowVirtualizer = useVirtualizer({\n  count: rows.length,\n  estimateSize: () => 50,\n  getScrollElement: () => parentRef.value,\n  rangeExtractor: (range) => {\n    activeStickyIndexRef.value = [...stickyIndexes.value]\n      .reverse()\n      .find((index) => range.startIndex >= index)\n\n    const next = new Set([\n      activeStickyIndexRef.value,\n      ...defaultRangeExtractor(range),\n    ])\n\n    return [...next].sort((a, b) => a - b)\n  },\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/sticky/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/sticky/src/style.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/vue/sticky/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/sticky/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/sticky/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/sticky/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/table/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/table/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/table/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/table/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-table\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"^8.4.1\",\n    \"@tanstack/vue-table\": \"^8.21.3\",\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/table/src/App.vue",
    "content": "<template>\n  <div>\n    <p>\n      For tables, the basis for the offset of the translate css function is from\n      the row's initial position itself. Because of this, we need to calculate\n      the translateY pixel count differently and base it off the index.\n    </p>\n\n    <div ref=\"parentRef\" class=\"container\">\n      <div :style=\"{ height: `${totalSize}px` }\">\n        <table>\n          <thead>\n            <tr\n              v-for=\"headerGroup in table.getHeaderGroups()\"\n              :key=\"headerGroup.id\"\n            >\n              <th\n                v-for=\"header in headerGroup.headers\"\n                :key=\"header.id\"\n                :colspan=\"header.colSpan\"\n                :style=\"{ width: `${header.getSize()}px` }\"\n              >\n                <div\n                  v-if=\"!header.isPlaceholder\"\n                  :class=\"[\n                    'text-left',\n                    header.column.getCanSort()\n                      ? 'cursor-pointer select-none'\n                      : '',\n                  ]\"\n                  @click=\"\n                    getSortingHandler(\n                      $event,\n                      header.column.getToggleSortingHandler(),\n                    )\n                  \"\n                >\n                  <FlexRender\n                    :render=\"header.column.columnDef.header\"\n                    :props=\"header.getContext()\"\n                  />\n                  <span v-if=\"header.column.getIsSorted() === 'asc'\"> 🔼</span>\n                  <span v-if=\"header.column.getIsSorted() === 'desc'\"> 🔽</span>\n                </div>\n              </th>\n            </tr>\n          </thead>\n          <tbody>\n            <tr\n              v-for=\"(virtualRow, index) in virtualRows\"\n              :key=\"virtualRow.key\"\n              :style=\"{\n                transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`,\n              }\"\n            >\n              <td\n                v-for=\"cell in rows[virtualRow.index].getVisibleCells()\"\n                :key=\"cell.id\"\n              >\n                <FlexRender\n                  :render=\"cell.column.columnDef.cell\"\n                  :props=\"cell.getContext()\"\n                />\n              </td>\n            </tr>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\nimport {\n  FlexRender,\n  ColumnDef,\n  SortingState,\n  useVueTable,\n  getCoreRowModel,\n  getSortedRowModel,\n} from '@tanstack/vue-table'\nimport { makeData, Person } from './makeData'\n\nconst data = ref(makeData(50_000))\n\nconst sorting = ref<SortingState>([])\n\nconst getSortingHandler = (e: Event, fn: any) => {\n  return fn(e)\n}\n\nconst setSorting = (sortingUpdater: any) => {\n  const newSortVal = sortingUpdater(sorting.value)\n\n  sorting.value = newSortVal\n}\n\nconst columns = computed<ColumnDef<Person>[]>(() => {\n  return [\n    {\n      accessorKey: 'id',\n      header: 'ID',\n      size: 60,\n    },\n    {\n      accessorKey: 'firstName',\n      cell: (info) => info.getValue(),\n    },\n    {\n      accessorFn: (row) => row.lastName,\n      id: 'lastName',\n      cell: (info) => info.getValue(),\n      header: () => 'Last Name',\n    },\n    {\n      accessorKey: 'age',\n      header: () => 'Age',\n      size: 50,\n    },\n    {\n      accessorKey: 'visits',\n      header: () => 'Visits',\n      size: 50,\n    },\n    {\n      accessorKey: 'status',\n      header: 'Status',\n    },\n    {\n      accessorKey: 'progress',\n      header: 'Profile Progress',\n      size: 80,\n    },\n    {\n      accessorKey: 'createdAt',\n      header: 'Created At',\n      cell: (info) => info.getValue<Date>().toLocaleString(),\n    },\n  ]\n})\n\nconst table = useVueTable({\n  get data() {\n    return data.value\n  },\n  columns: columns.value,\n  state: {\n    get sorting() {\n      return sorting.value\n    },\n  },\n  onSortingChange: setSorting,\n  getCoreRowModel: getCoreRowModel(),\n  getSortedRowModel: getSortedRowModel(),\n  debugTable: true,\n})\n\nconst rows = computed(() => {\n  return table.getRowModel().rows\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizerOptions = computed(() => {\n  return {\n    count: rows.value.length,\n    getScrollElement: () => parentRef.value,\n    estimateSize: () => 34,\n    overscan: 5,\n  }\n})\n\nconst rowVirtualizer = useVirtualizer(rowVirtualizerOptions)\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/table/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/table/src/makeData.ts",
    "content": "import { faker } from '@faker-js/faker'\n\nexport type Person = {\n  id: number\n  firstName: string\n  lastName: string\n  age: number\n  visits: number\n  progress: number\n  status: 'relationship' | 'complicated' | 'single'\n  createdAt: Date\n}\n\nconst range = (len: number) => {\n  const arr: number[] = []\n  for (let i = 0; i < len; i++) {\n    arr.push(i)\n  }\n  return arr\n}\n\nconst newPerson = (index: number): Person => {\n  return {\n    id: index + 1,\n    firstName: faker.person.firstName(),\n    lastName: faker.person.lastName(),\n    age: faker.number.int(40),\n    visits: faker.number.int(1000),\n    progress: faker.number.int(100),\n    createdAt: faker.datatype.datetime({ max: new Date().getTime() }),\n    status: faker.helpers.shuffle<Person['status']>([\n      'relationship',\n      'complicated',\n      'single',\n    ])[0]!,\n  }\n}\n\nexport function makeData(...lens: number[]) {\n  const makeDataLevel = (depth = 0): Person[] => {\n    const len = lens[depth]!\n    return range(len).map((d): Person => {\n      return {\n        ...newPerson(d),\n      }\n    })\n  }\n\n  return makeDataLevel()\n}\n"
  },
  {
    "path": "examples/vue/table/src/style.css",
    "content": "*,\n*:before,\n*:after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.container {\n  height: 600px;\n  overflow: auto;\n}\n\n.cursor-pointer {\n  cursor: pointer;\n}\n\n.select-none {\n  user-select: none;\n}\n\n.text-left {\n  text-align: left;\n}\n"
  },
  {
    "path": "examples/vue/table/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/table/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/table/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/table/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "examples/vue/variable/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/vue/variable/README.md",
    "content": "# Example\n\nTo run this example:\n\n- `npm install` or `yarn`\n- `npm run dev` or `yarn dev`\n"
  },
  {
    "path": "examples/vue/variable/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + Vue + TS</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/vue/variable/package.json",
    "content": "{\n  \"name\": \"tanstack-vue-virtual-example-variable\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@tanstack/vue-virtual\": \"^3.13.23\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"devDependencies\": {\n    \"@codesandbox/vue-preview\": \"^0.1.1-alpha.16\",\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vue-tsc\": \"^2.2.10\"\n  }\n}\n"
  },
  {
    "path": "examples/vue/variable/src/App.vue",
    "content": "<template>\n  <div>\n    <p>\n      These components are using <strong>variable</strong> sizes. This means\n      that each element has a unique, but knowable dimension at render time.\n    </p>\n    <br />\n    <br />\n\n    <h3>Rows</h3>\n    <RowVirtualizerVariable :rows=\"rows\" />\n    <br />\n    <br />\n    <h3>Columns</h3>\n    <ColumnVirtualizerVariable :columns=\"columns\" />\n    <br />\n    <br />\n    <h3>Grid</h3>\n    <GridVirtualizerVariable :rows=\"rows\" :columns=\"columns\" />\n    <br />\n    <br />\n    <h3>Masonry (vertical)</h3>\n    <MasonryVerticalVirtualizerVariable :rows=\"rows\" />\n    <br />\n    <br />\n    <h3>Masonry (horizontal)</h3>\n    <MasonryHorizontalVirtualizerVariable :columns=\"columns\" />\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport RowVirtualizerVariable from './components/RowVirtualizerVariable.vue'\nimport ColumnVirtualizerVariable from './components/ColumnVirtualizerVariable.vue'\nimport GridVirtualizerVariable from './components/GridVirtualizerVariable.vue'\nimport MasonryVerticalVirtualizerVariable from './components/MasonryVerticalVirtualizerVariable.vue'\nimport MasonryHorizontalVirtualizerVariable from './components/MasonryHorizontalVirtualizerVariable.vue'\n\nconst rows = new Array(10000)\n  .fill(true)\n  .map(() => 25 + Math.round(Math.random() * 100))\n\nconst columns = new Array(10000)\n  .fill(true)\n  .map(() => 75 + Math.round(Math.random() * 100))\n</script>\n"
  },
  {
    "path": "examples/vue/variable/src/components/ColumnVirtualizerVariable.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"width: 400px; height: 100px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        width: `${totalSize}px`,\n        height: '100%',\n        position: 'relative',\n      }\"\n    >\n      <div\n        v-for=\"virtualColumn in virtualColumns\"\n        :key=\"virtualColumn.index\"\n        :class=\"virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          height: '100%',\n          width: `${columns[virtualColumn.index]}px`,\n          transform: `translateX(${virtualColumn.start}px)`,\n        }\"\n      >\n        Column {{ virtualColumn.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  columns: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: props.columns.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: (i) => props.columns[i],\n  overscan: 5,\n})\n\nconst virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => columnVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/variable/src/components/GridVirtualizerVariable.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 400px; width: 500px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        height: `${totalSizeRows}px`,\n        width: `${totalSizeColumns}px`,\n        position: 'relative',\n      }\"\n    >\n      <template v-for=\"virtualRow in virtualRows\" :key=\"virtualRow.index\">\n        <div\n          v-for=\"virtualColumn in virtualColumns\"\n          :key=\"virtualColumn.index\"\n          :class=\"\n            virtualColumn.index % 2\n              ? virtualRow.index % 2 === 0\n                ? 'ListItemOdd'\n                : 'ListItemEven'\n              : virtualRow.index % 2\n                ? 'ListItemOdd'\n                : 'ListItemEven'\n          \"\n          :style=\"{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            width: `${columns[virtualColumn.index]}px`,\n            height: `${rows[virtualRow.index]}px`,\n            transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,\n          }\"\n        >\n          Cell {{ virtualRow.index }}, {{ virtualColumn.index }}\n        </div>\n      </template>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  rows: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n  columns: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: props.rows.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: (i) => props.rows[i],\n  overscan: 5,\n})\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: props.columns.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: (i) => props.columns[i],\n  overscan: 5,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\nconst totalSizeRows = computed(() => rowVirtualizer.value.getTotalSize())\n\nconst virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems())\nconst totalSizeColumns = computed(() => columnVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/variable/src/components/MasonryHorizontalVirtualizerVariable.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"width: 500px; height: 400px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        width: `${totalSize}px`,\n        height: '100%',\n        position: 'relative',\n      }\"\n    >\n      <div\n        v-for=\"virtualColumn in virtualColumns\"\n        :key=\"virtualColumn.index\"\n        :class=\"virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: `${virtualColumn.lane * 25}%`,\n          left: 0,\n          height: '25%',\n          width: `${columns[virtualColumn.index]}px`,\n          transform: `translateX(${virtualColumn.start}px)`,\n        }\"\n      >\n        Column {{ virtualColumn.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  columns: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst columnVirtualizer = useVirtualizer({\n  horizontal: true,\n  count: props.columns.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: (i) => props.columns[i],\n  overscan: 5,\n  lanes: 4,\n})\n\nconst virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems())\n\nconst totalSize = computed(() => columnVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/variable/src/components/MasonryVerticalVirtualizerVariable.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 200px; width: 400px; overflow: auto\"\n  >\n    <div\n      :style=\"{\n        height: `${totalSize}px`,\n        width: '100%',\n        position: 'relative',\n      }\"\n    >\n      <div\n        v-for=\"virtualRow in virtualRows\"\n        :key=\"virtualRow.index\"\n        :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: `${virtualRow.lane * 25}%`,\n          width: '25%',\n          height: `${rows[virtualRow.index]}px`,\n          transform: `translateY(${virtualRow.start}px)`,\n        }\"\n      >\n        Row {{ virtualRow.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  rows: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: props.rows.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: (i) => props.rows[i],\n  overscan: 5,\n  lanes: 4,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/variable/src/components/RowVirtualizerVariable.vue",
    "content": "<template>\n  <div\n    ref=\"parentRef\"\n    class=\"List\"\n    style=\"height: 200px; width: 400px; overflow: auto\"\n  >\n    <div\n      :style=\"{ height: `${totalSize}px`, width: '100%', position: 'relative' }\"\n    >\n      <div\n        v-for=\"virtualRow in virtualRows\"\n        :key=\"virtualRow.index\"\n        :class=\"virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'\"\n        :style=\"{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: '100%',\n          height: `${virtualRow.size}px`,\n          transform: `translateY(${virtualRow.start}px)`,\n        }\"\n      >\n        Row {{ virtualRow.index }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, PropType } from 'vue'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\n\nconst props = defineProps({\n  rows: {\n    type: Array as PropType<number[]>,\n    default: () => [],\n  },\n})\n\nconst parentRef = ref<HTMLElement | null>(null)\n\nconst rowVirtualizer = useVirtualizer({\n  count: props.rows.length,\n  getScrollElement: () => parentRef.value,\n  estimateSize: (i) => props.rows[i],\n  overscan: 5,\n})\n\nconst virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())\nconst totalSize = computed(() => rowVirtualizer.value.getTotalSize())\n</script>\n"
  },
  {
    "path": "examples/vue/variable/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport './style.css'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "examples/vue/variable/src/style.css",
    "content": "html {\n  font-family: sans-serif;\n  font-size: 14px;\n}\n\nbody {\n  padding: 1rem;\n}\n\n.List {\n  border: 1px solid #e6e4dc;\n  max-width: 100%;\n}\n\n.ListItemEven,\n.ListItemOdd {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ListItemEven {\n  background-color: #e6e4dc;\n}\n\nbutton {\n  border: 1px solid gray;\n}\n"
  },
  {
    "path": "examples/vue/variable/src/vite-env.d.ts",
    "content": "// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/vue/variable/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"skipLibCheck\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/vue/variable/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vue/variable/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "knip.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/knip@5/schema.json\",\n  \"ignoreWorkspaces\": [\"examples/**\", \"packages/react-virtual/e2e/**\"]\n}\n"
  },
  {
    "path": "nx.json",
    "content": "{\n  \"$schema\": \"./node_modules/nx/schemas/nx-schema.json\",\n  \"defaultBase\": \"main\",\n  \"nxCloudId\": \"65d1330edb0c1e7def5cb068\",\n  \"useInferencePlugins\": false,\n  \"parallel\": 5,\n  \"tui\": {\n    \"enabled\": false\n  },\n  \"namedInputs\": {\n    \"sharedGlobals\": [\n      \"{workspaceRoot}/.nvmrc\",\n      \"{workspaceRoot}/package.json\",\n      \"{workspaceRoot}/tsconfig.json\"\n    ],\n    \"default\": [\n      \"sharedGlobals\",\n      \"{projectRoot}/**/*\",\n      \"!{projectRoot}/**/*.md\"\n    ],\n    \"production\": [\n      \"default\",\n      \"!{projectRoot}/tests/**/*\",\n      \"!{projectRoot}/eslint.config.js\"\n    ]\n  },\n  \"targetDefaults\": {\n    \"test:docs\": {\n      \"cache\": true,\n      \"inputs\": [\"{workspaceRoot}/docs/**/*\"]\n    },\n    \"test:knip\": {\n      \"cache\": true,\n      \"inputs\": [\"{workspaceRoot}/**/*\"]\n    },\n    \"test:sherif\": {\n      \"cache\": true,\n      \"inputs\": [\"{workspaceRoot}/**/package.json\"]\n    },\n    \"test:eslint\": {\n      \"cache\": true,\n      \"dependsOn\": [\"^build\"],\n      \"inputs\": [\"default\", \"^production\", \"{workspaceRoot}/eslint.config.js\"]\n    },\n    \"test:lib\": {\n      \"cache\": true,\n      \"dependsOn\": [\"^build\"],\n      \"inputs\": [\"default\", \"^production\"],\n      \"outputs\": [\"{projectRoot}/coverage\"]\n    },\n    \"test:types\": {\n      \"cache\": true,\n      \"dependsOn\": [\"^build\"],\n      \"inputs\": [\"default\", \"^production\"]\n    },\n    \"build\": {\n      \"cache\": true,\n      \"dependsOn\": [\"^build\"],\n      \"inputs\": [\"production\", \"^production\"],\n      \"outputs\": [\"{projectRoot}/build\", \"{projectRoot}/dist\"]\n    },\n    \"test:build\": {\n      \"cache\": true,\n      \"dependsOn\": [\"build\"],\n      \"inputs\": [\"production\"]\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"root\",\n  \"private\": true,\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\"\n  },\n  \"packageManager\": \"pnpm@10.24.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"clean\": \"pnpm --filter \\\"./packages/**\\\" run clean\",\n    \"test\": \"pnpm run test:ci\",\n    \"test:pr\": \"nx affected --targets=test:sherif,test:knip,test:docs,test:eslint,test:lib,test:e2e,test:types,test:build,build\",\n    \"test:ci\": \"nx run-many --targets=test:sherif,test:knip,test:docs,test:eslint,test:lib,test:e2e,test:types,test:build,build\",\n    \"test:eslint\": \"nx affected --target=test:eslint\",\n    \"test:sherif\": \"sherif\",\n    \"test:lib\": \"nx affected --target=test:lib --exclude=examples/**\",\n    \"test:lib:dev\": \"pnpm run test:lib && nx watch --all -- pnpm run test:lib\",\n    \"test:build\": \"nx affected --target=test:build --exclude=examples/**\",\n    \"test:types\": \"nx affected --target=test:types --exclude=examples/**\",\n    \"test:e2e\": \"nx affected --target=test:e2e --exclude=examples/**\",\n    \"test:knip\": \"knip\",\n    \"test:docs\": \"node scripts/verify-links.ts\",\n    \"build\": \"nx affected --target=build --exclude=examples/**\",\n    \"build:all\": \"nx run-many --target=build --exclude=examples/**\",\n    \"watch\": \"pnpm run build:all && nx watch --all -- pnpm run build:all\",\n    \"dev\": \"pnpm run watch\",\n    \"format\": \"prettier --experimental-cli --ignore-unknown '**/*' --write\",\n    \"changeset\": \"changeset\",\n    \"changeset:version\": \"changeset version && pnpm install --no-frozen-lockfile && pnpm format\",\n    \"changeset:publish\": \"changeset publish\"\n  },\n  \"nx\": {\n    \"includedScripts\": [\n      \"test:docs\",\n      \"test:knip\",\n      \"test:sherif\"\n    ]\n  },\n  \"devDependencies\": {\n    \"@changesets/cli\": \"^2.30.0\",\n    \"@playwright/test\": \"^1.53.1\",\n    \"@svitejs/changesets-changelog-github-compact\": \"^1.2.0\",\n    \"@tanstack/eslint-config\": \"0.3.4\",\n    \"@tanstack/vite-config\": \"0.3.0\",\n    \"@testing-library/jest-dom\": \"^6.6.3\",\n    \"@types/node\": \"^24.5.2\",\n    \"eslint\": \"^9.36.0\",\n    \"jsdom\": \"^27.0.0\",\n    \"knip\": \"^5.63.1\",\n    \"markdown-link-extractor\": \"^4.0.2\",\n    \"nx\": \"22.1.3\",\n    \"premove\": \"^4.0.0\",\n    \"prettier\": \"^3.7.4\",\n    \"prettier-plugin-svelte\": \"^3.4.0\",\n    \"publint\": \"^0.3.15\",\n    \"sherif\": \"^1.9.0\",\n    \"tinyglobby\": \"^0.2.15\",\n    \"typescript\": \"5.4.5\",\n    \"vite\": \"^5.4.19\",\n    \"vitest\": \"^2.1.9\"\n  }\n}\n"
  },
  {
    "path": "packages/angular-virtual/CHANGELOG.md",
    "content": "# @tanstack/angular-virtual\n\n## 4.0.11\n\n### Patch Changes\n\n- Updated dependencies [[`7ece2d5`](https://github.com/TanStack/virtual/commit/7ece2d5d4249b7e703c68ac497ae5545c54e7c67)]:\n  - @tanstack/virtual-core@3.13.23\n\n## 4.0.10\n\n### Patch Changes\n\n- Updated dependencies [[`54d771a`](https://github.com/TanStack/virtual/commit/54d771a7d4c74f6968e8132b5a85f3e04682376a), [`d3416c3`](https://github.com/TanStack/virtual/commit/d3416c386c6446957f413db2eef3211f5fdf3b5f)]:\n  - @tanstack/virtual-core@3.13.22\n\n## 4.0.9\n\n### Patch Changes\n\n- Updated dependencies [[`be89e29`](https://github.com/TanStack/virtual/commit/be89e293ea01654df6334dc6473b65eebed13e51)]:\n  - @tanstack/virtual-core@3.13.21\n\n## 4.0.8\n\n### Patch Changes\n\n- Updated dependencies [[`ff83e94`](https://github.com/TanStack/virtual/commit/ff83e949408ba8a714436fa10cafc3725a56274b)]:\n  - @tanstack/virtual-core@3.13.20\n\n## 4.0.7\n\n### Patch Changes\n\n- Updated dependencies [[`843109c`](https://github.com/TanStack/virtual/commit/843109c5bf780591a762f9767f3808fd15e3f94e)]:\n  - @tanstack/virtual-core@3.13.19\n\n## 4.0.6\n\n### Patch Changes\n\n- Updated dependencies [[`9067574`](https://github.com/TanStack/virtual/commit/9067574f1a0178d30e27bcac70853bdcbf437fec)]:\n  - @tanstack/virtual-core@3.13.18\n\n## 4.0.5\n\n### Patch Changes\n\n- Updated dependencies [[`21d9a46`](https://github.com/TanStack/virtual/commit/21d9a46eac034cb4299872891694965bceed526d)]:\n  - @tanstack/virtual-core@3.13.17\n\n## 4.0.4\n\n### Patch Changes\n\n- Updated dependencies [[`db6df21`](https://github.com/TanStack/virtual/commit/db6df212ed83dd7e4eb6450d1340c95475667b7b)]:\n  - @tanstack/virtual-core@3.13.16\n\n## 4.0.3\n\n### Patch Changes\n\n- Updated dependencies [[`5a273bf`](https://github.com/TanStack/virtual/commit/5a273bf0c0bc0255ca172929f021c3b6e50cb69d)]:\n  - @tanstack/virtual-core@3.13.15\n\n## 4.0.2\n\n### Patch Changes\n\n- Updated dependencies [[`6d9274c`](https://github.com/TanStack/virtual/commit/6d9274c3f0a9e64450b5829872079a65277bc654)]:\n  - @tanstack/virtual-core@3.13.14\n\n## 4.0.1\n\n### Patch Changes\n\n- Fix: Notify framework when count changes to update getTotalSize() ([#1085](https://github.com/TanStack/virtual/pull/1085))\n\n  Fixed an issue where `getTotalSize()` would return stale values when the `count` option changed (e.g., during filtering or search operations). The virtualizer now automatically notifies the framework when measurement-affecting options change, ensuring the UI updates correctly without requiring manual `useMemo` workarounds.\n\n  **Before**: When filtering items, the list container would maintain its previous height, causing excessive blank space (when count decreased) or inaccessible items (when count increased).\n\n  **After**: Height updates automatically when count changes, providing the correct user experience.\n\n  This fix applies to all framework adapters and has minimal performance impact (< 0.1ms per change).\n\n- Updated dependencies [[`2542c5a`](https://github.com/TanStack/virtual/commit/2542c5a3d6820cea956fa3b4f94c42e3526a8d68), [`96e32a6`](https://github.com/TanStack/virtual/commit/96e32a6ffc125743a0172ea4e0fe37ac29c4187b)]:\n  - @tanstack/virtual-core@3.13.13\n\n## 4.0.0\n\n### Major Changes\n\n- feat(angular-virtual): add support for angular 20 ([#1040](https://github.com/TanStack/virtual/pull/1040))\n\n  BREAKING CHANGE: minimum Angular version is now 18.1.0\n\n## 3.13.12\n\n### Patch Changes\n\n- Updated dependencies [[`d21ed98`](https://github.com/TanStack/virtual/commit/d21ed98da3470b9986c9a028ed70fdf0d6189ab4)]:\n  - @tanstack/virtual-core@3.13.12\n\n## 3.13.11\n\n### Patch Changes\n\n- Updated dependencies [[`73fa867`](https://github.com/TanStack/virtual/commit/73fa86752599a4bffba51ec8e4ff2f8cb8283010)]:\n  - @tanstack/virtual-core@3.13.11\n\n## 3.13.10\n\n### Patch Changes\n\n- Updated dependencies [[`b3b7e7d`](https://github.com/TanStack/virtual/commit/b3b7e7dc8b25daeebbd2da61b3b7ae3448babbdb)]:\n  - @tanstack/virtual-core@3.13.10\n\n## 3.13.9\n\n### Patch Changes\n\n- Updated dependencies [[`9e33cdb`](https://github.com/TanStack/virtual/commit/9e33cdb1c8780c2f455aafc11a0aeea58b71fc69)]:\n  - @tanstack/virtual-core@3.13.9\n\n## 3.13.8\n\n### Patch Changes\n\n- Updated dependencies [[`60719f6`](https://github.com/TanStack/virtual/commit/60719f61b589d6f9d886e4f7c093217f6d693faf)]:\n  - @tanstack/virtual-core@3.13.8\n\n## 3.13.7\n\n### Patch Changes\n\n- Updated dependencies [[`e2d93c2`](https://github.com/TanStack/virtual/commit/e2d93c2dcde9ccf60f658e56edccd8d05aefeee6)]:\n  - @tanstack/virtual-core@3.13.7\n\n## 3.13.6\n\n### Patch Changes\n\n- Updated dependencies [[`042616f`](https://github.com/TanStack/virtual/commit/042616f39ced842470db0b4b40fca77f22454b7f)]:\n  - @tanstack/virtual-core@3.13.6\n\n## 3.13.5\n\n### Patch Changes\n\n- Updated dependencies [[`51656d9`](https://github.com/TanStack/virtual/commit/51656d94a2469a065e631f25ffc8ec0288d9f5ec)]:\n  - @tanstack/virtual-core@3.13.5\n\n## 3.13.4\n\n### Patch Changes\n\n- Updated dependencies [[`514b62d`](https://github.com/TanStack/virtual/commit/514b62d04974c2fd59fc8a68ed40f4c1a1547dd2), [`f03d814`](https://github.com/TanStack/virtual/commit/f03d8142c03ea0f5816161a4dad38ca35469841c)]:\n  - @tanstack/virtual-core@3.13.4\n\n## 3.13.3\n\n### Patch Changes\n\n- Updated dependencies [[`02ef309`](https://github.com/TanStack/virtual/commit/02ef3097de4a14ed4077ace2ca901dc411bf81c1)]:\n  - @tanstack/virtual-core@3.13.3\n"
  },
  {
    "path": "packages/angular-virtual/README.md",
    "content": "# Angular Virtual\n\nEfficiently virtualize only the visible DOM nodes within massive scrollable elements using Angular, while maintaining complete control over markup and styles.\n\n# Quick Start\n\n> NOTE: Angular Virtual requires Angular 17.\n\n1. Install `@tanstack/angular-virtual`\n\n   ```bash\n   $ npm i @tanstack/angular-virtual\n   ```\n\n   or\n\n   ```bash\n   $ pnpm add @tanstack/angular-virtual\n   ```\n\n   or\n\n   ```bash\n   $ yarn add @tanstack/angular-virtual\n   ```\n\n   or\n\n   ```bash\n   $ bun add @tanstack/angular-virtual\n   ```\n\n2. Inject a virtualizer\n\n   `@tanstack/angular-virtual` utilizes a helper function `injectVirtualizer` to create the virtualizer and integrate it with the component lifecycle:\n\n   ```ts\n   import { Component, ElementRef, viewChild } from '@angular/core'\n   import { injectVirtualizer } from '@tanstack/angular-virtual'\n\n   @Component({\n     selector: 'my-virtualized-list',\n     template: `\n       <div\n         #scrollElement\n         style=\"height: 400px; border: 1px solid gray; overflow: auto;\"\n       >\n         <div\n           style=\"position: relative; width: 100%;\"\n           [style.height.px]=\"virtualizer.getTotalSize()\"\n         >\n           @for (row of virtualizer.getVirtualItems(); track row.index) {\n             <div\n               style=\"position: absolute; top: 0; left: 0; width: 100%; height: 35px\"\n               [style.transform]=\"'translateY(' + row.start + 'px)'\"\n             >\n               Row {{ row.index }}\n             </div>\n           }\n         </div>\n       </div>\n     `,\n   })\n   export class MyVirtualizedList {\n     scrollElement = viewChild<ElementRef<HTMLDivElement>>('scrollElement')\n\n     virtualizer = injectVirtualizer(() => ({\n       scrollElement: this.scrollElement(),\n       count: 1000,\n       estimateSize: () => 35,\n       overscan: 5,\n     }))\n   }\n   ```\n\n   Note that a [ViewChild](https://angular.dev/api/core/viewChild) is used to get a reference to the scrolling container to allow the virtualizer to interact with it. The adapter will automatically unwrap the [ElementRef](https://angular.dev/api/core/ElementRef) for you.\n\n   You can also create a virtualizer that attaches to the Window with `injectWindowVirtualizer`:\n\n   ```ts\n   import { Component } from '@angular/core'\n   import { injectWindowVirtualizer } from '@tanstack/angular-virtual'\n\n   @Component({\n     selector: 'my-window-virtualized-list',\n     template: `\n       <div\n         style=\"position: relative; width: 100%;\"\n         [style.height.px]=\"virtualizer.getTotalSize()\"\n       >\n         @for (row of virtualizer.getVirtualItems(); track row.index) {\n           <div\n             style=\"position: absolute; top: 0; left: 0; width: 100%; height: 35px\"\n             [style.transform]=\"'translateY(' + row.start + 'px)'\"\n           >\n             Row {{ row.index }}\n           </div>\n         }\n       </div>\n     `,\n   })\n   export class MyWindowVirtualizedList {\n     virtualizer = injectWindowVirtualizer(() => ({\n       count: 1000,\n       estimateSize: () => 35,\n       overscan: 5,\n     }))\n   }\n   ```\n\n3. If you need to update options on your virtualizer dynamically, make sure to use signals.\n\n   ```ts\n   import { Component, input } from '@angular/core'\n   import { injectVirtualizer } from '@tanstack/angular-virtual'\n\n   @Component({...})\n   export class MyVirtualizedList {\n     items = input<Array<string>>()\n\n     virtualizer = injectVirtualizer(() => ({\n       scrollElement: this.scrollElement(),\n       count: this.items().length,\n       estimateSize: () => 35,\n       overscan: 5,\n     }))\n   }\n   ```\n\nFor more examples and detailed usage, visit the [official documentation](https://tanstack.com/virtual/latest).\n"
  },
  {
    "path": "packages/angular-virtual/eslint.config.js",
    "content": "// @ts-check\n\nimport rootConfig from '../../eslint.config.js'\n\nexport default [...rootConfig]\n"
  },
  {
    "path": "packages/angular-virtual/ng-package.json",
    "content": "{\n  \"$schema\": \"./node_modules/ng-packagr/ng-package.schema.json\",\n  \"lib\": {\n    \"entryFile\": \"src/index.ts\"\n  },\n  \"allowedNonPeerDependencies\": [\"@tanstack/virtual-core\"],\n  \"dest\": \"build\",\n  \"deleteDestPath\": false\n}\n"
  },
  {
    "path": "packages/angular-virtual/package.json",
    "content": "{\n  \"name\": \"@tanstack/angular-virtual\",\n  \"version\": \"4.0.11\",\n  \"description\": \"Headless UI for virtualizing scrollable elements in Angular\",\n  \"author\": \"Garrett Darnell\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\",\n    \"directory\": \"packages/angular-virtual\"\n  },\n  \"homepage\": \"https://tanstack.com/virtual\",\n  \"funding\": {\n    \"type\": \"github\",\n    \"url\": \"https://github.com/sponsors/tannerlinsley\"\n  },\n  \"keywords\": [\n    \"angular\",\n    \"virtual\",\n    \"virtual-core\"\n  ],\n  \"type\": \"module\",\n  \"module\": \"build/fesm2022/tanstack-angular-virtual.mjs\",\n  \"types\": \"build/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./build/index.d.ts\",\n      \"esm2022\": \"./build/esm2022/tanstack-angular-virtual.mjs\",\n      \"esm\": \"./build/esm2022/tanstack-angular-virtual.mjs\",\n      \"default\": \"./build/fesm2022/tanstack-angular-virtual.mjs\"\n    },\n    \"./package.json\": {\n      \"default\": \"./package.json\"\n    }\n  },\n  \"engines\": {\n    \"node\": \">=12\"\n  },\n  \"files\": [\n    \"build\"\n  ],\n  \"scripts\": {\n    \"clean\": \"premove ./build\",\n    \"test:types\": \"tsc --noEmit\",\n    \"test:eslint\": \"eslint ./src\",\n    \"build\": \"ng-packagr -p ng-package.json -c tsconfig.build.json\"\n  },\n  \"dependencies\": {\n    \"@tanstack/virtual-core\": \"workspace:*\",\n    \"tslib\": \"^2.8.1\"\n  },\n  \"devDependencies\": {\n    \"@angular/core\": \"^18.1.0\",\n    \"ng-packagr\": \"^18.1.0\",\n    \"typescript\": \"5.4.5\"\n  },\n  \"peerDependencies\": {\n    \"@angular/core\": \">=18.1.0\"\n  },\n  \"sideEffects\": false\n}\n"
  },
  {
    "path": "packages/angular-virtual/src/index.ts",
    "content": "import {\n  DestroyRef,\n  afterNextRender,\n  computed,\n  effect,\n  inject,\n  signal,\n  untracked,\n} from '@angular/core'\nimport {\n  Virtualizer,\n  elementScroll,\n  observeElementOffset,\n  observeElementRect,\n  observeWindowOffset,\n  observeWindowRect,\n  windowScroll,\n} from '@tanstack/virtual-core'\nimport { proxyVirtualizer } from './proxy'\nimport type { ElementRef, Signal } from '@angular/core'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\nimport type { AngularVirtualizer } from './types'\n\nexport * from '@tanstack/virtual-core'\nexport * from './types'\n\nfunction createVirtualizerBase<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n>(\n  options: Signal<VirtualizerOptions<TScrollElement, TItemElement>>,\n): AngularVirtualizer<TScrollElement, TItemElement> {\n  let virtualizer: Virtualizer<TScrollElement, TItemElement>\n  function lazyInit() {\n    virtualizer ??= new Virtualizer(options())\n    return virtualizer\n  }\n\n  const virtualizerSignal = signal(virtualizer!, { equal: () => false })\n\n  // two-way sync options\n  effect(\n    () => {\n      const _options = options()\n      lazyInit()\n      virtualizerSignal.set(virtualizer)\n      virtualizer.setOptions({\n        ..._options,\n        onChange: (instance, sync) => {\n          // update virtualizerSignal so that dependent computeds recompute.\n          virtualizerSignal.set(instance)\n          _options.onChange?.(instance, sync)\n        },\n      })\n      // update virtualizerSignal so that dependent computeds recompute.\n      virtualizerSignal.set(virtualizer)\n    },\n    { allowSignalWrites: true },\n  )\n\n  const scrollElement = computed(() => options().getScrollElement())\n  // let the virtualizer know when the scroll element is changed\n  effect(\n    () => {\n      const el = scrollElement()\n      if (el) {\n        untracked(virtualizerSignal)._willUpdate()\n      }\n    },\n    { allowSignalWrites: true },\n  )\n\n  let cleanup: () => void | undefined\n  afterNextRender({ read: () => (virtualizer ?? lazyInit())._didMount() })\n\n  inject(DestroyRef).onDestroy(() => cleanup?.())\n\n  return proxyVirtualizer(virtualizerSignal, lazyInit)\n}\n\nexport function injectVirtualizer<\n  TScrollElement extends Element,\n  TItemElement extends Element,\n>(\n  options: () => PartialKeys<\n    Omit<VirtualizerOptions<TScrollElement, TItemElement>, 'getScrollElement'>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  > & {\n    scrollElement: ElementRef<TScrollElement> | TScrollElement | undefined\n  },\n): AngularVirtualizer<TScrollElement, TItemElement> {\n  const resolvedOptions = computed(() => {\n    return {\n      observeElementRect: observeElementRect,\n      observeElementOffset: observeElementOffset,\n      scrollToFn: elementScroll,\n      getScrollElement: () => {\n        const elementOrRef = options().scrollElement\n        return (\n          (isElementRef(elementOrRef)\n            ? elementOrRef.nativeElement\n            : elementOrRef) ?? null\n        )\n      },\n      ...options(),\n    }\n  })\n  return createVirtualizerBase<TScrollElement, TItemElement>(resolvedOptions)\n}\n\nfunction isElementRef<T extends Element>(\n  elementOrRef: ElementRef<T> | T | undefined,\n): elementOrRef is ElementRef<T> {\n  return elementOrRef != null && 'nativeElement' in elementOrRef\n}\n\nexport function injectWindowVirtualizer<TItemElement extends Element>(\n  options: () => PartialKeys<\n    VirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): AngularVirtualizer<Window, TItemElement> {\n  const resolvedOptions = computed(() => {\n    return {\n      getScrollElement: () => (typeof document !== 'undefined' ? window : null),\n      observeElementRect: observeWindowRect,\n      observeElementOffset: observeWindowOffset,\n      scrollToFn: windowScroll,\n      initialOffset: () =>\n        typeof document !== 'undefined' ? window.scrollY : 0,\n      ...options(),\n    }\n  })\n  return createVirtualizerBase<Window, TItemElement>(resolvedOptions)\n}\n"
  },
  {
    "path": "packages/angular-virtual/src/proxy.ts",
    "content": "import { computed, untracked } from '@angular/core'\nimport type { Signal, WritableSignal } from '@angular/core'\nimport type { Virtualizer } from '@tanstack/virtual-core'\nimport type { AngularVirtualizer } from './types'\n\nexport function proxyVirtualizer<\n  V extends Virtualizer<any, any>,\n  S extends Element | Window = V extends Virtualizer<infer U, any> ? U : never,\n  I extends Element = V extends Virtualizer<any, infer U> ? U : never,\n>(\n  virtualizerSignal: WritableSignal<V>,\n  lazyInit: () => V,\n): AngularVirtualizer<S, I> {\n  return new Proxy(virtualizerSignal, {\n    apply() {\n      return virtualizerSignal()\n    },\n    get(target, property) {\n      const untypedTarget = target as any\n      if (untypedTarget[property]) {\n        return untypedTarget[property]\n      }\n      let virtualizer = untracked(virtualizerSignal)\n      if (virtualizer == null) {\n        virtualizer = lazyInit()\n        untracked(() => virtualizerSignal.set(virtualizer))\n      }\n\n      // Create computed signals for each property that represents a reactive value\n      if (\n        typeof property === 'string' &&\n        [\n          'getTotalSize',\n          'getVirtualItems',\n          'isScrolling',\n          'options',\n          'range',\n          'scrollDirection',\n          'scrollElement',\n          'scrollOffset',\n          'scrollRect',\n          'measureElementCache',\n          'measurementsCache',\n        ].includes(property)\n      ) {\n        const isFunction =\n          typeof virtualizer[property as keyof V] === 'function'\n        Object.defineProperty(untypedTarget, property, {\n          value: isFunction\n            ? computed(() => (target()[property as keyof V] as Function)())\n            : computed(() => target()[property as keyof V]),\n          configurable: true,\n          enumerable: true,\n        })\n      }\n\n      // Create plain signals for functions that accept arguments and return reactive values\n      if (\n        typeof property === 'string' &&\n        [\n          'getOffsetForAlignment',\n          'getOffsetForIndex',\n          'getVirtualItemForOffset',\n          'indexFromElement',\n        ].includes(property)\n      ) {\n        const fn = virtualizer[property as keyof V] as Function\n        Object.defineProperty(untypedTarget, property, {\n          value: toComputed(virtualizerSignal, fn),\n          configurable: true,\n          enumerable: true,\n        })\n      }\n\n      return untypedTarget[property] || virtualizer[property as keyof V]\n    },\n    has(_, property: string) {\n      return !!untracked(virtualizerSignal)[property as keyof V]\n    },\n    ownKeys() {\n      return Reflect.ownKeys(untracked(virtualizerSignal))\n    },\n    getOwnPropertyDescriptor() {\n      return {\n        enumerable: true,\n        configurable: true,\n      }\n    },\n  }) as unknown as AngularVirtualizer<S, I>\n}\n\nfunction toComputed<V extends Virtualizer<any, any>>(\n  signal: Signal<V>,\n  fn: Function,\n) {\n  const computedCache: Record<string, Signal<unknown>> = {}\n\n  return (...args: Array<any>) => {\n    // Cache computeds by their arguments to avoid re-creating the computed on each call\n    const serializedArgs = serializeArgs(...args)\n    if (computedCache.hasOwnProperty(serializedArgs)) {\n      return computedCache[serializedArgs]?.()\n    }\n    const computedSignal = computed(() => {\n      void signal()\n      return fn(...args)\n    })\n\n    computedCache[serializedArgs] = computedSignal\n\n    return computedSignal()\n  }\n}\n\nfunction serializeArgs(...args: Array<any>) {\n  return JSON.stringify(args)\n}\n"
  },
  {
    "path": "packages/angular-virtual/src/types.ts",
    "content": "import type { Signal } from '@angular/core'\nimport type { Virtualizer } from '@tanstack/virtual-core'\n\nexport type AngularVirtualizer<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n> = Omit<\n  Virtualizer<TScrollElement, TItemElement>,\n  | 'getTotalSize'\n  | 'getVirtualItems'\n  | 'isScrolling'\n  | 'options'\n  | 'range'\n  | 'scrollDirection'\n  | 'scrollElement'\n  | 'scrollOffset'\n  | 'scrollRect'\n> & {\n  getTotalSize: Signal<\n    ReturnType<Virtualizer<TScrollElement, TItemElement>['getTotalSize']>\n  >\n  getVirtualItems: Signal<\n    ReturnType<Virtualizer<TScrollElement, TItemElement>['getVirtualItems']>\n  >\n  isScrolling: Signal<Virtualizer<TScrollElement, TItemElement>['isScrolling']>\n  options: Signal<Virtualizer<TScrollElement, TItemElement>['options']>\n  range: Signal<Virtualizer<TScrollElement, TItemElement>['range']>\n  scrollDirection: Signal<\n    Virtualizer<TScrollElement, TItemElement>['scrollDirection']\n  >\n  scrollElement: Signal<\n    Virtualizer<TScrollElement, TItemElement>['scrollElement']\n  >\n  scrollOffset: Signal<\n    Virtualizer<TScrollElement, TItemElement>['scrollOffset']\n  >\n  scrollRect: Signal<Virtualizer<TScrollElement, TItemElement>['scrollRect']>\n}\n"
  },
  {
    "path": "packages/angular-virtual/tsconfig.build.json",
    "content": "{\n  \"extends\": \"./node_modules/ng-packagr/lib/ts/conf/tsconfig.ngc.json\",\n  \"compilerOptions\": {\n    \"moduleResolution\": \"bundler\",\n    \"allowJs\": true,\n    \"moduleDetection\": \"force\",\n    \"module\": \"ESNext\"\n  },\n  \"angularCompilerOptions\": {\n    \"enableI18nLegacyMessageIdFormat\": false,\n    \"strictInjectionParameters\": true,\n    \"strictInputAccessModifiers\": true\n  },\n  \"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/angular-virtual/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"./build/lib\"\n  },\n  \"angularCompilerOptions\": {\n    \"compilationMode\": \"partial\"\n  },\n  \"include\": [\"src\", \"eslint.config.js\"],\n  \"exclude\": [\"**/*.spec.ts\"]\n}\n"
  },
  {
    "path": "packages/lit-virtual/CHANGELOG.md",
    "content": "# @tanstack/lit-virtual\n\n## 3.13.24\n\n### Patch Changes\n\n- Updated dependencies [[`7ece2d5`](https://github.com/TanStack/virtual/commit/7ece2d5d4249b7e703c68ac497ae5545c54e7c67)]:\n  - @tanstack/virtual-core@3.13.23\n\n## 3.13.23\n\n### Patch Changes\n\n- Updated dependencies [[`54d771a`](https://github.com/TanStack/virtual/commit/54d771a7d4c74f6968e8132b5a85f3e04682376a), [`d3416c3`](https://github.com/TanStack/virtual/commit/d3416c386c6446957f413db2eef3211f5fdf3b5f)]:\n  - @tanstack/virtual-core@3.13.22\n\n## 3.13.22\n\n### Patch Changes\n\n- Updated dependencies [[`be89e29`](https://github.com/TanStack/virtual/commit/be89e293ea01654df6334dc6473b65eebed13e51)]:\n  - @tanstack/virtual-core@3.13.21\n\n## 3.13.21\n\n### Patch Changes\n\n- Updated dependencies [[`ff83e94`](https://github.com/TanStack/virtual/commit/ff83e949408ba8a714436fa10cafc3725a56274b)]:\n  - @tanstack/virtual-core@3.13.20\n\n## 3.13.20\n\n### Patch Changes\n\n- Updated dependencies [[`843109c`](https://github.com/TanStack/virtual/commit/843109c5bf780591a762f9767f3808fd15e3f94e)]:\n  - @tanstack/virtual-core@3.13.19\n\n## 3.13.19\n\n### Patch Changes\n\n- Updated dependencies [[`9067574`](https://github.com/TanStack/virtual/commit/9067574f1a0178d30e27bcac70853bdcbf437fec)]:\n  - @tanstack/virtual-core@3.13.18\n\n## 3.13.18\n\n### Patch Changes\n\n- Updated dependencies [[`21d9a46`](https://github.com/TanStack/virtual/commit/21d9a46eac034cb4299872891694965bceed526d)]:\n  - @tanstack/virtual-core@3.13.17\n\n## 3.13.17\n\n### Patch Changes\n\n- Updated dependencies [[`db6df21`](https://github.com/TanStack/virtual/commit/db6df212ed83dd7e4eb6450d1340c95475667b7b)]:\n  - @tanstack/virtual-core@3.13.16\n\n## 3.13.16\n\n### Patch Changes\n\n- Updated dependencies [[`5a273bf`](https://github.com/TanStack/virtual/commit/5a273bf0c0bc0255ca172929f021c3b6e50cb69d)]:\n  - @tanstack/virtual-core@3.13.15\n\n## 3.13.15\n\n### Patch Changes\n\n- Updated dependencies [[`6d9274c`](https://github.com/TanStack/virtual/commit/6d9274c3f0a9e64450b5829872079a65277bc654)]:\n  - @tanstack/virtual-core@3.13.14\n\n## 3.13.14\n\n### Patch Changes\n\n- Fix: Notify framework when count changes to update getTotalSize() ([#1085](https://github.com/TanStack/virtual/pull/1085))\n\n  Fixed an issue where `getTotalSize()` would return stale values when the `count` option changed (e.g., during filtering or search operations). The virtualizer now automatically notifies the framework when measurement-affecting options change, ensuring the UI updates correctly without requiring manual `useMemo` workarounds.\n\n  **Before**: When filtering items, the list container would maintain its previous height, causing excessive blank space (when count decreased) or inaccessible items (when count increased).\n\n  **After**: Height updates automatically when count changes, providing the correct user experience.\n\n  This fix applies to all framework adapters and has minimal performance impact (< 0.1ms per change).\n\n- Updated dependencies [[`2542c5a`](https://github.com/TanStack/virtual/commit/2542c5a3d6820cea956fa3b4f94c42e3526a8d68), [`96e32a6`](https://github.com/TanStack/virtual/commit/96e32a6ffc125743a0172ea4e0fe37ac29c4187b)]:\n  - @tanstack/virtual-core@3.13.13\n\n## 3.13.13\n\n### Patch Changes\n\n- fix(lit-virtual): create Virtualizer instance before hostConnected ([#1061](https://github.com/TanStack/virtual/pull/1061))\n\n  When creating an instance of the reactive controller in `connectedCallback`, calling `addController` will synchronously call `hostConnected` on the controller. This means that `this.virtualizer` will still be `undefined`.\n\n## 3.13.12\n\n### Patch Changes\n\n- Updated dependencies [[`d21ed98`](https://github.com/TanStack/virtual/commit/d21ed98da3470b9986c9a028ed70fdf0d6189ab4)]:\n  - @tanstack/virtual-core@3.13.12\n\n## 3.13.11\n\n### Patch Changes\n\n- Updated dependencies [[`73fa867`](https://github.com/TanStack/virtual/commit/73fa86752599a4bffba51ec8e4ff2f8cb8283010)]:\n  - @tanstack/virtual-core@3.13.11\n\n## 3.13.10\n\n### Patch Changes\n\n- Updated dependencies [[`b3b7e7d`](https://github.com/TanStack/virtual/commit/b3b7e7dc8b25daeebbd2da61b3b7ae3448babbdb)]:\n  - @tanstack/virtual-core@3.13.10\n\n## 3.13.9\n\n### Patch Changes\n\n- Updated dependencies [[`9e33cdb`](https://github.com/TanStack/virtual/commit/9e33cdb1c8780c2f455aafc11a0aeea58b71fc69)]:\n  - @tanstack/virtual-core@3.13.9\n\n## 3.13.8\n\n### Patch Changes\n\n- Updated dependencies [[`60719f6`](https://github.com/TanStack/virtual/commit/60719f61b589d6f9d886e4f7c093217f6d693faf)]:\n  - @tanstack/virtual-core@3.13.8\n\n## 3.13.7\n\n### Patch Changes\n\n- Updated dependencies [[`e2d93c2`](https://github.com/TanStack/virtual/commit/e2d93c2dcde9ccf60f658e56edccd8d05aefeee6)]:\n  - @tanstack/virtual-core@3.13.7\n\n## 3.13.6\n\n### Patch Changes\n\n- Updated dependencies [[`042616f`](https://github.com/TanStack/virtual/commit/042616f39ced842470db0b4b40fca77f22454b7f)]:\n  - @tanstack/virtual-core@3.13.6\n\n## 3.13.5\n\n### Patch Changes\n\n- Updated dependencies [[`51656d9`](https://github.com/TanStack/virtual/commit/51656d94a2469a065e631f25ffc8ec0288d9f5ec)]:\n  - @tanstack/virtual-core@3.13.5\n\n## 3.13.4\n\n### Patch Changes\n\n- Updated dependencies [[`514b62d`](https://github.com/TanStack/virtual/commit/514b62d04974c2fd59fc8a68ed40f4c1a1547dd2), [`f03d814`](https://github.com/TanStack/virtual/commit/f03d8142c03ea0f5816161a4dad38ca35469841c)]:\n  - @tanstack/virtual-core@3.13.4\n\n## 3.13.3\n\n### Patch Changes\n\n- Updated dependencies [[`02ef309`](https://github.com/TanStack/virtual/commit/02ef3097de4a14ed4077ace2ca901dc411bf81c1)]:\n  - @tanstack/virtual-core@3.13.3\n"
  },
  {
    "path": "packages/lit-virtual/README.md",
    "content": "# @tanstack/lit-virtual\n\nEfficiently virtualize only the visible DOM nodes within massive scrollable elements using Lit, while maintaining complete control over markup and styles.\n\n## `VirtualizerController`\n\n`@tanstack/lit-virtual` utilizes [Reactive Controllers](https://lit.dev/docs/composition/controllers/) to create the virtualizer and integrate it with the element lifecycle:\n\n```ts\nimport { LitElement } from 'lit'\nimport { VirtualizerController } from '@tanstack/lit-virtual'\nimport { Ref, createRef } from 'lit/directives/ref.js'\n\nclass MyVirtualElement extends LitElement {\n  private virtualizerController: VirtualizerController<HTMLDivElement, Element>\n  private scrollElementRef: Ref<HTMLDivElement> = createRef()\n\n  constructor() {\n    super()\n    this.virtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count: 10000,\n      estimateSize: () => 35,\n      overscan: 5,\n    })\n  }\n\n  render() {\n    const virtualizer = this.virtualizerController.getVirtualizer()\n    const virtualItems = virtualizer.getVirtualItems()\n\n    return html`\n      <div class=\"list scroll-container\" ${ref(this.scrollElementRef)}>\n        ${virtualItems.map(\n          (item) => html`<div class=\"item\">${item.index}</div>`,\n        )}\n      </div>\n    `\n  }\n}\n```\n\nNote that a [Ref](https://lit.dev/docs/templates/directives/#ref) is attached to the scrolling container to allow the virtualizer to interact with it.\n\n## `WindowVirtualizerController`\n\nYou can also create a virtualizer controller that attaches to the Window:\n\n```ts\nimport { WindowVirtualizerController } from '@tanstack/lit-virtual'\n\nclass MyWindowVirtualElement extends LitElement {\n  private windowVirtualizerController: WindowVirtualizerController\n\n  constructor() {\n    super()\n    this.windowVirtualizerController = new WindowVirtualizerController(this, {\n      count: this.data.length,\n      estimateSize: () => 350,\n      overscan: 5,\n    })\n  }\n\n  // Implement render and other lifecycle methods as needed\n}\n```\n\nFor more examples and detailed usage, visit the [official documentation](https://tanstack.com/virtual/latest).\n"
  },
  {
    "path": "packages/lit-virtual/eslint.config.js",
    "content": "// @ts-check\n\nimport rootConfig from '../../eslint.config.js'\n\nexport default [...rootConfig]\n"
  },
  {
    "path": "packages/lit-virtual/package.json",
    "content": "{\n  \"name\": \"@tanstack/lit-virtual\",\n  \"version\": \"3.13.24\",\n  \"description\": \"Headless UI for virtualizing scrollable elements in Lit\",\n  \"author\": \"Tanner Linsley\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\",\n    \"directory\": \"packages/lit-virtual\"\n  },\n  \"homepage\": \"https://tanstack.com/virtual\",\n  \"funding\": {\n    \"type\": \"github\",\n    \"url\": \"https://github.com/sponsors/tannerlinsley\"\n  },\n  \"keywords\": [\n    \"lit\",\n    \"virtual\",\n    \"virtual-core\",\n    \"datagrid\"\n  ],\n  \"scripts\": {\n    \"clean\": \"premove ./dist ./coverage\",\n    \"test:eslint\": \"eslint ./src\",\n    \"test:types\": \"tsc\",\n    \"test:build\": \"publint --strict\",\n    \"test:lib\": \"vitest\",\n    \"test:lib:dev\": \"pnpm run test:lib --watch\",\n    \"build\": \"vite build\"\n  },\n  \"type\": \"module\",\n  \"types\": \"dist/esm/index.d.ts\",\n  \"main\": \"dist/cjs/index.cjs\",\n  \"module\": \"dist/esm/index.js\",\n  \"exports\": {\n    \".\": {\n      \"import\": {\n        \"types\": \"./dist/esm/index.d.ts\",\n        \"default\": \"./dist/esm/index.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/cjs/index.d.cts\",\n        \"default\": \"./dist/cjs/index.cjs\"\n      }\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"dependencies\": {\n    \"@tanstack/virtual-core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@open-wc/testing\": \"^4.0.0\",\n    \"lit\": \"^3.3.0\"\n  },\n  \"peerDependencies\": {\n    \"lit\": \"^3.1.0\"\n  }\n}\n"
  },
  {
    "path": "packages/lit-virtual/src/index.ts",
    "content": "import {\n  Virtualizer,\n  elementScroll,\n  observeElementOffset,\n  observeElementRect,\n  observeWindowOffset,\n  observeWindowRect,\n  windowScroll,\n} from '@tanstack/virtual-core'\nimport type { ReactiveController, ReactiveControllerHost } from 'lit'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\n\nclass VirtualizerControllerBase<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n> implements ReactiveController {\n  host: ReactiveControllerHost\n\n  private readonly virtualizer: Virtualizer<TScrollElement, TItemElement>\n\n  private cleanup: () => void = () => {}\n\n  constructor(\n    host: ReactiveControllerHost,\n    options: VirtualizerOptions<TScrollElement, TItemElement>,\n  ) {\n    const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> = {\n      ...options,\n      onChange: (instance, sync) => {\n        this.host.updateComplete.then(() => this.host.requestUpdate())\n        options.onChange?.(instance, sync)\n      },\n    }\n    this.virtualizer = new Virtualizer(resolvedOptions)\n    ;(this.host = host).addController(this)\n  }\n\n  public getVirtualizer() {\n    return this.virtualizer\n  }\n\n  hostConnected() {\n    this.cleanup = this.virtualizer._didMount()\n  }\n\n  hostUpdated() {\n    this.virtualizer._willUpdate()\n  }\n\n  hostDisconnected() {\n    this.cleanup()\n  }\n}\n\nexport class VirtualizerController<\n  TScrollElement extends Element,\n  TItemElement extends Element,\n> extends VirtualizerControllerBase<TScrollElement, TItemElement> {\n  constructor(\n    host: ReactiveControllerHost,\n    options: PartialKeys<\n      VirtualizerOptions<TScrollElement, TItemElement>,\n      'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n    >,\n  ) {\n    super(host, {\n      observeElementRect: observeElementRect,\n      observeElementOffset: observeElementOffset,\n      scrollToFn: elementScroll,\n      ...options,\n    })\n  }\n}\n\nexport class WindowVirtualizerController<\n  TItemElement extends Element,\n> extends VirtualizerControllerBase<Window, TItemElement> {\n  constructor(\n    host: ReactiveControllerHost,\n    options: PartialKeys<\n      VirtualizerOptions<Window, TItemElement>,\n      | 'getScrollElement'\n      | 'observeElementRect'\n      | 'observeElementOffset'\n      | 'scrollToFn'\n    >,\n  ) {\n    super(host, {\n      getScrollElement: () => (typeof document !== 'undefined' ? window : null),\n      observeElementRect: observeWindowRect,\n      observeElementOffset: observeWindowOffset,\n      scrollToFn: windowScroll,\n      initialOffset: () =>\n        typeof document !== 'undefined' ? window.scrollY : 0,\n      ...options,\n    })\n  }\n}\n"
  },
  {
    "path": "packages/lit-virtual/tests/index.test.ts",
    "content": "import { describe, test, expect, beforeEach } from 'vitest'\nimport { elementUpdated, fixture, html, waitUntil } from '@open-wc/testing'\nimport { LitElement } from 'lit'\nimport { VirtualizerController } from '../src'\nimport { createRef, ref, Ref } from 'lit/directives/ref.js'\nimport { repeat } from 'lit/directives/repeat.js'\nimport { customElement } from 'lit/decorators.js'\n\nconst width = 400\nconst height = 400\nconst count = 50\n\n@customElement('test-list')\nclass List extends LitElement {\n  private scrollElementRef: Ref<HTMLDivElement> = createRef()\n\n  private virtualizerController: VirtualizerController<HTMLDivElement, Element>\n\n  constructor() {\n    super()\n    this.virtualizerController = new VirtualizerController(this, {\n      getScrollElement: () => this.scrollElementRef.value,\n      count,\n      estimateSize: () => 50,\n      observeElementRect: (_, cb) => {\n        cb({ height, width })\n      },\n    })\n  }\n\n  render() {\n    const virtualizer = this.virtualizerController.getVirtualizer()\n    const virtualRows = virtualizer.getVirtualItems()\n\n    return html`\n      <div>\n        <button\n          @click=${() => {\n            virtualizer.scrollToIndex(count - 1)\n            console.log('scrolled down!')\n          }}\n        >\n          scroll to the end\n        </button>\n        <div class=\"list scroll-container\" ${ref(this.scrollElementRef)}>\n          <div\n            style=\"position: relative; height: ${virtualizer.getTotalSize()}px; width: 100%;\"\n          >\n            <div\n              style=\"position:absolute;top:0;left:0;width:100%;transform:translateY(${virtualRows[0]\n                ? virtualRows[0].start\n                : 0}px);\"\n            >\n              ${repeat(\n                virtualRows,\n                (virtualRow) => virtualRow.key,\n                (virtualRow) =>\n                  html` <div\n                    data-index=\"${virtualRow.index}\"\n                    ${ref(virtualizer.measureElement)}\n                    class=\"${virtualRow.index % 2 === 0\n                      ? 'list-item-even'\n                      : 'list-item-odd'}\"\n                  >\n                    <div style=\"padding: 10px 0;\">\n                      <div>Row ${virtualRow.index}</div>\n                      <div>Item ${virtualRow.index}</div>\n                    </div>\n                  </div>`,\n              )}\n            </div>\n          </div>\n        </div>\n      </div>\n      <style>\n        .list {\n          border: 1px solid #e6e4dc;\n          max-width: 100%;\n        }\n\n        .list-item-even,\n        .list-item-odd {\n          display: flex;\n          align-items: center;\n          justify-content: center;\n        }\n\n        .list-item-even {\n          background-color: #e6e4dc;\n        }\n\n        .scroll-container {\n          height: ${height}px;\n          width: ${width}px;\n          overflow-y: auto;\n        }\n      </style>\n    `\n  }\n}\n\ntest('should render', async () => {\n  const el = await fixture(html`<test-list></test-list>`)\n  await elementUpdated(el)\n  expect(el).toBeTruthy()\n})\n\ntest('should render virtual items', async () => {\n  const el = await fixture(html`<test-list></test-list>`)\n  await elementUpdated(el)\n  await waitUntil(\n    () => el.shadowRoot.querySelector('[data-index=\"4\"]'),\n    'Element did not render virtual items',\n  )\n})\n"
  },
  {
    "path": "packages/lit-virtual/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true\n  },\n  \"include\": [\"src\", \"eslint.config.js\", \"vite.config.ts\"]\n}\n"
  },
  {
    "path": "packages/lit-virtual/vite.config.ts",
    "content": "import { defineConfig, mergeConfig } from 'vitest/config'\nimport { tanstackViteConfig } from '@tanstack/vite-config'\nimport packageJson from './package.json'\n\nconst config = defineConfig({\n  test: {\n    name: packageJson.name,\n    dir: './tests',\n    watch: false,\n    environment: 'jsdom',\n  },\n})\n\nexport default mergeConfig(\n  config,\n  tanstackViteConfig({\n    entry: './src/index.ts',\n    srcDir: './src',\n  }),\n)\n"
  },
  {
    "path": "packages/react-virtual/CHANGELOG.md",
    "content": "# @tanstack/react-virtual\n\n## 3.13.23\n\n### Patch Changes\n\n- Updated dependencies [[`7ece2d5`](https://github.com/TanStack/virtual/commit/7ece2d5d4249b7e703c68ac497ae5545c54e7c67)]:\n  - @tanstack/virtual-core@3.13.23\n\n## 3.13.22\n\n### Patch Changes\n\n- Updated dependencies [[`54d771a`](https://github.com/TanStack/virtual/commit/54d771a7d4c74f6968e8132b5a85f3e04682376a), [`d3416c3`](https://github.com/TanStack/virtual/commit/d3416c386c6446957f413db2eef3211f5fdf3b5f)]:\n  - @tanstack/virtual-core@3.13.22\n\n## 3.13.21\n\n### Patch Changes\n\n- Updated dependencies [[`be89e29`](https://github.com/TanStack/virtual/commit/be89e293ea01654df6334dc6473b65eebed13e51)]:\n  - @tanstack/virtual-core@3.13.21\n\n## 3.13.20\n\n### Patch Changes\n\n- Updated dependencies [[`ff83e94`](https://github.com/TanStack/virtual/commit/ff83e949408ba8a714436fa10cafc3725a56274b)]:\n  - @tanstack/virtual-core@3.13.20\n\n## 3.13.19\n\n### Patch Changes\n\n- Updated dependencies [[`843109c`](https://github.com/TanStack/virtual/commit/843109c5bf780591a762f9767f3808fd15e3f94e)]:\n  - @tanstack/virtual-core@3.13.19\n\n## 3.13.18\n\n### Patch Changes\n\n- Updated dependencies [[`9067574`](https://github.com/TanStack/virtual/commit/9067574f1a0178d30e27bcac70853bdcbf437fec)]:\n  - @tanstack/virtual-core@3.13.18\n\n## 3.13.17\n\n### Patch Changes\n\n- Updated dependencies [[`21d9a46`](https://github.com/TanStack/virtual/commit/21d9a46eac034cb4299872891694965bceed526d)]:\n  - @tanstack/virtual-core@3.13.17\n\n## 3.13.16\n\n### Patch Changes\n\n- Updated dependencies [[`db6df21`](https://github.com/TanStack/virtual/commit/db6df212ed83dd7e4eb6450d1340c95475667b7b)]:\n  - @tanstack/virtual-core@3.13.16\n\n## 3.13.15\n\n### Patch Changes\n\n- feat(react-virtual): add `useFlushSync` option ([#1100](https://github.com/TanStack/virtual/pull/1100))\n\n  Adds a React-specific `useFlushSync` option to control whether `flushSync` is used for synchronous scroll correction during measurement.\n\n  The default behavior remains unchanged (`useFlushSync: true`) to preserve the best scrolling experience.\n  Disabling it avoids the React 19 warning about calling `flushSync` during render, at the cost of potentially increased visible whitespace during fast scrolling with dynamically sized items.\n\n- Updated dependencies [[`5a273bf`](https://github.com/TanStack/virtual/commit/5a273bf0c0bc0255ca172929f021c3b6e50cb69d)]:\n  - @tanstack/virtual-core@3.13.15\n\n## 3.13.14\n\n### Patch Changes\n\n- Updated dependencies [[`6d9274c`](https://github.com/TanStack/virtual/commit/6d9274c3f0a9e64450b5829872079a65277bc654)]:\n  - @tanstack/virtual-core@3.13.14\n\n## 3.13.13\n\n### Patch Changes\n\n- Fix: Notify framework when count changes to update getTotalSize() ([#1085](https://github.com/TanStack/virtual/pull/1085))\n\n  Fixed an issue where `getTotalSize()` would return stale values when the `count` option changed (e.g., during filtering or search operations). The virtualizer now automatically notifies the framework when measurement-affecting options change, ensuring the UI updates correctly without requiring manual `useMemo` workarounds.\n\n  **Before**: When filtering items, the list container would maintain its previous height, causing excessive blank space (when count decreased) or inaccessible items (when count increased).\n\n  **After**: Height updates automatically when count changes, providing the correct user experience.\n\n  This fix applies to all framework adapters and has minimal performance impact (< 0.1ms per change).\n\n- Updated dependencies [[`2542c5a`](https://github.com/TanStack/virtual/commit/2542c5a3d6820cea956fa3b4f94c42e3526a8d68), [`96e32a6`](https://github.com/TanStack/virtual/commit/96e32a6ffc125743a0172ea4e0fe37ac29c4187b)]:\n  - @tanstack/virtual-core@3.13.13\n\n## 3.13.12\n\n### Patch Changes\n\n- chore(react-virtual): fix vite e2e build ([#1030](https://github.com/TanStack/virtual/pull/1030))\n\n- Updated dependencies [[`d21ed98`](https://github.com/TanStack/virtual/commit/d21ed98da3470b9986c9a028ed70fdf0d6189ab4)]:\n  - @tanstack/virtual-core@3.13.12\n\n## 3.13.11\n\n### Patch Changes\n\n- Updated dependencies [[`73fa867`](https://github.com/TanStack/virtual/commit/73fa86752599a4bffba51ec8e4ff2f8cb8283010)]:\n  - @tanstack/virtual-core@3.13.11\n\n## 3.13.10\n\n### Patch Changes\n\n- Updated dependencies [[`b3b7e7d`](https://github.com/TanStack/virtual/commit/b3b7e7dc8b25daeebbd2da61b3b7ae3448babbdb)]:\n  - @tanstack/virtual-core@3.13.10\n\n## 3.13.9\n\n### Patch Changes\n\n- Updated dependencies [[`9e33cdb`](https://github.com/TanStack/virtual/commit/9e33cdb1c8780c2f455aafc11a0aeea58b71fc69)]:\n  - @tanstack/virtual-core@3.13.9\n\n## 3.13.8\n\n### Patch Changes\n\n- Updated dependencies [[`60719f6`](https://github.com/TanStack/virtual/commit/60719f61b589d6f9d886e4f7c093217f6d693faf)]:\n  - @tanstack/virtual-core@3.13.8\n\n## 3.13.7\n\n### Patch Changes\n\n- Updated dependencies [[`e2d93c2`](https://github.com/TanStack/virtual/commit/e2d93c2dcde9ccf60f658e56edccd8d05aefeee6)]:\n  - @tanstack/virtual-core@3.13.7\n\n## 3.13.6\n\n### Patch Changes\n\n- Updated dependencies [[`042616f`](https://github.com/TanStack/virtual/commit/042616f39ced842470db0b4b40fca77f22454b7f)]:\n  - @tanstack/virtual-core@3.13.6\n\n## 3.13.5\n\n### Patch Changes\n\n- Updated dependencies [[`51656d9`](https://github.com/TanStack/virtual/commit/51656d94a2469a065e631f25ffc8ec0288d9f5ec)]:\n  - @tanstack/virtual-core@3.13.5\n\n## 3.13.4\n\n### Patch Changes\n\n- Updated dependencies [[`514b62d`](https://github.com/TanStack/virtual/commit/514b62d04974c2fd59fc8a68ed40f4c1a1547dd2), [`f03d814`](https://github.com/TanStack/virtual/commit/f03d8142c03ea0f5816161a4dad38ca35469841c)]:\n  - @tanstack/virtual-core@3.13.4\n\n## 3.13.3\n\n### Patch Changes\n\n- Updated dependencies [[`02ef309`](https://github.com/TanStack/virtual/commit/02ef3097de4a14ed4077ace2ca901dc411bf81c1)]:\n  - @tanstack/virtual-core@3.13.3\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/measure-element/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/measure-element/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\ninterface Item {\n  id: string\n  label: string\n}\n\nconst INITIAL_ITEMS: Array<Item> = [\n  { id: 'item-a', label: 'A' },\n  { id: 'item-b', label: 'B' },\n  { id: 'item-c', label: 'C' },\n]\n\nconst App = () => {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n  const [items, setItems] = React.useState(INITIAL_ITEMS)\n  const [expandedId, setExpandedId] = React.useState<string | null>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: items.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 36,\n    getItemKey: (index) => items[index].id,\n  })\n\n  const toggleExpand = (id: string) => {\n    setExpandedId((prev) => (prev === id ? null : id))\n  }\n\n  const deleteItem = (id: string) => {\n    setItems((prev) => prev.filter((item) => item.id !== id))\n    if (expandedId === id) {\n      setExpandedId(null)\n    }\n  }\n\n  return (\n    <div>\n      <div\n        ref={parentRef}\n        id=\"scroll-container\"\n        style={{ height: 400, overflow: 'auto' }}\n      >\n        <div\n          style={{\n            height: rowVirtualizer.getTotalSize(),\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((v) => {\n            const item = items[v.index]\n            const isExpanded = expandedId === item.id\n\n            return (\n              <div\n                key={item.id}\n                data-testid={item.id}\n                ref={rowVirtualizer.measureElement}\n                data-index={v.index}\n                style={{\n                  position: 'absolute',\n                  top: 0,\n                  left: 0,\n                  transform: `translateY(${v.start}px)`,\n                  width: '100%',\n                }}\n              >\n                <div\n                  style={{\n                    display: 'flex',\n                    gap: 8,\n                    alignItems: 'center',\n                    padding: 4,\n                  }}\n                >\n                  <span>Row {item.label}</span>\n                  <button\n                    data-testid={`expand-${item.id}`}\n                    onClick={() => toggleExpand(item.id)}\n                  >\n                    {isExpanded ? 'Collapse' : 'Expand'}\n                  </button>\n                  <button\n                    data-testid={`delete-${item.id}`}\n                    onClick={() => deleteItem(item.id)}\n                  >\n                    Delete\n                  </button>\n                </div>\n                {isExpanded && (\n                  <div\n                    data-testid={`content-${item.id}`}\n                    style={{\n                      height: 124,\n                      background: '#eee',\n                      padding: 8,\n                    }}\n                  >\n                    Expanded content for {item.label}\n                  </div>\n                )}\n              </div>\n            )\n          })}\n        </div>\n      </div>\n      <div data-testid=\"total-size\">{rowVirtualizer.getTotalSize()}</div>\n    </div>\n  )\n}\n\nReactDOM.createRoot(document.getElementById('root')!).render(<App />)\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/scroll/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nfunction getRandomInt(min: number, max: number) {\n  return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\nconst randomHeight = (() => {\n  const cache = new Map()\n  return (id: string) => {\n    const value = cache.get(id)\n    if (value !== undefined) {\n      return value\n    }\n    const v = getRandomInt(25, 100)\n    cache.set(id, v)\n    return v\n  }\n})()\n\nconst App = () => {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n  const initialOffset = Number(\n    new URLSearchParams(window.location.search).get('initialOffset') ?? 0,\n  )\n  const rowVirtualizer = useVirtualizer({\n    count: 1002,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n    initialOffset,\n    debug: true,\n  })\n\n  return (\n    <div>\n      <button\n        id=\"scroll-to-1000\"\n        onClick={() => rowVirtualizer.scrollToIndex(1000)}\n      >\n        Scroll to 1000\n      </button>\n      <button\n        id=\"scroll-to-last\"\n        onClick={() => rowVirtualizer.scrollToIndex(1001)}\n      >\n        Scroll to last\n      </button>\n      <button id=\"scroll-to-0\" onClick={() => rowVirtualizer.scrollToIndex(0)}>\n        Scroll to 0\n      </button>\n\n      <div\n        ref={parentRef}\n        id=\"scroll-container\"\n        style={{ height: 400, overflow: 'auto' }}\n      >\n        <div\n          style={{\n            height: rowVirtualizer.getTotalSize(),\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((v) => (\n            <div\n              key={v.key}\n              data-testid={`item-${v.index}`}\n              ref={rowVirtualizer.measureElement}\n              data-index={v.index}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                transform: `translateY(${v.start}px)`,\n                width: '100%',\n              }}\n            >\n              <div style={{ height: randomHeight(String(v.key)) }}>\n                Row {v.index}\n              </div>\n            </div>\n          ))}\n        </div>\n      </div>\n    </div>\n  )\n}\n\nReactDOM.createRoot(document.getElementById('root')!).render(<App />)\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/smooth-scroll/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/smooth-scroll/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nfunction getRandomInt(min: number, max: number) {\n  return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\nconst randomHeight = (() => {\n  const cache = new Map<string, number>()\n  return (id: string) => {\n    const value = cache.get(id)\n    if (value !== undefined) {\n      return value\n    }\n    const v = getRandomInt(25, 100)\n    cache.set(id, v)\n    return v\n  }\n})()\n\nconst App = () => {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: 1002,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n  })\n\n  return (\n    <div>\n      <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>\n        <button\n          id=\"scroll-to-100\"\n          onClick={() =>\n            rowVirtualizer.scrollToIndex(100, { behavior: 'smooth' })\n          }\n        >\n          Smooth scroll to 100\n        </button>\n        <button\n          id=\"scroll-to-500\"\n          onClick={() =>\n            rowVirtualizer.scrollToIndex(500, { behavior: 'smooth' })\n          }\n        >\n          Smooth scroll to 500\n        </button>\n        <button\n          id=\"scroll-to-1000\"\n          onClick={() =>\n            rowVirtualizer.scrollToIndex(1000, { behavior: 'smooth' })\n          }\n        >\n          Smooth scroll to 1000\n        </button>\n        <button\n          id=\"scroll-to-0\"\n          onClick={() =>\n            rowVirtualizer.scrollToIndex(0, { behavior: 'smooth' })\n          }\n        >\n          Smooth scroll to 0\n        </button>\n        <button\n          id=\"scroll-to-500-start\"\n          onClick={() =>\n            rowVirtualizer.scrollToIndex(500, {\n              behavior: 'smooth',\n              align: 'start',\n            })\n          }\n        >\n          Smooth scroll to 500 (start)\n        </button>\n        <button\n          id=\"scroll-to-500-center\"\n          onClick={() =>\n            rowVirtualizer.scrollToIndex(500, {\n              behavior: 'smooth',\n              align: 'center',\n            })\n          }\n        >\n          Smooth scroll to 500 (center)\n        </button>\n      </div>\n\n      <div\n        ref={parentRef}\n        id=\"scroll-container\"\n        style={{ height: 400, overflow: 'auto' }}\n      >\n        <div\n          style={{\n            height: rowVirtualizer.getTotalSize(),\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((v) => (\n            <div\n              key={v.key}\n              data-testid={`item-${v.index}`}\n              ref={rowVirtualizer.measureElement}\n              data-index={v.index}\n              style={{\n                position: 'absolute',\n                top: 0,\n                left: 0,\n                transform: `translateY(${v.start}px)`,\n                width: '100%',\n              }}\n            >\n              <div style={{ height: randomHeight(String(v.key)) }}>\n                Row {v.index}\n              </div>\n            </div>\n          ))}\n        </div>\n      </div>\n    </div>\n  )\n}\n\nReactDOM.createRoot(document.getElementById('root')!).render(<App />)\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/stale-index/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/stale-index/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\n/**\n * Regression test app for stale index bug:\n * When items are removed from the end of the list, the ResizeObserver may fire\n * for a disconnected node whose data-index >= the new count. If getItemKey\n * indexes into the items array, this causes an out-of-bounds error.\n */\n\ninterface Item {\n  id: string\n  label: string\n}\n\nfunction makeItems(count: number): Array<Item> {\n  return Array.from({ length: count }, (_, i) => ({\n    id: `item-${i}`,\n    label: `Row ${i}`,\n  }))\n}\n\nconst App = () => {\n  const parentRef = React.useRef<HTMLDivElement>(null)\n  const [items, setItems] = React.useState(() => makeItems(20))\n  const [error, setError] = React.useState<string | null>(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count: items.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n    getItemKey: (index) => {\n      if (index < 0 || index >= items.length) {\n        const msg = `getItemKey called with stale index ${index} (count=${items.length})`\n        setError(msg)\n        throw new Error(msg)\n      }\n      return items[index].id\n    },\n  })\n\n  const removeLastFive = () => {\n    setItems((prev) => prev.slice(0, Math.max(0, prev.length - 5)))\n  }\n\n  return (\n    <div>\n      <button data-testid=\"remove-items\" onClick={removeLastFive}>\n        Remove last 5\n      </button>\n      <div data-testid=\"item-count\">Count: {items.length}</div>\n      {error && <div data-testid=\"error\">{error}</div>}\n      <div\n        ref={parentRef}\n        data-testid=\"scroll-container\"\n        style={{ height: 300, overflow: 'auto' }}\n      >\n        <div\n          style={{\n            height: rowVirtualizer.getTotalSize(),\n            position: 'relative',\n          }}\n        >\n          {rowVirtualizer.getVirtualItems().map((v) => {\n            const item = items[v.index]\n            return (\n              <div\n                key={item.id}\n                data-testid={item.id}\n                ref={rowVirtualizer.measureElement}\n                data-index={v.index}\n                style={{\n                  position: 'absolute',\n                  top: 0,\n                  left: 0,\n                  transform: `translateY(${v.start}px)`,\n                  width: '100%',\n                  height: 50,\n                  borderBottom: '1px solid #ccc',\n                  padding: 8,\n                  boxSizing: 'border-box',\n                }}\n              >\n                {item.label}\n              </div>\n            )\n          })}\n        </div>\n      </div>\n    </div>\n  )\n}\n\nReactDOM.createRoot(document.getElementById('root')!).render(<App />)\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/test/measure-element.spec.ts",
    "content": "import { expect, test } from '@playwright/test'\n\ntest('positions items correctly after expand → collapse → delete → expand', async ({\n  page,\n}) => {\n  await page.goto('/measure-element/')\n\n  // All 3 items visible at ~36px each\n  await expect(page.locator('[data-testid=\"item-a\"]')).toBeVisible()\n  await expect(page.locator('[data-testid=\"item-b\"]')).toBeVisible()\n  await expect(page.locator('[data-testid=\"item-c\"]')).toBeVisible()\n\n  // Step 1: Expand A → should grow to ~160px\n  await page.click('[data-testid=\"expand-item-a\"]')\n  await expect(page.locator('[data-testid=\"content-item-a\"]')).toBeVisible()\n\n  // Step 2: Collapse A → back to ~36px\n  await page.click('[data-testid=\"expand-item-a\"]')\n  await expect(page.locator('[data-testid=\"content-item-a\"]')).not.toBeVisible()\n\n  // Step 3: Delete A\n  await page.click('[data-testid=\"delete-item-a\"]')\n  await expect(page.locator('[data-testid=\"item-a\"]')).not.toBeVisible()\n\n  // Step 4: Expand B → should grow to ~160px\n  await page.click('[data-testid=\"expand-item-b\"]')\n  await expect(page.locator('[data-testid=\"content-item-b\"]')).toBeVisible()\n\n  // Wait for ResizeObserver to measure the expanded B\n  await page.waitForTimeout(200)\n\n  // C should be positioned after the expanded B, not overlapping it\n  const bBox = await page.locator('[data-testid=\"item-b\"]').boundingBox()\n  const cBox = await page.locator('[data-testid=\"item-c\"]').boundingBox()\n\n  expect(bBox).not.toBeNull()\n  expect(cBox).not.toBeNull()\n\n  // C's top should be at or after B's bottom (with no overlap)\n  const bBottom = bBox!.y + bBox!.height\n  expect(cBox!.y).toBeGreaterThanOrEqual(bBottom - 1) // 1px tolerance\n})\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/test/scroll.spec.ts",
    "content": "import { expect, test } from '@playwright/test'\n\nconst check = () => {\n  const item = document.querySelector('[data-testid=\"item-1000\"]')\n  const container = document.querySelector('#scroll-container')\n\n  if (!item || !container) throw new Error('Elements not found')\n\n  const itemRect = item.getBoundingClientRect()\n  const containerRect = container.getBoundingClientRect()\n  const scrollTop = container.scrollTop\n\n  const top = itemRect.top + scrollTop - containerRect.top\n  const botttom = top + itemRect.height\n\n  const containerBottom = scrollTop + container.clientHeight\n\n  return Math.abs(botttom - containerBottom)\n}\n\ntest('scrolls to index 1000', async ({ page }) => {\n  await page.goto('/scroll/')\n  await page.click('#scroll-to-1000')\n\n  // Wait for scroll effect (including retries)\n  await page.waitForTimeout(1000)\n\n  await expect(page.locator('[data-testid=\"item-1000\"]')).toBeVisible()\n\n  const delta = await page.evaluate(check)\n  expect(delta).toBeLessThan(1.01)\n})\n\ntest('scrolls to last item', async ({ page }) => {\n  await page.goto('/scroll/')\n  await page.click('#scroll-to-last')\n\n  await page.waitForTimeout(1000)\n\n  // Last item (index 1001) should be visible\n  await expect(page.locator('[data-testid=\"item-1001\"]')).toBeVisible()\n\n  // Container should be scrolled to the very bottom\n  const atBottom = await page.evaluate(() => {\n    const container = document.querySelector('#scroll-container')\n    if (!container) throw new Error('Container not found')\n    return Math.abs(\n      container.scrollTop + container.clientHeight - container.scrollHeight,\n    )\n  })\n  expect(atBottom).toBeLessThan(1.01)\n})\n\ntest('renders correctly with initialOffset and user scroll up', async ({\n  page,\n}) => {\n  // Start at offset 5000 (no programmatic scrollToIndex)\n  await page.goto('/scroll/?initialOffset=5000')\n  await page.waitForTimeout(500)\n\n  // Items around offset 5000 should be visible\n  const visibleIndex = await page.evaluate(() => {\n    const container = document.querySelector('#scroll-container')\n    if (!container) throw new Error('Container not found')\n    const items = container.querySelectorAll('[data-index]')\n    const indices = Array.from(items).map((el) =>\n      Number(el.getAttribute('data-index')),\n    )\n    return Math.min(...indices)\n  })\n  expect(visibleIndex).toBeGreaterThan(0)\n\n  // Scroll up by 2000px (user scroll, not programmatic)\n  await page.evaluate(() => {\n    const container = document.querySelector('#scroll-container')\n    if (!container) throw new Error('Container not found')\n    container.scrollTop -= 2000\n  })\n  await page.waitForTimeout(500)\n\n  // After scroll up, items should be properly measured and positioned\n  // (no gaps, no overlaps) — verify consecutive items are contiguous\n  const layout = await page.evaluate(() => {\n    const container = document.querySelector('#scroll-container')\n    if (!container) throw new Error('Container not found')\n    const items = Array.from(container.querySelectorAll('[data-index]'))\n      .map((el) => {\n        const rect = el.getBoundingClientRect()\n        return {\n          index: Number(el.getAttribute('data-index')),\n          top: rect.top,\n          bottom: rect.bottom,\n          height: rect.height,\n        }\n      })\n      .sort((a, b) => a.index - b.index)\n\n    // Check that each item's top matches the previous item's bottom (within tolerance)\n    let maxGap = 0\n    for (let i = 1; i < items.length; i++) {\n      const gap = Math.abs(items[i].top - items[i - 1].bottom)\n      maxGap = Math.max(maxGap, gap)\n    }\n\n    return { items, maxGap }\n  })\n\n  expect(layout.items.length > 0).toBe(true)\n  expect(layout.items.length).toBeGreaterThan(3)\n  // Items should be contiguous — no gaps between consecutive items\n  expect(layout.maxGap).toBeLessThan(2)\n})\n\ntest('scrolls to index 0', async ({ page }) => {\n  await page.goto('/scroll/')\n\n  // First scroll down\n  await page.click('#scroll-to-1000')\n  await page.waitForTimeout(1000)\n\n  // Then scroll to first item\n  await page.click('#scroll-to-0')\n  await page.waitForTimeout(1000)\n\n  await expect(page.locator('[data-testid=\"item-0\"]')).toBeVisible()\n\n  const scrollTop = await page.evaluate(() => {\n    const container = document.querySelector('#scroll-container')\n    return container?.scrollTop ?? -1\n  })\n  expect(scrollTop).toBeLessThan(1.01)\n})\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/test/smooth-scroll.spec.ts",
    "content": "import { expect, test } from '@playwright/test'\n\ntest('smooth scrolls to index 1000', async ({ page }) => {\n  await page.goto('/smooth-scroll/')\n  await page.click('#scroll-to-1000')\n\n  // Smooth scroll animation is 500ms + reconciliation time\n  await page.waitForTimeout(2000)\n\n  await expect(page.locator('[data-testid=\"item-1000\"]')).toBeVisible()\n\n  const delta = await page.evaluate(() => {\n    const item = document.querySelector('[data-testid=\"item-1000\"]')\n    const container = document.querySelector('#scroll-container')\n    if (!item || !container) throw new Error('Elements not found')\n\n    const itemRect = item.getBoundingClientRect()\n    const containerRect = container.getBoundingClientRect()\n    const scrollTop = container.scrollTop\n    const top = itemRect.top + scrollTop - containerRect.top\n    const bottom = top + itemRect.height\n    const containerBottom = scrollTop + container.clientHeight\n    return Math.abs(bottom - containerBottom)\n  })\n  expect(delta).toBeLessThan(1.01)\n})\n\ntest('smooth scrolls to index 100', async ({ page }) => {\n  await page.goto('/smooth-scroll/')\n  await page.click('#scroll-to-100')\n\n  await page.waitForTimeout(2000)\n\n  await expect(page.locator('[data-testid=\"item-100\"]')).toBeVisible()\n})\n\ntest('smooth scrolls to index 0 after scrolling away', async ({ page }) => {\n  await page.goto('/smooth-scroll/')\n\n  // First scroll down\n  await page.click('#scroll-to-500')\n  await page.waitForTimeout(2000)\n  await expect(page.locator('[data-testid=\"item-500\"]')).toBeVisible()\n\n  // Then smooth scroll back to top\n  await page.click('#scroll-to-0')\n  await page.waitForTimeout(2000)\n\n  await expect(page.locator('[data-testid=\"item-0\"]')).toBeVisible()\n\n  const scrollTop = await page.evaluate(() => {\n    const container = document.querySelector('#scroll-container')\n    return container?.scrollTop ?? -1\n  })\n  expect(scrollTop).toBeLessThan(1.01)\n})\n\ntest('smooth scrolls to index 500 with start alignment', async ({ page }) => {\n  await page.goto('/smooth-scroll/')\n  await page.click('#scroll-to-500-start')\n\n  await page.waitForTimeout(2000)\n\n  await expect(page.locator('[data-testid=\"item-500\"]')).toBeVisible()\n\n  const delta = await page.evaluate(\n    ([idx, align]) => {\n      const item = document.querySelector(`[data-testid=\"item-${idx}\"]`)\n      const container = document.querySelector('#scroll-container')\n      if (!item || !container) throw new Error('Elements not found')\n      const itemRect = item.getBoundingClientRect()\n      const containerRect = container.getBoundingClientRect()\n      if (align === 'start') {\n        return Math.abs(itemRect.top - containerRect.top)\n      }\n      return 0\n    },\n    [500, 'start'] as const,\n  )\n  expect(delta).toBeLessThan(1.01)\n})\n\ntest('smooth scrolls to index 500 with center alignment', async ({ page }) => {\n  await page.goto('/smooth-scroll/')\n  await page.click('#scroll-to-500-center')\n\n  await page.waitForTimeout(2000)\n\n  await expect(page.locator('[data-testid=\"item-500\"]')).toBeVisible()\n\n  const delta = await page.evaluate(\n    ([idx]) => {\n      const item = document.querySelector(`[data-testid=\"item-${idx}\"]`)\n      const container = document.querySelector('#scroll-container')\n      if (!item || !container) throw new Error('Elements not found')\n      const itemRect = item.getBoundingClientRect()\n      const containerRect = container.getBoundingClientRect()\n      const containerCenter = containerRect.top + containerRect.height / 2\n      const itemCenter = itemRect.top + itemRect.height / 2\n      return Math.abs(itemCenter - containerCenter)\n    },\n    [500] as const,\n  )\n  // Center alignment has slightly more tolerance due to rounding\n  expect(delta).toBeLessThan(50)\n})\n\ntest('smooth scrolls sequentially to multiple targets', async ({ page }) => {\n  await page.goto('/smooth-scroll/')\n\n  // Scroll to 100 first\n  await page.click('#scroll-to-100')\n  await page.waitForTimeout(2000)\n  await expect(page.locator('[data-testid=\"item-100\"]')).toBeVisible()\n\n  // Then scroll to 500\n  await page.click('#scroll-to-500')\n  await page.waitForTimeout(2000)\n  await expect(page.locator('[data-testid=\"item-500\"]')).toBeVisible()\n\n  // Then scroll to 1000\n  await page.click('#scroll-to-1000')\n  await page.waitForTimeout(2000)\n  await expect(page.locator('[data-testid=\"item-1000\"]')).toBeVisible()\n})\n\ntest('interrupting smooth scroll with another smooth scroll', async ({\n  page,\n}) => {\n  await page.goto('/smooth-scroll/')\n\n  // Start scrolling to 1000\n  await page.click('#scroll-to-1000')\n  // Interrupt mid-animation (before the 500ms animation completes)\n  await page.waitForTimeout(200)\n  await page.click('#scroll-to-100')\n\n  // Wait for the second scroll to complete\n  await page.waitForTimeout(2000)\n\n  // Should have ended at 100, not 1000\n  await expect(page.locator('[data-testid=\"item-100\"]')).toBeVisible()\n})\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/test/stale-index.spec.ts",
    "content": "import { expect, test } from '@playwright/test'\n\ntest('does not call getItemKey with stale index after removing items', async ({\n  page,\n}) => {\n  await page.goto('/stale-index/')\n\n  // Verify initial state\n  await expect(page.locator('[data-testid=\"item-count\"]')).toHaveText(\n    'Count: 20',\n  )\n\n  // Scroll to the bottom so the last items are rendered and observed by ResizeObserver\n  const container = page.locator('[data-testid=\"scroll-container\"]')\n  await container.evaluate((el) => (el.scrollTop = el.scrollHeight))\n  await page.waitForTimeout(100)\n\n  // Remove 5 items from the end — the RO may still fire for the now-disconnected nodes\n  await page.click('[data-testid=\"remove-items\"]')\n  await expect(page.locator('[data-testid=\"item-count\"]')).toHaveText(\n    'Count: 15',\n  )\n\n  // Wait for any pending ResizeObserver callbacks\n  await page.waitForTimeout(200)\n\n  // No error should have been thrown\n  await expect(page.locator('[data-testid=\"error\"]')).not.toBeVisible()\n\n  // Remove 5 more to stress it\n  await page.click('[data-testid=\"remove-items\"]')\n  await expect(page.locator('[data-testid=\"item-count\"]')).toHaveText(\n    'Count: 10',\n  )\n  await page.waitForTimeout(200)\n\n  await expect(page.locator('[data-testid=\"error\"]')).not.toBeVisible()\n})\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"jsx\": \"react-jsx\",\n    \"target\": \"ESNext\",\n    \"moduleResolution\": \"Bundler\",\n    \"module\": \"ESNext\",\n    \"resolveJsonModule\": true,\n    \"allowJs\": true,\n    \"skipLibCheck\": true\n  },\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "packages/react-virtual/e2e/app/vite.config.ts",
    "content": "import path from 'node:path'\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  root: __dirname,\n  plugins: [react()],\n  build: {\n    rollupOptions: {\n      input: {\n        scroll: path.resolve(__dirname, 'scroll/index.html'),\n        'measure-element': path.resolve(\n          __dirname,\n          'measure-element/index.html',\n        ),\n        'smooth-scroll': path.resolve(__dirname, 'smooth-scroll/index.html'),\n        'stale-index': path.resolve(__dirname, 'stale-index/index.html'),\n      },\n    },\n  },\n  resolve: {\n    alias: {\n      '@tanstack/react-virtual': path.resolve(__dirname, '../../src/index'),\n      '@tanstack/virtual-core': path.resolve(\n        __dirname,\n        '../../../virtual-core/src/index',\n      ),\n    },\n  },\n})\n"
  },
  {
    "path": "packages/react-virtual/eslint.config.js",
    "content": "// @ts-check\n\nimport rootConfig from '../../eslint.config.js'\n\nexport default [...rootConfig]\n"
  },
  {
    "path": "packages/react-virtual/package.json",
    "content": "{\n  \"name\": \"@tanstack/react-virtual\",\n  \"version\": \"3.13.23\",\n  \"description\": \"Headless UI for virtualizing scrollable elements in React\",\n  \"author\": \"Tanner Linsley\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\",\n    \"directory\": \"packages/react-virtual\"\n  },\n  \"homepage\": \"https://tanstack.com/virtual\",\n  \"funding\": {\n    \"type\": \"github\",\n    \"url\": \"https://github.com/sponsors/tannerlinsley\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"vue\",\n    \"solid\",\n    \"virtual\",\n    \"virtual-core\",\n    \"datagrid\"\n  ],\n  \"scripts\": {\n    \"clean\": \"premove ./dist ./coverage\",\n    \"test:eslint\": \"eslint ./src\",\n    \"test:types\": \"tsc\",\n    \"test:lib\": \"vitest\",\n    \"test:lib:dev\": \"pnpm run test:lib --watch\",\n    \"test:build\": \"publint --strict\",\n    \"build\": \"vite build\",\n    \"test:e2e\": \"../../node_modules/.bin/playwright test\"\n  },\n  \"type\": \"module\",\n  \"types\": \"dist/esm/index.d.ts\",\n  \"main\": \"dist/cjs/index.cjs\",\n  \"module\": \"dist/esm/index.js\",\n  \"exports\": {\n    \".\": {\n      \"import\": {\n        \"types\": \"./dist/esm/index.d.ts\",\n        \"default\": \"./dist/esm/index.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/cjs/index.d.cts\",\n        \"default\": \"./dist/cjs/index.cjs\"\n      }\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"dependencies\": {\n    \"@tanstack/virtual-core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@testing-library/react\": \"^16.3.0\",\n    \"@types/react\": \"^18.3.23\",\n    \"@types/react-dom\": \"^18.3.7\",\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"resize-observer-polyfill\": \"^1.5.1\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\n    \"react-dom\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/react-virtual/playwright.config.ts",
    "content": "import { defineConfig } from '@playwright/test'\n\nconst PORT = 5173\nconst baseURL = `http://localhost:${PORT}`\n\nexport default defineConfig({\n  testDir: './e2e/app/test',\n  use: {\n    baseURL,\n  },\n  webServer: {\n    command: `VITE_SERVER_PORT=${PORT} vite build --config e2e/app/vite.config.ts && VITE_SERVER_PORT=${PORT} vite preview --config e2e/app/vite.config.ts --port ${PORT}`,\n    url: `${baseURL}/scroll/`,\n    reuseExistingServer: !process.env.CI,\n    stdout: 'pipe',\n  },\n})\n"
  },
  {
    "path": "packages/react-virtual/src/index.tsx",
    "content": "import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n  Virtualizer,\n  elementScroll,\n  observeElementOffset,\n  observeElementRect,\n  observeWindowOffset,\n  observeWindowRect,\n  windowScroll,\n} from '@tanstack/virtual-core'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\n\nexport * from '@tanstack/virtual-core'\n\nconst useIsomorphicLayoutEffect =\n  typeof document !== 'undefined' ? React.useLayoutEffect : React.useEffect\n\nexport type ReactVirtualizerOptions<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n> = VirtualizerOptions<TScrollElement, TItemElement> & {\n  useFlushSync?: boolean\n}\n\nfunction useVirtualizerBase<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n>({\n  useFlushSync = true,\n  ...options\n}: ReactVirtualizerOptions<TScrollElement, TItemElement>): Virtualizer<\n  TScrollElement,\n  TItemElement\n> {\n  const rerender = React.useReducer(() => ({}), {})[1]\n\n  const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> = {\n    ...options,\n    onChange: (instance, sync) => {\n      if (useFlushSync && sync) {\n        flushSync(rerender)\n      } else {\n        rerender()\n      }\n      options.onChange?.(instance, sync)\n    },\n  }\n\n  const [instance] = React.useState(\n    () => new Virtualizer<TScrollElement, TItemElement>(resolvedOptions),\n  )\n\n  instance.setOptions(resolvedOptions)\n\n  useIsomorphicLayoutEffect(() => {\n    return instance._didMount()\n  }, [])\n\n  useIsomorphicLayoutEffect(() => {\n    return instance._willUpdate()\n  })\n\n  return instance\n}\n\nexport function useVirtualizer<\n  TScrollElement extends Element,\n  TItemElement extends Element,\n>(\n  options: PartialKeys<\n    ReactVirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  >,\n): Virtualizer<TScrollElement, TItemElement> {\n  return useVirtualizerBase<TScrollElement, TItemElement>({\n    observeElementRect: observeElementRect,\n    observeElementOffset: observeElementOffset,\n    scrollToFn: elementScroll,\n    ...options,\n  })\n}\n\nexport function useWindowVirtualizer<TItemElement extends Element>(\n  options: PartialKeys<\n    ReactVirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): Virtualizer<Window, TItemElement> {\n  return useVirtualizerBase<Window, TItemElement>({\n    getScrollElement: () => (typeof document !== 'undefined' ? window : null),\n    observeElementRect: observeWindowRect,\n    observeElementOffset: observeWindowOffset,\n    scrollToFn: windowScroll,\n    initialOffset: () => (typeof document !== 'undefined' ? window.scrollY : 0),\n    ...options,\n  })\n}\n"
  },
  {
    "path": "packages/react-virtual/tests/index.test.tsx",
    "content": "import { beforeEach, test, expect, vi } from 'vitest'\nimport * as React from 'react'\nimport { render, screen } from '@testing-library/react'\n\nimport { useVirtualizer, Range } from '../src/index'\n\nbeforeEach(() => {\n  Object.defineProperties(HTMLElement.prototype, {\n    scrollHeight: {\n      configurable: true,\n      get: () => Number.MAX_SAFE_INTEGER,\n    },\n    scrollWidth: {\n      configurable: true,\n      get: () => Number.MAX_SAFE_INTEGER,\n    },\n  })\n})\n\nlet renderer: vi.Mock<undefined, []>\n\ninterface ListProps {\n  count?: number\n  overscan?: number\n  height?: number\n  width?: number\n  itemSize?: number\n  rangeExtractor?: (range: Range) => number[]\n  dynamic?: boolean\n}\n\nfunction List({\n  count = 200,\n  overscan,\n  height = 200,\n  width = 200,\n  itemSize,\n  rangeExtractor,\n  dynamic,\n}: ListProps) {\n  renderer()\n\n  const parentRef = React.useRef<HTMLDivElement>(null)\n\n  const elementRectCallbackRef = React.useRef<\n    ((rect: { height: number; width: number }) => void) | null\n  >(null)\n\n  const rowVirtualizer = useVirtualizer({\n    count,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 50,\n    overscan,\n    observeElementRect: (_, cb) => {\n      cb({ height, width })\n      elementRectCallbackRef.current = cb\n    },\n    measureElement: () => itemSize ?? 0,\n    rangeExtractor,\n  })\n\n  React.useEffect(() => {\n    elementRectCallbackRef.current?.({ height, width })\n  }, [height, width])\n\n  const measureElement = dynamic ? rowVirtualizer.measureElement : undefined\n\n  const items = rowVirtualizer.getVirtualItems()\n\n  return (\n    <div\n      ref={parentRef}\n      style={{ height, width, overflow: 'auto' }}\n      data-testid=\"scroller\"\n    >\n      <div\n        style={{\n          height: rowVirtualizer.getTotalSize(),\n          width: '100%',\n          position: 'relative',\n        }}\n      >\n        {items.map((virtualRow) => (\n          <div\n            data-testid={`item-${virtualRow.key}`}\n            key={virtualRow.key}\n            data-index={virtualRow.index}\n            ref={measureElement}\n            style={{\n              position: 'absolute',\n              top: 0,\n              left: 0,\n              width: '100%',\n              transform: `translateY(${virtualRow.start}px)`,\n              height: itemSize,\n            }}\n          >\n            Row {virtualRow.index}\n          </div>\n        ))}\n      </div>\n    </div>\n  )\n}\n\nbeforeEach(() => {\n  renderer = vi.fn(() => undefined)\n})\n\ntest('should render', () => {\n  render(<List />)\n\n  expect(screen.queryByText('Row 0')).toBeInTheDocument()\n  expect(screen.queryByText('Row 4')).toBeInTheDocument()\n  expect(screen.queryByText('Row 5')).not.toBeInTheDocument()\n\n  expect(renderer).toHaveBeenCalledTimes(2)\n})\n\ntest('should render with overscan', () => {\n  render(<List overscan={0} />)\n\n  expect(screen.queryByText('Row 0')).toBeInTheDocument()\n  expect(screen.queryByText('Row 3')).toBeInTheDocument()\n  expect(screen.queryByText('Row 4')).not.toBeInTheDocument()\n\n  expect(renderer).toHaveBeenCalledTimes(2)\n})\n\ntest('should render given dynamic size', async () => {\n  render(<List itemSize={100} dynamic />)\n\n  expect(screen.queryByText('Row 0')).toBeInTheDocument()\n  expect(screen.queryByText('Row 1')).toBeInTheDocument()\n  expect(screen.queryByText('Row 2')).toBeInTheDocument()\n  expect(screen.queryByText('Row 3')).not.toBeInTheDocument()\n\n  expect(renderer).toHaveBeenCalledTimes(3)\n})\n\ntest('should use rangeExtractor', () => {\n  render(<List rangeExtractor={() => [0, 1]} />)\n\n  expect(screen.queryByText('Row 0')).toBeInTheDocument()\n  expect(screen.queryByText('Row 1')).toBeInTheDocument()\n  expect(screen.queryByText('Row 2')).not.toBeInTheDocument()\n})\n\ntest('should handle count change', () => {\n  const { rerender } = render(<List count={2} />)\n\n  expect(screen.queryByText('Row 0')).toBeInTheDocument()\n  expect(screen.queryByText('Row 1')).toBeInTheDocument()\n  expect(screen.queryByText('Row 2')).not.toBeInTheDocument()\n\n  rerender(<List count={10} />)\n\n  expect(screen.queryByText('Row 2')).toBeInTheDocument()\n  expect(screen.queryByText('Row 4')).toBeInTheDocument()\n  expect(screen.queryByText('Row 5')).not.toBeInTheDocument()\n})\n\ntest('should handle handle height change', () => {\n  const { rerender } = render(<List count={0} height={0} />)\n\n  expect(screen.queryByText('Row 0')).not.toBeInTheDocument()\n  rerender(<List count={1} height={200} />)\n  expect(screen.queryByText('Row 0')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "packages/react-virtual/tests/test-setup.ts",
    "content": "import '@testing-library/jest-dom/vitest'\nimport { cleanup } from '@testing-library/react'\nimport { afterEach } from 'vitest'\nimport ResizeObserver from 'resize-observer-polyfill'\n\n// https://testing-library.com/docs/react-testing-library/api#cleanup\nafterEach(() => cleanup())\n\nglobal.ResizeObserver = ResizeObserver\n"
  },
  {
    "path": "packages/react-virtual/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"jsx\": \"react\"\n  },\n  \"include\": [\n    \"src\",\n    \"eslint.config.js\",\n    \"vite.config.ts\",\n    \"playwright.config.ts\"\n  ]\n}\n"
  },
  {
    "path": "packages/react-virtual/vite.config.ts",
    "content": "import { defineConfig, mergeConfig } from 'vitest/config'\nimport { tanstackViteConfig } from '@tanstack/vite-config'\nimport react from '@vitejs/plugin-react'\nimport packageJson from './package.json'\n\nconst config = defineConfig({\n  plugins: [react()],\n  test: {\n    name: packageJson.name,\n    dir: './tests',\n    watch: false,\n    environment: 'jsdom',\n    setupFiles: ['./tests/test-setup.ts'],\n  },\n})\n\nexport default mergeConfig(\n  config,\n  tanstackViteConfig({\n    entry: './src/index.tsx',\n    srcDir: './src',\n  }),\n)\n"
  },
  {
    "path": "packages/solid-virtual/CHANGELOG.md",
    "content": "# @tanstack/solid-virtual\n\n## 3.13.23\n\n### Patch Changes\n\n- Updated dependencies [[`7ece2d5`](https://github.com/TanStack/virtual/commit/7ece2d5d4249b7e703c68ac497ae5545c54e7c67)]:\n  - @tanstack/virtual-core@3.13.23\n\n## 3.13.22\n\n### Patch Changes\n\n- Updated dependencies [[`54d771a`](https://github.com/TanStack/virtual/commit/54d771a7d4c74f6968e8132b5a85f3e04682376a), [`d3416c3`](https://github.com/TanStack/virtual/commit/d3416c386c6446957f413db2eef3211f5fdf3b5f)]:\n  - @tanstack/virtual-core@3.13.22\n\n## 3.13.21\n\n### Patch Changes\n\n- Updated dependencies [[`be89e29`](https://github.com/TanStack/virtual/commit/be89e293ea01654df6334dc6473b65eebed13e51)]:\n  - @tanstack/virtual-core@3.13.21\n\n## 3.13.20\n\n### Patch Changes\n\n- Updated dependencies [[`ff83e94`](https://github.com/TanStack/virtual/commit/ff83e949408ba8a714436fa10cafc3725a56274b)]:\n  - @tanstack/virtual-core@3.13.20\n\n## 3.13.19\n\n### Patch Changes\n\n- Updated dependencies [[`843109c`](https://github.com/TanStack/virtual/commit/843109c5bf780591a762f9767f3808fd15e3f94e)]:\n  - @tanstack/virtual-core@3.13.19\n\n## 3.13.18\n\n### Patch Changes\n\n- Updated dependencies [[`9067574`](https://github.com/TanStack/virtual/commit/9067574f1a0178d30e27bcac70853bdcbf437fec)]:\n  - @tanstack/virtual-core@3.13.18\n\n## 3.13.17\n\n### Patch Changes\n\n- Updated dependencies [[`21d9a46`](https://github.com/TanStack/virtual/commit/21d9a46eac034cb4299872891694965bceed526d)]:\n  - @tanstack/virtual-core@3.13.17\n\n## 3.13.16\n\n### Patch Changes\n\n- Updated dependencies [[`db6df21`](https://github.com/TanStack/virtual/commit/db6df212ed83dd7e4eb6450d1340c95475667b7b)]:\n  - @tanstack/virtual-core@3.13.16\n\n## 3.13.15\n\n### Patch Changes\n\n- Updated dependencies [[`5a273bf`](https://github.com/TanStack/virtual/commit/5a273bf0c0bc0255ca172929f021c3b6e50cb69d)]:\n  - @tanstack/virtual-core@3.13.15\n\n## 3.13.14\n\n### Patch Changes\n\n- Updated dependencies [[`6d9274c`](https://github.com/TanStack/virtual/commit/6d9274c3f0a9e64450b5829872079a65277bc654)]:\n  - @tanstack/virtual-core@3.13.14\n\n## 3.13.13\n\n### Patch Changes\n\n- Fix: Notify framework when count changes to update getTotalSize() ([#1085](https://github.com/TanStack/virtual/pull/1085))\n\n  Fixed an issue where `getTotalSize()` would return stale values when the `count` option changed (e.g., during filtering or search operations). The virtualizer now automatically notifies the framework when measurement-affecting options change, ensuring the UI updates correctly without requiring manual `useMemo` workarounds.\n\n  **Before**: When filtering items, the list container would maintain its previous height, causing excessive blank space (when count decreased) or inaccessible items (when count increased).\n\n  **After**: Height updates automatically when count changes, providing the correct user experience.\n\n  This fix applies to all framework adapters and has minimal performance impact (< 0.1ms per change).\n\n- Updated dependencies [[`2542c5a`](https://github.com/TanStack/virtual/commit/2542c5a3d6820cea956fa3b4f94c42e3526a8d68), [`96e32a6`](https://github.com/TanStack/virtual/commit/96e32a6ffc125743a0172ea4e0fe37ac29c4187b)]:\n  - @tanstack/virtual-core@3.13.13\n\n## 3.13.12\n\n### Patch Changes\n\n- Updated dependencies [[`d21ed98`](https://github.com/TanStack/virtual/commit/d21ed98da3470b9986c9a028ed70fdf0d6189ab4)]:\n  - @tanstack/virtual-core@3.13.12\n\n## 3.13.11\n\n### Patch Changes\n\n- Updated dependencies [[`73fa867`](https://github.com/TanStack/virtual/commit/73fa86752599a4bffba51ec8e4ff2f8cb8283010)]:\n  - @tanstack/virtual-core@3.13.11\n\n## 3.13.10\n\n### Patch Changes\n\n- Updated dependencies [[`b3b7e7d`](https://github.com/TanStack/virtual/commit/b3b7e7dc8b25daeebbd2da61b3b7ae3448babbdb)]:\n  - @tanstack/virtual-core@3.13.10\n\n## 3.13.9\n\n### Patch Changes\n\n- Updated dependencies [[`9e33cdb`](https://github.com/TanStack/virtual/commit/9e33cdb1c8780c2f455aafc11a0aeea58b71fc69)]:\n  - @tanstack/virtual-core@3.13.9\n\n## 3.13.8\n\n### Patch Changes\n\n- Updated dependencies [[`60719f6`](https://github.com/TanStack/virtual/commit/60719f61b589d6f9d886e4f7c093217f6d693faf)]:\n  - @tanstack/virtual-core@3.13.8\n\n## 3.13.7\n\n### Patch Changes\n\n- Updated dependencies [[`e2d93c2`](https://github.com/TanStack/virtual/commit/e2d93c2dcde9ccf60f658e56edccd8d05aefeee6)]:\n  - @tanstack/virtual-core@3.13.7\n\n## 3.13.6\n\n### Patch Changes\n\n- Updated dependencies [[`042616f`](https://github.com/TanStack/virtual/commit/042616f39ced842470db0b4b40fca77f22454b7f)]:\n  - @tanstack/virtual-core@3.13.6\n\n## 3.13.5\n\n### Patch Changes\n\n- Updated dependencies [[`51656d9`](https://github.com/TanStack/virtual/commit/51656d94a2469a065e631f25ffc8ec0288d9f5ec)]:\n  - @tanstack/virtual-core@3.13.5\n\n## 3.13.4\n\n### Patch Changes\n\n- Updated dependencies [[`514b62d`](https://github.com/TanStack/virtual/commit/514b62d04974c2fd59fc8a68ed40f4c1a1547dd2), [`f03d814`](https://github.com/TanStack/virtual/commit/f03d8142c03ea0f5816161a4dad38ca35469841c)]:\n  - @tanstack/virtual-core@3.13.4\n\n## 3.13.3\n\n### Patch Changes\n\n- Updated dependencies [[`02ef309`](https://github.com/TanStack/virtual/commit/02ef3097de4a14ed4077ace2ca901dc411bf81c1)]:\n  - @tanstack/virtual-core@3.13.3\n"
  },
  {
    "path": "packages/solid-virtual/eslint.config.js",
    "content": "// @ts-check\n\nimport rootConfig from '../../eslint.config.js'\n\nexport default [...rootConfig]\n"
  },
  {
    "path": "packages/solid-virtual/package.json",
    "content": "{\n  \"name\": \"@tanstack/solid-virtual\",\n  \"version\": \"3.13.23\",\n  \"description\": \"Headless UI for virtualizing scrollable elements in Solid\",\n  \"author\": \"Tanner Linsley\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\",\n    \"directory\": \"packages/solid-virtual\"\n  },\n  \"homepage\": \"https://tanstack.com/virtual\",\n  \"funding\": {\n    \"type\": \"github\",\n    \"url\": \"https://github.com/sponsors/tannerlinsley\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"vue\",\n    \"solid\",\n    \"virtual\",\n    \"virtual-core\",\n    \"datagrid\"\n  ],\n  \"scripts\": {\n    \"clean\": \"premove ./dist ./coverage\",\n    \"test:eslint\": \"eslint ./src\",\n    \"test:types\": \"tsc\",\n    \"test:build\": \"publint --strict\",\n    \"build\": \"vite build\"\n  },\n  \"type\": \"module\",\n  \"types\": \"dist/esm/index.d.ts\",\n  \"main\": \"dist/cjs/index.cjs\",\n  \"module\": \"dist/esm/index.js\",\n  \"exports\": {\n    \".\": {\n      \"import\": {\n        \"types\": \"./dist/esm/index.d.ts\",\n        \"default\": \"./dist/esm/index.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/cjs/index.d.cts\",\n        \"default\": \"./dist/cjs/index.cjs\"\n      }\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"dependencies\": {\n    \"@tanstack/virtual-core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"solid-js\": \"^1.9.7\",\n    \"vite-plugin-solid\": \"^2.11.6\"\n  },\n  \"peerDependencies\": {\n    \"solid-js\": \"^1.3.0\"\n  }\n}\n"
  },
  {
    "path": "packages/solid-virtual/src/index.tsx",
    "content": "import {\n  Virtualizer,\n  elementScroll,\n  observeElementOffset,\n  observeElementRect,\n  observeWindowOffset,\n  observeWindowRect,\n  windowScroll,\n} from '@tanstack/virtual-core'\n\nimport {\n  createComputed,\n  createSignal,\n  mergeProps,\n  onCleanup,\n  onMount,\n} from 'solid-js'\nimport { createStore, reconcile } from 'solid-js/store'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\n\nexport * from '@tanstack/virtual-core'\n\nfunction createVirtualizerBase<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n>(\n  options: VirtualizerOptions<TScrollElement, TItemElement>,\n): Virtualizer<TScrollElement, TItemElement> {\n  const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> =\n    mergeProps(options)\n\n  const instance = new Virtualizer<TScrollElement, TItemElement>(\n    resolvedOptions,\n  )\n\n  const [virtualItems, setVirtualItems] = createStore(\n    instance.getVirtualItems(),\n  )\n  const [totalSize, setTotalSize] = createSignal(instance.getTotalSize())\n\n  const handler = {\n    get(\n      target: Virtualizer<TScrollElement, TItemElement>,\n      prop: keyof Virtualizer<TScrollElement, TItemElement>,\n    ) {\n      switch (prop) {\n        case 'getVirtualItems':\n          return () => virtualItems\n        case 'getTotalSize':\n          return () => totalSize()\n        default:\n          return Reflect.get(target, prop)\n      }\n    },\n  }\n\n  const virtualizer = new Proxy(instance, handler)\n  virtualizer.setOptions(resolvedOptions)\n\n  onMount(() => {\n    const cleanup = virtualizer._didMount()\n    virtualizer._willUpdate()\n    onCleanup(cleanup)\n  })\n\n  createComputed(() => {\n    virtualizer.setOptions(\n      mergeProps(resolvedOptions, options, {\n        onChange: (\n          instance: Virtualizer<TScrollElement, TItemElement>,\n          sync: boolean,\n        ) => {\n          instance._willUpdate()\n          setVirtualItems(\n            reconcile(instance.getVirtualItems(), {\n              key: 'index',\n            }),\n          )\n          setTotalSize(instance.getTotalSize())\n          options.onChange?.(instance, sync)\n        },\n      }),\n    )\n    virtualizer.measure()\n  })\n\n  return virtualizer\n}\n\nexport function createVirtualizer<\n  TScrollElement extends Element,\n  TItemElement extends Element,\n>(\n  options: PartialKeys<\n    VirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  >,\n): Virtualizer<TScrollElement, TItemElement> {\n  return createVirtualizerBase<TScrollElement, TItemElement>(\n    mergeProps(\n      {\n        observeElementRect: observeElementRect,\n        observeElementOffset: observeElementOffset,\n        scrollToFn: elementScroll,\n      },\n      options,\n    ),\n  )\n}\n\nexport function createWindowVirtualizer<TItemElement extends Element>(\n  options: PartialKeys<\n    VirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): Virtualizer<Window, TItemElement> {\n  return createVirtualizerBase<Window, TItemElement>(\n    mergeProps(\n      {\n        getScrollElement: () =>\n          typeof document !== 'undefined' ? window : null,\n        observeElementRect: observeWindowRect,\n        observeElementOffset: observeWindowOffset,\n        scrollToFn: windowScroll,\n        initialOffset: () =>\n          typeof document !== 'undefined' ? window.scrollY : 0,\n      },\n      options,\n    ),\n  )\n}\n"
  },
  {
    "path": "packages/solid-virtual/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"jsx\": \"preserve\",\n    \"jsxImportSource\": \"solid-js\"\n  },\n  \"include\": [\"src\", \"eslint.config.js\", \"vite.config.ts\"]\n}\n"
  },
  {
    "path": "packages/solid-virtual/vite.config.ts",
    "content": "import { defineConfig, mergeConfig } from 'vitest/config'\nimport { tanstackViteConfig } from '@tanstack/vite-config'\nimport solid from 'vite-plugin-solid'\n\nconst config = defineConfig({\n  plugins: [solid()],\n})\n\nexport default mergeConfig(\n  config,\n  tanstackViteConfig({\n    entry: './src/index.tsx',\n    srcDir: './src',\n  }),\n)\n"
  },
  {
    "path": "packages/svelte-virtual/CHANGELOG.md",
    "content": "# @tanstack/svelte-virtual\n\n## 3.13.23\n\n### Patch Changes\n\n- Updated dependencies [[`7ece2d5`](https://github.com/TanStack/virtual/commit/7ece2d5d4249b7e703c68ac497ae5545c54e7c67)]:\n  - @tanstack/virtual-core@3.13.23\n\n## 3.13.22\n\n### Patch Changes\n\n- fix(svelte-virtual): force store update when setOptions is called ([#1143](https://github.com/TanStack/virtual/pull/1143))\n\n- Updated dependencies [[`54d771a`](https://github.com/TanStack/virtual/commit/54d771a7d4c74f6968e8132b5a85f3e04682376a), [`d3416c3`](https://github.com/TanStack/virtual/commit/d3416c386c6446957f413db2eef3211f5fdf3b5f)]:\n  - @tanstack/virtual-core@3.13.22\n\n## 3.13.21\n\n### Patch Changes\n\n- Updated dependencies [[`be89e29`](https://github.com/TanStack/virtual/commit/be89e293ea01654df6334dc6473b65eebed13e51)]:\n  - @tanstack/virtual-core@3.13.21\n\n## 3.13.20\n\n### Patch Changes\n\n- Updated dependencies [[`ff83e94`](https://github.com/TanStack/virtual/commit/ff83e949408ba8a714436fa10cafc3725a56274b)]:\n  - @tanstack/virtual-core@3.13.20\n\n## 3.13.19\n\n### Patch Changes\n\n- Updated dependencies [[`843109c`](https://github.com/TanStack/virtual/commit/843109c5bf780591a762f9767f3808fd15e3f94e)]:\n  - @tanstack/virtual-core@3.13.19\n\n## 3.13.18\n\n### Patch Changes\n\n- Updated dependencies [[`9067574`](https://github.com/TanStack/virtual/commit/9067574f1a0178d30e27bcac70853bdcbf437fec)]:\n  - @tanstack/virtual-core@3.13.18\n\n## 3.13.17\n\n### Patch Changes\n\n- Updated dependencies [[`21d9a46`](https://github.com/TanStack/virtual/commit/21d9a46eac034cb4299872891694965bceed526d)]:\n  - @tanstack/virtual-core@3.13.17\n\n## 3.13.16\n\n### Patch Changes\n\n- Updated dependencies [[`db6df21`](https://github.com/TanStack/virtual/commit/db6df212ed83dd7e4eb6450d1340c95475667b7b)]:\n  - @tanstack/virtual-core@3.13.16\n\n## 3.13.15\n\n### Patch Changes\n\n- Updated dependencies [[`5a273bf`](https://github.com/TanStack/virtual/commit/5a273bf0c0bc0255ca172929f021c3b6e50cb69d)]:\n  - @tanstack/virtual-core@3.13.15\n\n## 3.13.14\n\n### Patch Changes\n\n- Updated dependencies [[`6d9274c`](https://github.com/TanStack/virtual/commit/6d9274c3f0a9e64450b5829872079a65277bc654)]:\n  - @tanstack/virtual-core@3.13.14\n\n## 3.13.13\n\n### Patch Changes\n\n- Fix: Notify framework when count changes to update getTotalSize() ([#1085](https://github.com/TanStack/virtual/pull/1085))\n\n  Fixed an issue where `getTotalSize()` would return stale values when the `count` option changed (e.g., during filtering or search operations). The virtualizer now automatically notifies the framework when measurement-affecting options change, ensuring the UI updates correctly without requiring manual `useMemo` workarounds.\n\n  **Before**: When filtering items, the list container would maintain its previous height, causing excessive blank space (when count decreased) or inaccessible items (when count increased).\n\n  **After**: Height updates automatically when count changes, providing the correct user experience.\n\n  This fix applies to all framework adapters and has minimal performance impact (< 0.1ms per change).\n\n- Updated dependencies [[`2542c5a`](https://github.com/TanStack/virtual/commit/2542c5a3d6820cea956fa3b4f94c42e3526a8d68), [`96e32a6`](https://github.com/TanStack/virtual/commit/96e32a6ffc125743a0172ea4e0fe37ac29c4187b)]:\n  - @tanstack/virtual-core@3.13.13\n\n## 3.13.12\n\n### Patch Changes\n\n- Updated dependencies [[`d21ed98`](https://github.com/TanStack/virtual/commit/d21ed98da3470b9986c9a028ed70fdf0d6189ab4)]:\n  - @tanstack/virtual-core@3.13.12\n\n## 3.13.11\n\n### Patch Changes\n\n- Updated dependencies [[`73fa867`](https://github.com/TanStack/virtual/commit/73fa86752599a4bffba51ec8e4ff2f8cb8283010)]:\n  - @tanstack/virtual-core@3.13.11\n\n## 3.13.10\n\n### Patch Changes\n\n- Updated dependencies [[`b3b7e7d`](https://github.com/TanStack/virtual/commit/b3b7e7dc8b25daeebbd2da61b3b7ae3448babbdb)]:\n  - @tanstack/virtual-core@3.13.10\n\n## 3.13.9\n\n### Patch Changes\n\n- Updated dependencies [[`9e33cdb`](https://github.com/TanStack/virtual/commit/9e33cdb1c8780c2f455aafc11a0aeea58b71fc69)]:\n  - @tanstack/virtual-core@3.13.9\n\n## 3.13.8\n\n### Patch Changes\n\n- Updated dependencies [[`60719f6`](https://github.com/TanStack/virtual/commit/60719f61b589d6f9d886e4f7c093217f6d693faf)]:\n  - @tanstack/virtual-core@3.13.8\n\n## 3.13.7\n\n### Patch Changes\n\n- Updated dependencies [[`e2d93c2`](https://github.com/TanStack/virtual/commit/e2d93c2dcde9ccf60f658e56edccd8d05aefeee6)]:\n  - @tanstack/virtual-core@3.13.7\n\n## 3.13.6\n\n### Patch Changes\n\n- Updated dependencies [[`042616f`](https://github.com/TanStack/virtual/commit/042616f39ced842470db0b4b40fca77f22454b7f)]:\n  - @tanstack/virtual-core@3.13.6\n\n## 3.13.5\n\n### Patch Changes\n\n- Updated dependencies [[`51656d9`](https://github.com/TanStack/virtual/commit/51656d94a2469a065e631f25ffc8ec0288d9f5ec)]:\n  - @tanstack/virtual-core@3.13.5\n\n## 3.13.4\n\n### Patch Changes\n\n- Updated dependencies [[`514b62d`](https://github.com/TanStack/virtual/commit/514b62d04974c2fd59fc8a68ed40f4c1a1547dd2), [`f03d814`](https://github.com/TanStack/virtual/commit/f03d8142c03ea0f5816161a4dad38ca35469841c)]:\n  - @tanstack/virtual-core@3.13.4\n\n## 3.13.3\n\n### Patch Changes\n\n- Updated dependencies [[`02ef309`](https://github.com/TanStack/virtual/commit/02ef3097de4a14ed4077ace2ca901dc411bf81c1)]:\n  - @tanstack/virtual-core@3.13.3\n"
  },
  {
    "path": "packages/svelte-virtual/eslint.config.js",
    "content": "// @ts-check\n\nimport rootConfig from '../../eslint.config.js'\n\nexport default [...rootConfig]\n"
  },
  {
    "path": "packages/svelte-virtual/package.json",
    "content": "{\n  \"name\": \"@tanstack/svelte-virtual\",\n  \"version\": \"3.13.23\",\n  \"description\": \"Headless UI for virtualizing scrollable elements in Svelte\",\n  \"author\": \"Tanner Linsley\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\",\n    \"directory\": \"packages/svelte-virtual\"\n  },\n  \"homepage\": \"https://tanstack.com/virtual\",\n  \"funding\": {\n    \"type\": \"github\",\n    \"url\": \"https://github.com/sponsors/tannerlinsley\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"vue\",\n    \"solid\",\n    \"svelte\",\n    \"virtual\",\n    \"virtual-core\",\n    \"datagrid\"\n  ],\n  \"scripts\": {\n    \"clean\": \"premove ./dist ./coverage\",\n    \"test:eslint\": \"eslint ./src\",\n    \"test:types\": \"tsc\",\n    \"test:build\": \"publint --strict\",\n    \"build\": \"svelte-package --input ./src --output ./dist\"\n  },\n  \"type\": \"module\",\n  \"types\": \"dist/index.d.ts\",\n  \"module\": \"dist/index.js\",\n  \"svelte\": \"./dist/index.js\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"svelte\": \"./dist/index.js\",\n      \"import\": \"./dist/index.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"dependencies\": {\n    \"@tanstack/virtual-core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@sveltejs/package\": \"^2.3.11\",\n    \"@sveltejs/vite-plugin-svelte\": \"^3.1.2\",\n    \"svelte\": \"^4.2.20\"\n  },\n  \"peerDependencies\": {\n    \"svelte\": \"^3.48.0 || ^4.0.0 || ^5.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/svelte-virtual/src/index.ts",
    "content": "import {\n  Virtualizer,\n  elementScroll,\n  observeElementOffset,\n  observeElementRect,\n  observeWindowOffset,\n  observeWindowRect,\n  windowScroll,\n} from '@tanstack/virtual-core'\nimport { derived, writable } from 'svelte/store'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\nimport type { Readable, Writable } from 'svelte/store'\n\nexport * from '@tanstack/virtual-core'\n\nexport type SvelteVirtualizer<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n> = Omit<Virtualizer<TScrollElement, TItemElement>, 'setOptions'> & {\n  setOptions: (\n    options: Partial<VirtualizerOptions<TScrollElement, TItemElement>>,\n  ) => void\n}\n\nfunction createVirtualizerBase<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n>(\n  initialOptions: VirtualizerOptions<TScrollElement, TItemElement>,\n): Readable<SvelteVirtualizer<TScrollElement, TItemElement>> {\n  const virtualizer = new Virtualizer(initialOptions)\n  const originalSetOptions = virtualizer.setOptions\n\n  // eslint-disable-next-line prefer-const\n  let virtualizerWritable: Writable<Virtualizer<TScrollElement, TItemElement>>\n\n  const setOptions = (\n    options: Partial<VirtualizerOptions<TScrollElement, TItemElement>>,\n  ) => {\n    const resolvedOptions = {\n      ...virtualizer.options,\n      ...options,\n      onChange: options.onChange,\n    }\n    originalSetOptions({\n      ...resolvedOptions,\n      onChange: (\n        instance: Virtualizer<TScrollElement, TItemElement>,\n        sync: boolean,\n      ) => {\n        virtualizerWritable.set(instance)\n        resolvedOptions.onChange?.(instance, sync)\n      },\n    })\n    virtualizer._willUpdate()\n    // Force store update in case the range didn't change (e.g. count increased\n    // but scroll position stayed the same). Without this, the store only\n    // updates when onChange fires (on range change), so changes like a new\n    // count that don't shift the visible range would not trigger a re-render.\n    virtualizerWritable.set(virtualizer)\n  }\n\n  virtualizerWritable = writable(virtualizer, () => {\n    setOptions(initialOptions)\n    return virtualizer._didMount()\n  })\n\n  return derived(virtualizerWritable, (instance) =>\n    Object.assign(instance, { setOptions }),\n  )\n}\n\nexport function createVirtualizer<\n  TScrollElement extends Element,\n  TItemElement extends Element,\n>(\n  options: PartialKeys<\n    VirtualizerOptions<TScrollElement, TItemElement>,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  >,\n): Readable<SvelteVirtualizer<TScrollElement, TItemElement>> {\n  return createVirtualizerBase<TScrollElement, TItemElement>({\n    observeElementRect: observeElementRect,\n    observeElementOffset: observeElementOffset,\n    scrollToFn: elementScroll,\n    ...options,\n  })\n}\n\nexport function createWindowVirtualizer<TItemElement extends Element>(\n  options: PartialKeys<\n    VirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): Readable<SvelteVirtualizer<Window, TItemElement>> {\n  return createVirtualizerBase<Window, TItemElement>({\n    getScrollElement: () => (typeof document !== 'undefined' ? window : null),\n    observeElementRect: observeWindowRect,\n    observeElementOffset: observeWindowOffset,\n    scrollToFn: windowScroll,\n    initialOffset: () => (typeof document !== 'undefined' ? window.scrollY : 0),\n    ...options,\n  })\n}\n"
  },
  {
    "path": "packages/svelte-virtual/svelte.config.js",
    "content": "import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n\nconst config = {\n  preprocess: vitePreprocess(),\n}\n\nexport default config\n"
  },
  {
    "path": "packages/svelte-virtual/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\n    \"src/**/*.js\",\n    \"src/**/*.ts\",\n    \"src/**/*.svelte\",\n    \"eslint.config.js\",\n    \"svelte.config.js\",\n    \"vite.config.ts\"\n  ]\n}\n"
  },
  {
    "path": "packages/svelte-virtual/vite.config.ts",
    "content": "import { svelte } from '@sveltejs/vite-plugin-svelte'\nimport { defineConfig } from 'vitest/config'\n\nexport default defineConfig({\n  plugins: [svelte()],\n})\n"
  },
  {
    "path": "packages/virtual-core/CHANGELOG.md",
    "content": "# @tanstack/virtual-core\n\n## 3.13.23\n\n### Patch Changes\n\n- fix(virtual-core): remove incorrect elementsCache cleanup using getItemKey ([#1148](https://github.com/TanStack/virtual/pull/1148))\n\n## 3.13.22\n\n### Patch Changes\n\n- Add 'instant' to ScrollBehavior type to match the W3C spec ([#1122](https://github.com/TanStack/virtual/pull/1122))\n\n- perf(virtual-core): skip sync DOM reads during normal scrolling ([#1146](https://github.com/TanStack/virtual/pull/1146))\n\n## 3.13.21\n\n### Patch Changes\n\n- fix(virtual-core): smooth scrolling for dynamic item sizes ([#1108](https://github.com/TanStack/virtual/pull/1108))\n\n## 3.13.20\n\n### Patch Changes\n\n- fix(virtual-core): early return in \\_measureElement for disconnected nodes ([#1135](https://github.com/TanStack/virtual/pull/1135))\n\n## 3.13.19\n\n### Patch Changes\n\n- Fix crash when component unmounts during `scrollToIndex` by adding a null guard for `targetWindow` inside the `requestAnimationFrame` callback ([#1129](https://github.com/TanStack/virtual/pull/1129))\n\n## 3.13.18\n\n### Patch Changes\n\n- revert(virtual-core): \"notify framework when count changes\" 2542c5a ([#1112](https://github.com/TanStack/virtual/pull/1112))\n\n## 3.13.17\n\n### Patch Changes\n\n- fix(virtual-core): preserve auto alignment for visible items when scrolling ([#1110](https://github.com/TanStack/virtual/pull/1110))\n\n## 3.13.16\n\n### Patch Changes\n\n- fix(virtual-core): improve scrollToIndex reliability in dynamic mode ([#1106](https://github.com/TanStack/virtual/pull/1106))\n  - Wait extra frame for ResizeObserver measurements before verifying position\n  - Abort pending scroll operations when new scrollToIndex is called\n\n## 3.13.15\n\n### Patch Changes\n\n- fix(virtual-core): scroll to last index properly ([#1105](https://github.com/TanStack/virtual/pull/1105))\n\n## 3.13.14\n\n### Patch Changes\n\n- Fix: Correct lane assignments when lane count changes dynamically ([#1095](https://github.com/TanStack/virtual/pull/1095))\n\n  Fixed a critical bug where changing the number of lanes dynamically would cause layout breakage with incorrect lane assignments. When the lane count changed (e.g., from 3 to 2 columns in a responsive masonry layout), some virtual items would retain their old lane numbers, causing out-of-bounds errors and broken layouts.\n\n  **Root Cause**: After clearing measurements cache on lane change, the virtualizer was incorrectly restoring data from `initialMeasurementsCache`, which contained stale lane assignments from the previous lane count.\n\n  **Fix**: Skip `initialMeasurementsCache` restoration during lane transitions by checking the `lanesSettling` flag. This ensures all measurements are recalculated with correct lane assignments for the new lane count.\n\n  **Before**:\n\n  ```typescript\n  // With lanes = 2\n  virtualItems.forEach((item) => {\n    columns[item.lane].push(item) // ❌ Error: item.lane could be 3\n  })\n  ```\n\n  **After**:\n\n  ```typescript\n  // With lanes = 2\n  virtualItems.forEach((item) => {\n    columns[item.lane].push(item) // ✅ item.lane is always 0 or 1\n  })\n  ```\n\n  This fix is essential for responsive masonry layouts where column count changes based on viewport width. No performance impact as it only affects the lane change transition path.\n\n## 3.13.13\n\n### Patch Changes\n\n- Fix: Notify framework when count changes to update getTotalSize() ([#1085](https://github.com/TanStack/virtual/pull/1085))\n\n  Fixed an issue where `getTotalSize()` would return stale values when the `count` option changed (e.g., during filtering or search operations). The virtualizer now automatically notifies the framework when measurement-affecting options change, ensuring the UI updates correctly without requiring manual `useMemo` workarounds.\n\n  **Before**: When filtering items, the list container would maintain its previous height, causing excessive blank space (when count decreased) or inaccessible items (when count increased).\n\n  **After**: Height updates automatically when count changes, providing the correct user experience.\n\n  This fix applies to all framework adapters and has minimal performance impact (< 0.1ms per change).\n\n- fix: stabilize lane assignments in masonry layout ([#1080](https://github.com/TanStack/virtual/pull/1080))\n\n  Added lane assignment caching to prevent items from jumping between lanes when viewport is resized. Previously, items could shift to different lanes during resize due to recalculating \"shortest lane\" with slightly different heights.\n\n  Changes:\n  - Added `laneAssignments` cache (Map<index, lane>) to persist lane assignments\n  - Lane cache is cleared when `lanes` option changes or `measure()` is called\n  - Lane cache is cleaned up when `count` decreases (removes stale entries)\n  - Lane cache is cleared when virtualizer is disabled\n\n## 3.13.12\n\n### Patch Changes\n\n- fix(virtual-core): scroll to index doesn't scroll to bottom correctly ([#1029](https://github.com/TanStack/virtual/pull/1029))\n\n## 3.13.11\n\n### Patch Changes\n\n- Revert \"Adapt default logic to adjust scroll position only on backward scrolling (#1002)\" ([#1026](https://github.com/TanStack/virtual/pull/1026))\n\n## 3.13.10\n\n### Patch Changes\n\n- fix(virtual-core): Adapt default logic to adjust scroll position only on backward scrolling ([#1002](https://github.com/TanStack/virtual/pull/1002))\n\n## 3.13.9\n\n### Patch Changes\n\n- fix(virtual-core): fix `Error: Unexpected undefined` ([#1004](https://github.com/TanStack/virtual/pull/1004))\n\n## 3.13.8\n\n### Patch Changes\n\n- fix(virtual-core): loosen approxEqual to allow 1px difference ([#995](https://github.com/TanStack/virtual/pull/995))\n\n## 3.13.7\n\n### Patch Changes\n\n- fix(virtual-core): prevent measurement jitter when scale is applied ([#986](https://github.com/TanStack/virtual/pull/986))\n\n## 3.13.6\n\n### Patch Changes\n\n- fix(virtual-core): fix total size calculation for single item in multi-lane ([`042616f`](https://github.com/TanStack/virtual/commit/042616f39ced842470db0b4b40fca77f22454b7f))\n\n## 3.13.5\n\n### Patch Changes\n\n- fix(core): handle case when item count is less than or equal to lanes ([#964](https://github.com/TanStack/virtual/pull/964))\n\n## 3.13.4\n\n### Patch Changes\n\n- fix(virtual-core): update maybeNotify cache when deps change ([#957](https://github.com/TanStack/virtual/pull/957))\n\n- fix(virtual-core): set `useScrollendEvent` default to false for bette… ([#951](https://github.com/TanStack/virtual/pull/951))\n\n## 3.13.3\n\n### Patch Changes\n\n- fix(virtual-core): expand range in masonry layouts to catch items from all lanes ([#937](https://github.com/TanStack/virtual/pull/937))\n"
  },
  {
    "path": "packages/virtual-core/eslint.config.js",
    "content": "// @ts-check\n\nimport rootConfig from '../../eslint.config.js'\n\nexport default [...rootConfig]\n"
  },
  {
    "path": "packages/virtual-core/package.json",
    "content": "{\n  \"name\": \"@tanstack/virtual-core\",\n  \"version\": \"3.13.23\",\n  \"description\": \"Headless UI for virtualizing scrollable elements in TS/JS + Frameworks\",\n  \"author\": \"Tanner Linsley\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\",\n    \"directory\": \"packages/virtual-core\"\n  },\n  \"homepage\": \"https://tanstack.com/virtual\",\n  \"funding\": {\n    \"type\": \"github\",\n    \"url\": \"https://github.com/sponsors/tannerlinsley\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"vue\",\n    \"solid\",\n    \"virtual\",\n    \"virtual-core\",\n    \"datagrid\"\n  ],\n  \"scripts\": {\n    \"clean\": \"premove ./dist ./coverage\",\n    \"test:eslint\": \"eslint ./src\",\n    \"test:types\": \"tsc\",\n    \"test:lib\": \"vitest\",\n    \"test:lib:dev\": \"pnpm run test:lib --watch\",\n    \"test:build\": \"publint --strict\",\n    \"build\": \"vite build\"\n  },\n  \"type\": \"module\",\n  \"types\": \"dist/esm/index.d.ts\",\n  \"main\": \"dist/cjs/index.cjs\",\n  \"module\": \"dist/esm/index.js\",\n  \"exports\": {\n    \".\": {\n      \"import\": {\n        \"types\": \"./dist/esm/index.d.ts\",\n        \"default\": \"./dist/esm/index.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/cjs/index.d.cts\",\n        \"default\": \"./dist/cjs/index.cjs\"\n      }\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ]\n}\n"
  },
  {
    "path": "packages/virtual-core/src/index.ts",
    "content": "import { approxEqual, debounce, memo, notUndefined } from './utils'\n\nexport * from './utils'\n\n//\n\ntype ScrollDirection = 'forward' | 'backward'\n\ntype ScrollAlignment = 'start' | 'center' | 'end' | 'auto'\n\ntype ScrollBehavior = 'auto' | 'smooth' | 'instant'\n\nexport interface ScrollToOptions {\n  align?: ScrollAlignment\n  behavior?: ScrollBehavior\n}\n\ntype ScrollToOffsetOptions = ScrollToOptions\n\ntype ScrollToIndexOptions = ScrollToOptions\n\nexport interface Range {\n  startIndex: number\n  endIndex: number\n  overscan: number\n  count: number\n}\n\ntype Key = number | string | bigint\n\nexport interface VirtualItem {\n  key: Key\n  index: number\n  start: number\n  end: number\n  size: number\n  lane: number\n}\n\nexport interface Rect {\n  width: number\n  height: number\n}\n\n//\n\nconst getRect = (element: HTMLElement): Rect => {\n  const { offsetWidth, offsetHeight } = element\n  return { width: offsetWidth, height: offsetHeight }\n}\n\nexport const defaultKeyExtractor = (index: number) => index\n\nexport const defaultRangeExtractor = (range: Range) => {\n  const start = Math.max(range.startIndex - range.overscan, 0)\n  const end = Math.min(range.endIndex + range.overscan, range.count - 1)\n\n  const arr = []\n\n  for (let i = start; i <= end; i++) {\n    arr.push(i)\n  }\n\n  return arr\n}\n\nexport const observeElementRect = <T extends Element>(\n  instance: Virtualizer<T, any>,\n  cb: (rect: Rect) => void,\n) => {\n  const element = instance.scrollElement\n  if (!element) {\n    return\n  }\n  const targetWindow = instance.targetWindow\n  if (!targetWindow) {\n    return\n  }\n\n  const handler = (rect: Rect) => {\n    const { width, height } = rect\n    cb({ width: Math.round(width), height: Math.round(height) })\n  }\n\n  handler(getRect(element as unknown as HTMLElement))\n\n  if (!targetWindow.ResizeObserver) {\n    return () => {}\n  }\n\n  const observer = new targetWindow.ResizeObserver((entries) => {\n    const run = () => {\n      const entry = entries[0]\n      if (entry?.borderBoxSize) {\n        const box = entry.borderBoxSize[0]\n        if (box) {\n          handler({ width: box.inlineSize, height: box.blockSize })\n          return\n        }\n      }\n      handler(getRect(element as unknown as HTMLElement))\n    }\n\n    instance.options.useAnimationFrameWithResizeObserver\n      ? requestAnimationFrame(run)\n      : run()\n  })\n\n  observer.observe(element, { box: 'border-box' })\n\n  return () => {\n    observer.unobserve(element)\n  }\n}\n\nconst addEventListenerOptions = {\n  passive: true,\n}\n\nexport const observeWindowRect = (\n  instance: Virtualizer<Window, any>,\n  cb: (rect: Rect) => void,\n) => {\n  const element = instance.scrollElement\n  if (!element) {\n    return\n  }\n\n  const handler = () => {\n    cb({ width: element.innerWidth, height: element.innerHeight })\n  }\n  handler()\n\n  element.addEventListener('resize', handler, addEventListenerOptions)\n\n  return () => {\n    element.removeEventListener('resize', handler)\n  }\n}\n\nconst supportsScrollend =\n  typeof window == 'undefined' ? true : 'onscrollend' in window\n\ntype ObserveOffsetCallBack = (offset: number, isScrolling: boolean) => void\n\nexport const observeElementOffset = <T extends Element>(\n  instance: Virtualizer<T, any>,\n  cb: ObserveOffsetCallBack,\n) => {\n  const element = instance.scrollElement\n  if (!element) {\n    return\n  }\n  const targetWindow = instance.targetWindow\n  if (!targetWindow) {\n    return\n  }\n\n  let offset = 0\n  const fallback =\n    instance.options.useScrollendEvent && supportsScrollend\n      ? () => undefined\n      : debounce(\n          targetWindow,\n          () => {\n            cb(offset, false)\n          },\n          instance.options.isScrollingResetDelay,\n        )\n\n  const createHandler = (isScrolling: boolean) => () => {\n    const { horizontal, isRtl } = instance.options\n    offset = horizontal\n      ? element['scrollLeft'] * ((isRtl && -1) || 1)\n      : element['scrollTop']\n    fallback()\n    cb(offset, isScrolling)\n  }\n  const handler = createHandler(true)\n  const endHandler = createHandler(false)\n\n  element.addEventListener('scroll', handler, addEventListenerOptions)\n  const registerScrollendEvent =\n    instance.options.useScrollendEvent && supportsScrollend\n  if (registerScrollendEvent) {\n    element.addEventListener('scrollend', endHandler, addEventListenerOptions)\n  }\n  return () => {\n    element.removeEventListener('scroll', handler)\n    if (registerScrollendEvent) {\n      element.removeEventListener('scrollend', endHandler)\n    }\n  }\n}\n\nexport const observeWindowOffset = (\n  instance: Virtualizer<Window, any>,\n  cb: ObserveOffsetCallBack,\n) => {\n  const element = instance.scrollElement\n  if (!element) {\n    return\n  }\n  const targetWindow = instance.targetWindow\n  if (!targetWindow) {\n    return\n  }\n\n  let offset = 0\n  const fallback =\n    instance.options.useScrollendEvent && supportsScrollend\n      ? () => undefined\n      : debounce(\n          targetWindow,\n          () => {\n            cb(offset, false)\n          },\n          instance.options.isScrollingResetDelay,\n        )\n\n  const createHandler = (isScrolling: boolean) => () => {\n    offset = element[instance.options.horizontal ? 'scrollX' : 'scrollY']\n    fallback()\n    cb(offset, isScrolling)\n  }\n  const handler = createHandler(true)\n  const endHandler = createHandler(false)\n\n  element.addEventListener('scroll', handler, addEventListenerOptions)\n  const registerScrollendEvent =\n    instance.options.useScrollendEvent && supportsScrollend\n  if (registerScrollendEvent) {\n    element.addEventListener('scrollend', endHandler, addEventListenerOptions)\n  }\n  return () => {\n    element.removeEventListener('scroll', handler)\n    if (registerScrollendEvent) {\n      element.removeEventListener('scrollend', endHandler)\n    }\n  }\n}\n\nexport const measureElement = <TItemElement extends Element>(\n  element: TItemElement,\n  entry: ResizeObserverEntry | undefined,\n  instance: Virtualizer<any, TItemElement>,\n) => {\n  if (entry?.borderBoxSize) {\n    const box = entry.borderBoxSize[0]\n    if (box) {\n      const size = Math.round(\n        box[instance.options.horizontal ? 'inlineSize' : 'blockSize'],\n      )\n      return size\n    }\n  }\n\n  return (element as unknown as HTMLElement)[\n    instance.options.horizontal ? 'offsetWidth' : 'offsetHeight'\n  ]\n}\n\nexport const windowScroll = <T extends Window>(\n  offset: number,\n  {\n    adjustments = 0,\n    behavior,\n  }: { adjustments?: number; behavior?: ScrollBehavior },\n  instance: Virtualizer<T, any>,\n) => {\n  const toOffset = offset + adjustments\n\n  instance.scrollElement?.scrollTo?.({\n    [instance.options.horizontal ? 'left' : 'top']: toOffset,\n    behavior,\n  })\n}\n\nexport const elementScroll = <T extends Element>(\n  offset: number,\n  {\n    adjustments = 0,\n    behavior,\n  }: { adjustments?: number; behavior?: ScrollBehavior },\n  instance: Virtualizer<T, any>,\n) => {\n  const toOffset = offset + adjustments\n\n  instance.scrollElement?.scrollTo?.({\n    [instance.options.horizontal ? 'left' : 'top']: toOffset,\n    behavior,\n  })\n}\n\nexport interface VirtualizerOptions<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n> {\n  // Required from the user\n  count: number\n  getScrollElement: () => TScrollElement | null\n  estimateSize: (index: number) => number\n\n  // Required from the framework adapter (but can be overridden)\n  scrollToFn: (\n    offset: number,\n    options: { adjustments?: number; behavior?: ScrollBehavior },\n    instance: Virtualizer<TScrollElement, TItemElement>,\n  ) => void\n  observeElementRect: (\n    instance: Virtualizer<TScrollElement, TItemElement>,\n    cb: (rect: Rect) => void,\n  ) => void | (() => void)\n  observeElementOffset: (\n    instance: Virtualizer<TScrollElement, TItemElement>,\n    cb: ObserveOffsetCallBack,\n  ) => void | (() => void)\n  // Optional\n  debug?: boolean\n  initialRect?: Rect\n  onChange?: (\n    instance: Virtualizer<TScrollElement, TItemElement>,\n    sync: boolean,\n  ) => void\n  measureElement?: (\n    element: TItemElement,\n    entry: ResizeObserverEntry | undefined,\n    instance: Virtualizer<TScrollElement, TItemElement>,\n  ) => number\n  overscan?: number\n  horizontal?: boolean\n  paddingStart?: number\n  paddingEnd?: number\n  scrollPaddingStart?: number\n  scrollPaddingEnd?: number\n  initialOffset?: number | (() => number)\n  getItemKey?: (index: number) => Key\n  rangeExtractor?: (range: Range) => Array<number>\n  scrollMargin?: number\n  gap?: number\n  indexAttribute?: string\n  initialMeasurementsCache?: Array<VirtualItem>\n  lanes?: number\n  isScrollingResetDelay?: number\n  useScrollendEvent?: boolean\n  enabled?: boolean\n  isRtl?: boolean\n  useAnimationFrameWithResizeObserver?: boolean\n}\n\ntype ScrollState = {\n  // what we want\n  index: number | null\n  align: ScrollAlignment\n  behavior: ScrollBehavior\n\n  // lifecycle\n  startedAt: number\n\n  // target tracking\n  lastTargetOffset: number\n\n  // settling\n  stableFrames: number\n}\n\nexport class Virtualizer<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n> {\n  private unsubs: Array<void | (() => void)> = []\n  options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>\n  scrollElement: TScrollElement | null = null\n  targetWindow: (Window & typeof globalThis) | null = null\n  isScrolling = false\n  private scrollState: ScrollState | null = null\n  measurementsCache: Array<VirtualItem> = []\n  private itemSizeCache = new Map<Key, number>()\n  private laneAssignments = new Map<number, number>() // index → lane cache\n  private pendingMeasuredCacheIndexes: Array<number> = []\n  private prevLanes: number | undefined = undefined\n  private lanesChangedFlag = false\n  private lanesSettling = false\n  scrollRect: Rect | null = null\n  scrollOffset: number | null = null\n  scrollDirection: ScrollDirection | null = null\n  private scrollAdjustments = 0\n  shouldAdjustScrollPositionOnItemSizeChange:\n    | undefined\n    | ((\n        item: VirtualItem,\n        delta: number,\n        instance: Virtualizer<TScrollElement, TItemElement>,\n      ) => boolean)\n  elementsCache = new Map<Key, TItemElement>()\n  private now = () => this.targetWindow?.performance?.now?.() ?? Date.now()\n  private observer = (() => {\n    let _ro: ResizeObserver | null = null\n\n    const get = () => {\n      if (_ro) {\n        return _ro\n      }\n\n      if (!this.targetWindow || !this.targetWindow.ResizeObserver) {\n        return null\n      }\n\n      return (_ro = new this.targetWindow.ResizeObserver((entries) => {\n        entries.forEach((entry) => {\n          const run = () => {\n            const node = entry.target as TItemElement\n            const index = this.indexFromElement(node)\n\n            if (!node.isConnected) {\n              this.observer.unobserve(node)\n              return\n            }\n\n            if (this.shouldMeasureDuringScroll(index)) {\n              this.resizeItem(\n                index,\n                this.options.measureElement(node, entry, this),\n              )\n            }\n          }\n          this.options.useAnimationFrameWithResizeObserver\n            ? requestAnimationFrame(run)\n            : run()\n        })\n      }))\n    }\n\n    return {\n      disconnect: () => {\n        get()?.disconnect()\n        _ro = null\n      },\n      observe: (target: Element) =>\n        get()?.observe(target, { box: 'border-box' }),\n      unobserve: (target: Element) => get()?.unobserve(target),\n    }\n  })()\n  range: { startIndex: number; endIndex: number } | null = null\n\n  constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {\n    this.setOptions(opts)\n  }\n\n  setOptions = (opts: VirtualizerOptions<TScrollElement, TItemElement>) => {\n    Object.entries(opts).forEach(([key, value]) => {\n      if (typeof value === 'undefined') delete (opts as any)[key]\n    })\n\n    this.options = {\n      debug: false,\n      initialOffset: 0,\n      overscan: 1,\n      paddingStart: 0,\n      paddingEnd: 0,\n      scrollPaddingStart: 0,\n      scrollPaddingEnd: 0,\n      horizontal: false,\n      getItemKey: defaultKeyExtractor,\n      rangeExtractor: defaultRangeExtractor,\n      onChange: () => {},\n      measureElement,\n      initialRect: { width: 0, height: 0 },\n      scrollMargin: 0,\n      gap: 0,\n      indexAttribute: 'data-index',\n      initialMeasurementsCache: [],\n      lanes: 1,\n      isScrollingResetDelay: 150,\n      enabled: true,\n      isRtl: false,\n      useScrollendEvent: false,\n      useAnimationFrameWithResizeObserver: false,\n      ...opts,\n    }\n  }\n\n  private notify = (sync: boolean) => {\n    this.options.onChange?.(this, sync)\n  }\n\n  private maybeNotify = memo(\n    () => {\n      this.calculateRange()\n\n      return [\n        this.isScrolling,\n        this.range ? this.range.startIndex : null,\n        this.range ? this.range.endIndex : null,\n      ]\n    },\n    (isScrolling) => {\n      this.notify(isScrolling)\n    },\n    {\n      key: process.env.NODE_ENV !== 'production' && 'maybeNotify',\n      debug: () => this.options.debug,\n      initialDeps: [\n        this.isScrolling,\n        this.range ? this.range.startIndex : null,\n        this.range ? this.range.endIndex : null,\n      ] as [boolean, number | null, number | null],\n    },\n  )\n\n  private cleanup = () => {\n    this.unsubs.filter(Boolean).forEach((d) => d!())\n    this.unsubs = []\n    this.observer.disconnect()\n    if (this.rafId != null && this.targetWindow) {\n      this.targetWindow.cancelAnimationFrame(this.rafId)\n      this.rafId = null\n    }\n    this.scrollState = null\n    this.scrollElement = null\n    this.targetWindow = null\n  }\n\n  _didMount = () => {\n    return () => {\n      this.cleanup()\n    }\n  }\n\n  _willUpdate = () => {\n    const scrollElement = this.options.enabled\n      ? this.options.getScrollElement()\n      : null\n\n    if (this.scrollElement !== scrollElement) {\n      this.cleanup()\n\n      if (!scrollElement) {\n        this.maybeNotify()\n        return\n      }\n\n      this.scrollElement = scrollElement\n\n      if (this.scrollElement && 'ownerDocument' in this.scrollElement) {\n        this.targetWindow = this.scrollElement.ownerDocument.defaultView\n      } else {\n        this.targetWindow = this.scrollElement?.window ?? null\n      }\n\n      this.elementsCache.forEach((cached) => {\n        this.observer.observe(cached)\n      })\n\n      this.unsubs.push(\n        this.options.observeElementRect(this, (rect) => {\n          this.scrollRect = rect\n          this.maybeNotify()\n        }),\n      )\n\n      this.unsubs.push(\n        this.options.observeElementOffset(this, (offset, isScrolling) => {\n          this.scrollAdjustments = 0\n          this.scrollDirection = isScrolling\n            ? this.getScrollOffset() < offset\n              ? 'forward'\n              : 'backward'\n            : null\n          this.scrollOffset = offset\n          this.isScrolling = isScrolling\n\n          if (this.scrollState) {\n            this.scheduleScrollReconcile()\n          }\n          this.maybeNotify()\n        }),\n      )\n\n      this._scrollToOffset(this.getScrollOffset(), {\n        adjustments: undefined,\n        behavior: undefined,\n      })\n    }\n  }\n\n  private rafId: number | null = null\n  private scheduleScrollReconcile() {\n    if (!this.targetWindow) {\n      this.scrollState = null\n      return\n    }\n    if (this.rafId != null) return\n    this.rafId = this.targetWindow.requestAnimationFrame(() => {\n      this.rafId = null\n      this.reconcileScroll()\n    })\n  }\n  private reconcileScroll() {\n    if (!this.scrollState) return\n\n    const el = this.scrollElement\n    if (!el) return\n\n    // Safety valve: bail out if reconciliation has been running too long\n    const MAX_RECONCILE_MS = 5000\n    if (this.now() - this.scrollState.startedAt > MAX_RECONCILE_MS) {\n      this.scrollState = null\n      return\n    }\n\n    const offsetInfo =\n      this.scrollState.index != null\n        ? this.getOffsetForIndex(this.scrollState.index, this.scrollState.align)\n        : undefined\n    const targetOffset = offsetInfo\n      ? offsetInfo[0]\n      : this.scrollState.lastTargetOffset\n\n    // Require one stable frame where target matches scroll offset.\n    // approxEqual() already tolerates minor fluctuations, so one frame is sufficient\n    // to confirm scroll has reached its target without premature cleanup.\n    const STABLE_FRAMES = 1\n\n    const targetChanged = targetOffset !== this.scrollState.lastTargetOffset\n\n    if (!targetChanged && approxEqual(targetOffset, this.getScrollOffset())) {\n      this.scrollState.stableFrames++\n      if (this.scrollState.stableFrames >= STABLE_FRAMES) {\n        this.scrollState = null\n        return\n      }\n    } else {\n      this.scrollState.stableFrames = 0\n\n      if (targetChanged) {\n        this.scrollState.lastTargetOffset = targetOffset\n        // Switch to 'auto' behavior once measurements cause target to change\n        // We want to jump directly to the correct position, not smoothly animate to it\n        this.scrollState.behavior = 'auto'\n\n        this._scrollToOffset(targetOffset, {\n          adjustments: undefined,\n          behavior: 'auto',\n        })\n      }\n    }\n\n    // Always reschedule while scrollState is active to guarantee\n    // the safety valve timeout runs even if no scroll events fire\n    // (e.g. no-op scrollToFn, detached element)\n    this.scheduleScrollReconcile()\n  }\n\n  private getSize = () => {\n    if (!this.options.enabled) {\n      this.scrollRect = null\n      return 0\n    }\n\n    this.scrollRect = this.scrollRect ?? this.options.initialRect\n\n    return this.scrollRect[this.options.horizontal ? 'width' : 'height']\n  }\n\n  private getScrollOffset = () => {\n    if (!this.options.enabled) {\n      this.scrollOffset = null\n      return 0\n    }\n\n    this.scrollOffset =\n      this.scrollOffset ??\n      (typeof this.options.initialOffset === 'function'\n        ? this.options.initialOffset()\n        : this.options.initialOffset)\n\n    return this.scrollOffset\n  }\n\n  private getFurthestMeasurement = (\n    measurements: Array<VirtualItem>,\n    index: number,\n  ) => {\n    const furthestMeasurementsFound = new Map<number, true>()\n    const furthestMeasurements = new Map<number, VirtualItem>()\n    for (let m = index - 1; m >= 0; m--) {\n      const measurement = measurements[m]!\n\n      if (furthestMeasurementsFound.has(measurement.lane)) {\n        continue\n      }\n\n      const previousFurthestMeasurement = furthestMeasurements.get(\n        measurement.lane,\n      )\n      if (\n        previousFurthestMeasurement == null ||\n        measurement.end > previousFurthestMeasurement.end\n      ) {\n        furthestMeasurements.set(measurement.lane, measurement)\n      } else if (measurement.end < previousFurthestMeasurement.end) {\n        furthestMeasurementsFound.set(measurement.lane, true)\n      }\n\n      if (furthestMeasurementsFound.size === this.options.lanes) {\n        break\n      }\n    }\n\n    return furthestMeasurements.size === this.options.lanes\n      ? Array.from(furthestMeasurements.values()).sort((a, b) => {\n          if (a.end === b.end) {\n            return a.index - b.index\n          }\n\n          return a.end - b.end\n        })[0]\n      : undefined\n  }\n\n  private getMeasurementOptions = memo(\n    () => [\n      this.options.count,\n      this.options.paddingStart,\n      this.options.scrollMargin,\n      this.options.getItemKey,\n      this.options.enabled,\n      this.options.lanes,\n    ],\n    (count, paddingStart, scrollMargin, getItemKey, enabled, lanes) => {\n      const lanesChanged =\n        this.prevLanes !== undefined && this.prevLanes !== lanes\n\n      if (lanesChanged) {\n        // Set flag for getMeasurements to handle\n        this.lanesChangedFlag = true\n      }\n\n      this.prevLanes = lanes\n      this.pendingMeasuredCacheIndexes = []\n\n      return {\n        count,\n        paddingStart,\n        scrollMargin,\n        getItemKey,\n        enabled,\n        lanes,\n      }\n    },\n    {\n      key: false,\n    },\n  )\n\n  private getMeasurements = memo(\n    () => [this.getMeasurementOptions(), this.itemSizeCache],\n    (\n      { count, paddingStart, scrollMargin, getItemKey, enabled, lanes },\n      itemSizeCache,\n    ) => {\n      if (!enabled) {\n        this.measurementsCache = []\n        this.itemSizeCache.clear()\n        this.laneAssignments.clear()\n        return []\n      }\n\n      // Clean up stale lane cache entries when count decreases\n      if (this.laneAssignments.size > count) {\n        for (const index of this.laneAssignments.keys()) {\n          if (index >= count) {\n            this.laneAssignments.delete(index)\n          }\n        }\n      }\n\n      // ✅ Force complete recalculation when lanes change\n      if (this.lanesChangedFlag) {\n        this.lanesChangedFlag = false // Reset immediately\n        this.lanesSettling = true // Start settling period\n        this.measurementsCache = []\n        this.itemSizeCache.clear()\n        this.laneAssignments.clear() // Clear lane cache for new lane count\n        // Clear pending indexes to force min = 0\n        this.pendingMeasuredCacheIndexes = []\n      }\n\n      // Don't restore from initialMeasurementsCache during lane changes\n      // as it contains stale lane assignments from the previous lane count\n      if (this.measurementsCache.length === 0 && !this.lanesSettling) {\n        this.measurementsCache = this.options.initialMeasurementsCache\n        this.measurementsCache.forEach((item) => {\n          this.itemSizeCache.set(item.key, item.size)\n        })\n      }\n\n      // ✅ During lanes settling, ignore pendingMeasuredCacheIndexes to prevent repositioning\n      const min = this.lanesSettling\n        ? 0\n        : this.pendingMeasuredCacheIndexes.length > 0\n          ? Math.min(...this.pendingMeasuredCacheIndexes)\n          : 0\n      this.pendingMeasuredCacheIndexes = []\n\n      // ✅ End settling period when cache is fully built\n      if (this.lanesSettling && this.measurementsCache.length === count) {\n        this.lanesSettling = false\n      }\n\n      const measurements = this.measurementsCache.slice(0, min)\n\n      // ✅ Performance: Track last item index per lane for O(1) lookup\n      const laneLastIndex: Array<number | undefined> = new Array(lanes).fill(\n        undefined,\n      )\n\n      // Initialize from existing measurements (before min)\n      for (let m = 0; m < min; m++) {\n        const item = measurements[m]\n        if (item) {\n          laneLastIndex[item.lane] = m\n        }\n      }\n\n      for (let i = min; i < count; i++) {\n        const key = getItemKey(i)\n\n        // Check for cached lane assignment\n        const cachedLane = this.laneAssignments.get(i)\n        let lane: number\n        let start: number\n\n        if (cachedLane !== undefined && this.options.lanes > 1) {\n          // Use cached lane - O(1) lookup for previous item in same lane\n          lane = cachedLane\n          const prevIndex = laneLastIndex[lane]\n          const prevInLane =\n            prevIndex !== undefined ? measurements[prevIndex] : undefined\n          start = prevInLane\n            ? prevInLane.end + this.options.gap\n            : paddingStart + scrollMargin\n        } else {\n          // No cache - use original logic (find shortest lane)\n          const furthestMeasurement =\n            this.options.lanes === 1\n              ? measurements[i - 1]\n              : this.getFurthestMeasurement(measurements, i)\n\n          start = furthestMeasurement\n            ? furthestMeasurement.end + this.options.gap\n            : paddingStart + scrollMargin\n\n          lane = furthestMeasurement\n            ? furthestMeasurement.lane\n            : i % this.options.lanes\n\n          // Cache the lane assignment\n          if (this.options.lanes > 1) {\n            this.laneAssignments.set(i, lane)\n          }\n        }\n\n        const measuredSize = itemSizeCache.get(key)\n        const size =\n          typeof measuredSize === 'number'\n            ? measuredSize\n            : this.options.estimateSize(i)\n\n        const end = start + size\n\n        measurements[i] = {\n          index: i,\n          start,\n          size,\n          end,\n          key,\n          lane,\n        }\n\n        // ✅ Performance: Update lane's last item index\n        laneLastIndex[lane] = i\n      }\n\n      this.measurementsCache = measurements\n\n      return measurements\n    },\n    {\n      key: process.env.NODE_ENV !== 'production' && 'getMeasurements',\n      debug: () => this.options.debug,\n    },\n  )\n\n  calculateRange = memo(\n    () => [\n      this.getMeasurements(),\n      this.getSize(),\n      this.getScrollOffset(),\n      this.options.lanes,\n    ],\n    (measurements, outerSize, scrollOffset, lanes) => {\n      return (this.range =\n        measurements.length > 0 && outerSize > 0\n          ? calculateRange({\n              measurements,\n              outerSize,\n              scrollOffset,\n              lanes,\n            })\n          : null)\n    },\n    {\n      key: process.env.NODE_ENV !== 'production' && 'calculateRange',\n      debug: () => this.options.debug,\n    },\n  )\n\n  getVirtualIndexes = memo(\n    () => {\n      let startIndex: number | null = null\n      let endIndex: number | null = null\n      const range = this.calculateRange()\n      if (range) {\n        startIndex = range.startIndex\n        endIndex = range.endIndex\n      }\n      this.maybeNotify.updateDeps([this.isScrolling, startIndex, endIndex])\n      return [\n        this.options.rangeExtractor,\n        this.options.overscan,\n        this.options.count,\n        startIndex,\n        endIndex,\n      ]\n    },\n    (rangeExtractor, overscan, count, startIndex, endIndex) => {\n      return startIndex === null || endIndex === null\n        ? []\n        : rangeExtractor({\n            startIndex,\n            endIndex,\n            overscan,\n            count,\n          })\n    },\n    {\n      key: process.env.NODE_ENV !== 'production' && 'getVirtualIndexes',\n      debug: () => this.options.debug,\n    },\n  )\n\n  indexFromElement = (node: TItemElement) => {\n    const attributeName = this.options.indexAttribute\n    const indexStr = node.getAttribute(attributeName)\n\n    if (!indexStr) {\n      console.warn(\n        `Missing attribute name '${attributeName}={index}' on measured element.`,\n      )\n      return -1\n    }\n\n    return parseInt(indexStr, 10)\n  }\n\n  /**\n   * Determines if an item at the given index should be measured during smooth scroll.\n   * During smooth scroll, only items within a buffer range around the target are measured\n   * to prevent items far from the target from pushing it away.\n   */\n  private shouldMeasureDuringScroll = (index: number): boolean => {\n    // No scroll state or not smooth scroll - always allow measurements\n    if (!this.scrollState || this.scrollState.behavior !== 'smooth') {\n      return true\n    }\n\n    const scrollIndex =\n      this.scrollState.index ??\n      this.getVirtualItemForOffset(this.scrollState.lastTargetOffset)?.index\n\n    if (scrollIndex !== undefined && this.range) {\n      // Allow measurements within a buffer range around the scroll target\n      const bufferSize = Math.max(\n        this.options.overscan,\n        Math.ceil((this.range.endIndex - this.range.startIndex) / 2),\n      )\n      const minIndex = Math.max(0, scrollIndex - bufferSize)\n      const maxIndex = Math.min(\n        this.options.count - 1,\n        scrollIndex + bufferSize,\n      )\n      return index >= minIndex && index <= maxIndex\n    }\n\n    return true\n  }\n\n  measureElement = (node: TItemElement | null) => {\n    if (!node) {\n      this.elementsCache.forEach((cached, key) => {\n        if (!cached.isConnected) {\n          this.observer.unobserve(cached)\n          this.elementsCache.delete(key)\n        }\n      })\n      return\n    }\n\n    const index = this.indexFromElement(node)\n    const key = this.options.getItemKey(index)\n    const prevNode = this.elementsCache.get(key)\n\n    if (prevNode !== node) {\n      if (prevNode) {\n        this.observer.unobserve(prevNode)\n      }\n      this.observer.observe(node)\n      this.elementsCache.set(key, node)\n    }\n\n    // Sync-measure when idle (initial render) or during programmatic scrolling\n    // (scrollToIndex/scrollToOffset) where reconcileScroll needs sizes in the same frame.\n    // During normal user scrolling, skip sync measurement — the RO callback handles it async.\n    if (\n      (!this.isScrolling || this.scrollState) &&\n      this.shouldMeasureDuringScroll(index)\n    ) {\n      this.resizeItem(index, this.options.measureElement(node, undefined, this))\n    }\n  }\n\n  resizeItem = (index: number, size: number) => {\n    const item = this.measurementsCache[index]\n    if (!item) return\n\n    const itemSize = this.itemSizeCache.get(item.key) ?? item.size\n    const delta = size - itemSize\n\n    if (delta !== 0) {\n      if (\n        this.scrollState?.behavior !== 'smooth' &&\n        (this.shouldAdjustScrollPositionOnItemSizeChange !== undefined\n          ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this)\n          : item.start < this.getScrollOffset() + this.scrollAdjustments)\n      ) {\n        if (process.env.NODE_ENV !== 'production' && this.options.debug) {\n          console.info('correction', delta)\n        }\n        this._scrollToOffset(this.getScrollOffset(), {\n          adjustments: (this.scrollAdjustments += delta),\n          behavior: undefined,\n        })\n      }\n\n      this.pendingMeasuredCacheIndexes.push(item.index)\n      this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size))\n\n      this.notify(false)\n    }\n  }\n\n  getVirtualItems = memo(\n    () => [this.getVirtualIndexes(), this.getMeasurements()],\n    (indexes, measurements) => {\n      const virtualItems: Array<VirtualItem> = []\n\n      for (let k = 0, len = indexes.length; k < len; k++) {\n        const i = indexes[k]!\n        const measurement = measurements[i]!\n\n        virtualItems.push(measurement)\n      }\n\n      return virtualItems\n    },\n    {\n      key: process.env.NODE_ENV !== 'production' && 'getVirtualItems',\n      debug: () => this.options.debug,\n    },\n  )\n\n  getVirtualItemForOffset = (offset: number) => {\n    const measurements = this.getMeasurements()\n    if (measurements.length === 0) {\n      return undefined\n    }\n    return notUndefined(\n      measurements[\n        findNearestBinarySearch(\n          0,\n          measurements.length - 1,\n          (index: number) => notUndefined(measurements[index]).start,\n          offset,\n        )\n      ],\n    )\n  }\n\n  private getMaxScrollOffset = () => {\n    if (!this.scrollElement) return 0\n\n    if ('scrollHeight' in this.scrollElement) {\n      // Element\n      return this.options.horizontal\n        ? this.scrollElement.scrollWidth - this.scrollElement.clientWidth\n        : this.scrollElement.scrollHeight - this.scrollElement.clientHeight\n    } else {\n      // Window\n      const doc = this.scrollElement.document.documentElement\n      return this.options.horizontal\n        ? doc.scrollWidth - this.scrollElement.innerWidth\n        : doc.scrollHeight - this.scrollElement.innerHeight\n    }\n  }\n\n  getOffsetForAlignment = (\n    toOffset: number,\n    align: ScrollAlignment,\n    itemSize = 0,\n  ) => {\n    if (!this.scrollElement) return 0\n\n    const size = this.getSize()\n    const scrollOffset = this.getScrollOffset()\n\n    if (align === 'auto') {\n      align = toOffset >= scrollOffset + size ? 'end' : 'start'\n    }\n\n    if (align === 'center') {\n      // When aligning to a particular item (e.g. with scrollToIndex),\n      // adjust offset by the size of the item to center on the item\n      toOffset += (itemSize - size) / 2\n    } else if (align === 'end') {\n      toOffset -= size\n    }\n\n    const maxOffset = this.getMaxScrollOffset()\n\n    return Math.max(Math.min(maxOffset, toOffset), 0)\n  }\n\n  getOffsetForIndex = (index: number, align: ScrollAlignment = 'auto') => {\n    index = Math.max(0, Math.min(index, this.options.count - 1))\n\n    const size = this.getSize()\n    const scrollOffset = this.getScrollOffset()\n\n    const item = this.measurementsCache[index]\n    if (!item) return\n\n    if (align === 'auto') {\n      if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {\n        align = 'end'\n      } else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {\n        align = 'start'\n      } else {\n        return [scrollOffset, align] as const\n      }\n    }\n\n    // For the last item with 'end' alignment, use browser's actual max scroll\n    // to account for borders/padding that aren't in our measurements\n    if (align === 'end' && index === this.options.count - 1) {\n      return [this.getMaxScrollOffset(), align] as const\n    }\n\n    const toOffset =\n      align === 'end'\n        ? item.end + this.options.scrollPaddingEnd\n        : item.start - this.options.scrollPaddingStart\n\n    return [\n      this.getOffsetForAlignment(toOffset, align, item.size),\n      align,\n    ] as const\n  }\n\n  scrollToOffset = (\n    toOffset: number,\n    { align = 'start', behavior = 'auto' }: ScrollToOffsetOptions = {},\n  ) => {\n    const offset = this.getOffsetForAlignment(toOffset, align)\n\n    const now = this.now()\n    this.scrollState = {\n      index: null,\n      align,\n      behavior,\n      startedAt: now,\n      lastTargetOffset: offset,\n      stableFrames: 0,\n    }\n\n    this._scrollToOffset(offset, { adjustments: undefined, behavior })\n\n    this.scheduleScrollReconcile()\n  }\n\n  scrollToIndex = (\n    index: number,\n    {\n      align: initialAlign = 'auto',\n      behavior = 'auto',\n    }: ScrollToIndexOptions = {},\n  ) => {\n    index = Math.max(0, Math.min(index, this.options.count - 1))\n\n    const offsetInfo = this.getOffsetForIndex(index, initialAlign)\n    if (!offsetInfo) {\n      return\n    }\n    const [offset, align] = offsetInfo\n\n    const now = this.now()\n    this.scrollState = {\n      index,\n      align,\n      behavior,\n      startedAt: now,\n      lastTargetOffset: offset,\n      stableFrames: 0,\n    }\n\n    this._scrollToOffset(offset, { adjustments: undefined, behavior })\n\n    this.scheduleScrollReconcile()\n  }\n\n  scrollBy = (\n    delta: number,\n    { behavior = 'auto' }: ScrollToOffsetOptions = {},\n  ) => {\n    const offset = this.getScrollOffset() + delta\n    const now = this.now()\n\n    this.scrollState = {\n      index: null,\n      align: 'start',\n      behavior,\n      startedAt: now,\n      lastTargetOffset: offset,\n      stableFrames: 0,\n    }\n\n    this._scrollToOffset(offset, { adjustments: undefined, behavior })\n\n    this.scheduleScrollReconcile()\n  }\n\n  getTotalSize = () => {\n    const measurements = this.getMeasurements()\n\n    let end: number\n    // If there are no measurements, set the end to paddingStart\n    // If there is only one lane, use the last measurement's end\n    // Otherwise find the maximum end value among all measurements\n    if (measurements.length === 0) {\n      end = this.options.paddingStart\n    } else if (this.options.lanes === 1) {\n      end = measurements[measurements.length - 1]?.end ?? 0\n    } else {\n      const endByLane = Array<number | null>(this.options.lanes).fill(null)\n      let endIndex = measurements.length - 1\n      while (endIndex >= 0 && endByLane.some((val) => val === null)) {\n        const item = measurements[endIndex]!\n        if (endByLane[item.lane] === null) {\n          endByLane[item.lane] = item.end\n        }\n\n        endIndex--\n      }\n\n      end = Math.max(...endByLane.filter((val): val is number => val !== null))\n    }\n\n    return Math.max(\n      end - this.options.scrollMargin + this.options.paddingEnd,\n      0,\n    )\n  }\n\n  private _scrollToOffset = (\n    offset: number,\n    {\n      adjustments,\n      behavior,\n    }: {\n      adjustments: number | undefined\n      behavior: ScrollBehavior | undefined\n    },\n  ) => {\n    this.options.scrollToFn(offset, { behavior, adjustments }, this)\n  }\n\n  measure = () => {\n    this.itemSizeCache = new Map()\n    this.laneAssignments = new Map() // Clear lane cache for full re-layout\n    this.notify(false)\n  }\n}\n\nconst findNearestBinarySearch = (\n  low: number,\n  high: number,\n  getCurrentValue: (i: number) => number,\n  value: number,\n) => {\n  while (low <= high) {\n    const middle = ((low + high) / 2) | 0\n    const currentValue = getCurrentValue(middle)\n\n    if (currentValue < value) {\n      low = middle + 1\n    } else if (currentValue > value) {\n      high = middle - 1\n    } else {\n      return middle\n    }\n  }\n\n  if (low > 0) {\n    return low - 1\n  } else {\n    return 0\n  }\n}\n\nfunction calculateRange({\n  measurements,\n  outerSize,\n  scrollOffset,\n  lanes,\n}: {\n  measurements: Array<VirtualItem>\n  outerSize: number\n  scrollOffset: number\n  lanes: number\n}) {\n  const lastIndex = measurements.length - 1\n  const getOffset = (index: number) => measurements[index]!.start\n\n  // handle case when item count is less than or equal to lanes\n  if (measurements.length <= lanes) {\n    return {\n      startIndex: 0,\n      endIndex: lastIndex,\n    }\n  }\n\n  let startIndex = findNearestBinarySearch(\n    0,\n    lastIndex,\n    getOffset,\n    scrollOffset,\n  )\n  let endIndex = startIndex\n\n  if (lanes === 1) {\n    while (\n      endIndex < lastIndex &&\n      measurements[endIndex]!.end < scrollOffset + outerSize\n    ) {\n      endIndex++\n    }\n  } else if (lanes > 1) {\n    // Expand forward until we include the visible items from all lanes\n    // which are closer to the end of the virtualizer window\n    const endPerLane = Array(lanes).fill(0)\n    while (\n      endIndex < lastIndex &&\n      endPerLane.some((pos) => pos < scrollOffset + outerSize)\n    ) {\n      const item = measurements[endIndex]!\n      endPerLane[item.lane] = item.end\n      endIndex++\n    }\n\n    // Expand backward until we include all lanes' visible items\n    // closer to the top\n    const startPerLane = Array(lanes).fill(scrollOffset + outerSize)\n    while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {\n      const item = measurements[startIndex]!\n      startPerLane[item.lane] = item.start\n      startIndex--\n    }\n\n    // Align startIndex to the beginning of its lane\n    startIndex = Math.max(0, startIndex - (startIndex % lanes))\n    // Align endIndex to the end of its lane\n    endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - (endIndex % lanes)))\n  }\n\n  return { startIndex, endIndex }\n}\n"
  },
  {
    "path": "packages/virtual-core/src/utils.ts",
    "content": "export type NoInfer<A extends any> = [A][A extends any ? 0 : never]\n\nexport type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\nexport function memo<TDeps extends ReadonlyArray<any>, TResult>(\n  getDeps: () => [...TDeps],\n  fn: (...args: NoInfer<[...TDeps]>) => TResult,\n  opts: {\n    key: false | string\n    debug?: () => boolean\n    onChange?: (result: TResult) => void\n    initialDeps?: TDeps\n    skipInitialOnChange?: boolean\n  },\n) {\n  let deps = opts.initialDeps ?? []\n  let result: TResult | undefined\n  let isInitial = true\n\n  function memoizedFunction(): TResult {\n    let depTime: number\n    if (opts.key && opts.debug?.()) depTime = Date.now()\n\n    const newDeps = getDeps()\n\n    const depsChanged =\n      newDeps.length !== deps.length ||\n      newDeps.some((dep: any, index: number) => deps[index] !== dep)\n\n    if (!depsChanged) {\n      return result!\n    }\n\n    deps = newDeps\n\n    let resultTime: number\n    if (opts.key && opts.debug?.()) resultTime = Date.now()\n\n    result = fn(...newDeps)\n\n    if (opts.key && opts.debug?.()) {\n      const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100\n      const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100\n      const resultFpsPercentage = resultEndTime / 16\n\n      const pad = (str: number | string, num: number) => {\n        str = String(str)\n        while (str.length < num) {\n          str = ' ' + str\n        }\n        return str\n      }\n\n      console.info(\n        `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,\n        `\n            font-size: .6rem;\n            font-weight: bold;\n            color: hsl(${Math.max(\n              0,\n              Math.min(120 - 120 * resultFpsPercentage, 120),\n            )}deg 100% 31%);`,\n        opts?.key,\n      )\n    }\n\n    if (opts?.onChange && !(isInitial && opts.skipInitialOnChange)) {\n      opts.onChange(result)\n    }\n\n    isInitial = false\n\n    return result\n  }\n\n  // Attach updateDeps to the function itself\n  memoizedFunction.updateDeps = (newDeps: [...TDeps]) => {\n    deps = newDeps\n  }\n\n  return memoizedFunction\n}\n\nexport function notUndefined<T>(value: T | undefined, msg?: string): T {\n  if (value === undefined) {\n    throw new Error(`Unexpected undefined${msg ? `: ${msg}` : ''}`)\n  } else {\n    return value\n  }\n}\n\nexport const approxEqual = (a: number, b: number) => Math.abs(a - b) < 1.01\n\nexport const debounce = (\n  targetWindow: Window & typeof globalThis,\n  fn: Function,\n  ms: number,\n) => {\n  let timeoutId: number\n  return function (this: any, ...args: Array<any>) {\n    targetWindow.clearTimeout(timeoutId)\n    timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms)\n  }\n}\n"
  },
  {
    "path": "packages/virtual-core/tests/index.test.ts",
    "content": "import { expect, test, vi } from 'vitest'\nimport { Virtualizer } from '../src/index'\n\ntest('should export the Virtualizer class', () => {\n  expect(Virtualizer).toBeDefined()\n})\n\ntest('should return empty items for empty scroll element', () => {\n  const virtualizer = new Virtualizer({\n    count: 100,\n    getScrollElement: () => null,\n    estimateSize: () => 50,\n    scrollToFn: vi.fn(),\n    observeElementRect: vi.fn(),\n    observeElementOffset: vi.fn(),\n  })\n  expect(virtualizer.getVirtualItems()).toEqual([])\n})\n\ntest('should return correct total size with one item and multiple lanes', () => {\n  const virtualizer = new Virtualizer({\n    count: 1,\n    lanes: 2,\n    estimateSize: () => 50,\n    getScrollElement: () => null,\n    scrollToFn: vi.fn(),\n    observeElementRect: vi.fn(),\n    observeElementOffset: vi.fn(),\n  })\n  expect(virtualizer.getTotalSize()).toBe(50)\n})\n\ntest('should correctly recalculate lane assignments when lane count changes', () => {\n  // Create a mock scroll element\n  const mockScrollElement = {\n    scrollTop: 0,\n    scrollLeft: 0,\n    offsetWidth: 400,\n    offsetHeight: 600,\n  } as unknown as HTMLDivElement\n\n  // Mock ResizeObserver\n  let resizeCallback: ((entries: any[]) => void) | null = null\n  const mockResizeObserver = vi.fn((callback) => {\n    resizeCallback = callback\n    return {\n      observe: vi.fn(),\n      unobserve: vi.fn(),\n      disconnect: vi.fn(),\n    }\n  })\n  global.ResizeObserver = mockResizeObserver as any\n\n  // Create virtualizer with 3 lanes initially\n  const virtualizer = new Virtualizer({\n    count: 10,\n    lanes: 3,\n    estimateSize: () => 100,\n    getScrollElement: () => mockScrollElement,\n    scrollToFn: vi.fn(),\n    observeElementRect: (instance, cb) => {\n      cb({ width: 400, height: 600 })\n      return () => {}\n    },\n    observeElementOffset: (instance, cb) => {\n      cb(0, false)\n      return () => {}\n    },\n  })\n\n  virtualizer._willUpdate()\n\n  // Get initial measurements with 3 lanes\n  let measurements = virtualizer['getMeasurements']()\n  expect(measurements.length).toBe(10)\n\n  // All lane assignments should be 0, 1, or 2 (3 lanes)\n  measurements.forEach((item) => {\n    expect(item.lane).toBeGreaterThanOrEqual(0)\n    expect(item.lane).toBeLessThan(3)\n  })\n\n  // Change to 2 lanes\n  virtualizer.setOptions({\n    count: 10,\n    lanes: 2,\n    estimateSize: () => 100,\n    getScrollElement: () => mockScrollElement,\n    scrollToFn: vi.fn(),\n    observeElementRect: (instance, cb) => {\n      cb({ width: 400, height: 600 })\n      return () => {}\n    },\n    observeElementOffset: (instance, cb) => {\n      cb(0, false)\n      return () => {}\n    },\n  })\n\n  virtualizer._willUpdate()\n\n  // Get new measurements with 2 lanes\n  measurements = virtualizer['getMeasurements']()\n  expect(measurements.length).toBe(10)\n\n  // All lane assignments should now be 0 or 1 (2 lanes)\n  // This is the bug fix - previously some items could still have lane: 2\n  measurements.forEach((item, index) => {\n    expect(item.lane).toBeGreaterThanOrEqual(0)\n    expect(item.lane).toBeLessThan(2)\n  })\n\n  // Verify no out of bounds access would occur\n  const lanes = 2\n  const columns = Array.from({ length: lanes }, () => [] as typeof measurements)\n  measurements.forEach((item) => {\n    // This should not throw\n    expect(() => {\n      columns[item.lane].push(item)\n    }).not.toThrow()\n  })\n})\n\ntest('should update getTotalSize() when count option changes (filtering/search)', () => {\n  const virtualizer = new Virtualizer({\n    count: 100,\n    estimateSize: () => 50,\n    getScrollElement: () => null,\n    scrollToFn: vi.fn(),\n    observeElementRect: vi.fn(),\n    observeElementOffset: vi.fn(),\n  })\n\n  expect(virtualizer.getTotalSize()).toBe(5000) // 100 × 50\n\n  // Simulate filtering - reduce count to 20\n  virtualizer.setOptions({\n    count: 20,\n    estimateSize: () => 50,\n    getScrollElement: () => null,\n    scrollToFn: vi.fn(),\n    observeElementRect: vi.fn(),\n    observeElementOffset: vi.fn(),\n  })\n\n  // getTotalSize() should immediately return updated value (not stale)\n  expect(virtualizer.getTotalSize()).toBe(1000) // 20 × 50\n\n  // Restore full count\n  virtualizer.setOptions({\n    count: 100,\n    estimateSize: () => 50,\n    getScrollElement: () => null,\n    scrollToFn: vi.fn(),\n    observeElementRect: vi.fn(),\n    observeElementOffset: vi.fn(),\n  })\n\n  expect(virtualizer.getTotalSize()).toBe(5000) // 100 × 50\n})\n\ntest('should not throw when component unmounts during scrollToIndex rAF loop', () => {\n  // Collect rAF callbacks so we can flush them manually\n  const rafCallbacks: Array<FrameRequestCallback> = []\n  const mockRaf = vi.fn((cb: FrameRequestCallback) => {\n    rafCallbacks.push(cb)\n    return rafCallbacks.length\n  })\n\n  const mockWindow = {\n    requestAnimationFrame: mockRaf,\n    cancelAnimationFrame: vi.fn(),\n    ResizeObserver: vi.fn(() => ({\n      observe: vi.fn(),\n      unobserve: vi.fn(),\n      disconnect: vi.fn(),\n    })),\n  }\n\n  const mockScrollElement = {\n    scrollTop: 0,\n    scrollLeft: 0,\n    scrollWidth: 1000,\n    scrollHeight: 5000,\n    offsetWidth: 400,\n    offsetHeight: 600,\n    ownerDocument: {\n      defaultView: mockWindow,\n    },\n  } as unknown as HTMLDivElement\n\n  const virtualizer = new Virtualizer({\n    count: 100,\n    estimateSize: () => 50,\n    measureElement: (el) => el.getBoundingClientRect().height,\n    getScrollElement: () => mockScrollElement,\n    scrollToFn: vi.fn(),\n    observeElementRect: (instance, cb) => {\n      cb({ width: 400, height: 600 })\n      return () => {}\n    },\n    observeElementOffset: (instance, cb) => {\n      cb(0, false)\n      return () => {}\n    },\n  })\n\n  // Initialize the virtualizer so targetWindow is set\n  virtualizer._willUpdate()\n\n  // Populate elementsCache so isDynamicMode() returns true.\n  // This triggers the code path where the rAF callback calls\n  // this.targetWindow!.requestAnimationFrame(verify)\n  const mockElement = {\n    getBoundingClientRect: () => ({ height: 50 }),\n    isConnected: true,\n    setAttribute: vi.fn(),\n  } as unknown as HTMLElement\n  virtualizer.elementsCache.set(0, mockElement)\n\n  // Trigger scrollToIndex which schedules a rAF callback\n  virtualizer.scrollToIndex(50)\n\n  // Simulate component unmount — cleanup sets targetWindow to null\n  const unmount = virtualizer._didMount()\n  unmount()\n\n  // Flush all pending rAF callbacks — this should not throw\n  // Without the fix, this crashes with:\n  // \"Cannot read properties of null (reading 'requestAnimationFrame')\"\n  expect(() => {\n    rafCallbacks.forEach((cb) => cb(0))\n  }).not.toThrow()\n})\n\nfunction createMockEnvironment() {\n  const rafCallbacks: Array<FrameRequestCallback> = []\n  let rafIdCounter = 0\n  const mockRaf = vi.fn((cb: FrameRequestCallback) => {\n    rafCallbacks.push(cb)\n    return ++rafIdCounter\n  })\n  const mockCancelRaf = vi.fn()\n\n  const mockWindow = {\n    requestAnimationFrame: mockRaf,\n    cancelAnimationFrame: mockCancelRaf,\n    performance: { now: () => Date.now() },\n    ResizeObserver: vi.fn(() => ({\n      observe: vi.fn(),\n      unobserve: vi.fn(),\n      disconnect: vi.fn(),\n    })),\n  }\n\n  const mockScrollElement = {\n    scrollTop: 0,\n    scrollLeft: 0,\n    scrollWidth: 1000,\n    scrollHeight: 5000,\n    clientWidth: 400,\n    clientHeight: 600,\n    offsetWidth: 400,\n    offsetHeight: 600,\n    ownerDocument: {\n      defaultView: mockWindow,\n    },\n    scrollTo: vi.fn(),\n  } as unknown as HTMLDivElement\n\n  const scrollToFn = vi.fn()\n\n  return { rafCallbacks, mockWindow, mockScrollElement, scrollToFn }\n}\n\nfunction createVirtualizer(\n  mockScrollElement: HTMLDivElement,\n  scrollToFn: ReturnType<typeof vi.fn>,\n) {\n  return new Virtualizer({\n    count: 100,\n    estimateSize: () => 50,\n    getScrollElement: () => mockScrollElement,\n    scrollToFn,\n    observeElementRect: (_instance, cb) => {\n      cb({ width: 400, height: 600 })\n      return () => {}\n    },\n    observeElementOffset: (_instance, cb) => {\n      cb(0, false)\n      return () => {}\n    },\n  })\n}\n\ntest('scrollToIndex(0) should reconcile correctly', () => {\n  const { rafCallbacks, mockScrollElement, scrollToFn } =\n    createMockEnvironment()\n  const virtualizer = createVirtualizer(mockScrollElement, scrollToFn)\n\n  virtualizer._willUpdate()\n  scrollToFn.mockClear()\n\n  virtualizer.scrollToIndex(0)\n\n  // scrollToFn should have been called with offset for index 0\n  expect(scrollToFn).toHaveBeenCalled()\n  const calledOffset = scrollToFn.mock.calls[0]![0]\n  expect(calledOffset).toBe(0)\n\n  // Flush rAF — reconcileScroll should run and not bail\n  // It should eventually clear scrollState (settle)\n  rafCallbacks.forEach((cb) => cb(0))\n\n  // scrollState should be cleared after settling\n  expect(virtualizer['scrollState']).toBeNull()\n})\n\ntest('scrollToOffset should reconcile and clear scrollState', () => {\n  const { rafCallbacks, mockScrollElement, scrollToFn } =\n    createMockEnvironment()\n  const virtualizer = createVirtualizer(mockScrollElement, scrollToFn)\n\n  virtualizer._willUpdate()\n  scrollToFn.mockClear()\n\n  virtualizer.scrollToOffset(200)\n\n  expect(scrollToFn).toHaveBeenCalled()\n\n  // scrollState should be set with index: null\n  expect(virtualizer['scrollState']).not.toBeNull()\n  expect(virtualizer['scrollState']!.index).toBeNull()\n\n  // Simulate the scroll offset reaching the target\n  virtualizer.scrollOffset = 200\n\n  // Flush rAF — reconciliation should settle and clear scrollState\n  rafCallbacks.forEach((cb) => cb(0))\n\n  expect(virtualizer['scrollState']).toBeNull()\n})\n\ntest('scrollBy should reconcile and clear scrollState', () => {\n  const { rafCallbacks, mockScrollElement, scrollToFn } =\n    createMockEnvironment()\n  const virtualizer = createVirtualizer(mockScrollElement, scrollToFn)\n\n  virtualizer._willUpdate()\n  scrollToFn.mockClear()\n\n  virtualizer.scrollBy(100)\n\n  expect(virtualizer['scrollState']).not.toBeNull()\n  expect(virtualizer['scrollState']!.index).toBeNull()\n\n  // Simulate scroll offset reaching the target\n  virtualizer.scrollOffset = 100\n\n  rafCallbacks.forEach((cb) => cb(0))\n\n  expect(virtualizer['scrollState']).toBeNull()\n})\n\ntest('reconcileScroll should bail out after timeout', () => {\n  const { rafCallbacks, mockWindow, mockScrollElement, scrollToFn } =\n    createMockEnvironment()\n\n  // Make performance.now() return a controllable value\n  let fakeTime = 1000\n  mockWindow.performance.now = () => fakeTime\n\n  const virtualizer = createVirtualizer(mockScrollElement, scrollToFn)\n  virtualizer._willUpdate()\n\n  virtualizer.scrollToIndex(50)\n\n  expect(virtualizer['scrollState']).not.toBeNull()\n\n  // Advance time past the 5s safety valve\n  fakeTime = 7000\n\n  // Flush rAF — should trigger timeout bailout\n  rafCallbacks.forEach((cb) => cb(0))\n\n  expect(virtualizer['scrollState']).toBeNull()\n})\n\ntest('cleanup should cancel pending RAF and clear scrollState', () => {\n  const { mockWindow, mockScrollElement, scrollToFn } = createMockEnvironment()\n  const virtualizer = createVirtualizer(mockScrollElement, scrollToFn)\n\n  virtualizer._willUpdate()\n  virtualizer.scrollToIndex(50)\n\n  expect(virtualizer['scrollState']).not.toBeNull()\n  expect(virtualizer['rafId']).not.toBeNull()\n\n  const unmount = virtualizer._didMount()\n  unmount()\n\n  expect(virtualizer['scrollState']).toBeNull()\n  expect(virtualizer['rafId']).toBeNull()\n  expect(mockWindow.cancelAnimationFrame).toHaveBeenCalled()\n})\n"
  },
  {
    "path": "packages/virtual-core/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"src\", \"eslint.config.js\", \"vite.config.ts\"]\n}\n"
  },
  {
    "path": "packages/virtual-core/vite.config.ts",
    "content": "import { defineConfig, mergeConfig } from 'vitest/config'\nimport { tanstackViteConfig } from '@tanstack/vite-config'\nimport packageJson from './package.json'\n\nconst config = defineConfig({\n  test: {\n    name: packageJson.name,\n    dir: './tests',\n    watch: false,\n    environment: 'jsdom',\n  },\n})\n\nexport default mergeConfig(\n  config,\n  tanstackViteConfig({\n    entry: './src/index.ts',\n    srcDir: './src',\n  }),\n)\n"
  },
  {
    "path": "packages/vue-virtual/CHANGELOG.md",
    "content": "# @tanstack/vue-virtual\n\n## 3.13.23\n\n### Patch Changes\n\n- Updated dependencies [[`7ece2d5`](https://github.com/TanStack/virtual/commit/7ece2d5d4249b7e703c68ac497ae5545c54e7c67)]:\n  - @tanstack/virtual-core@3.13.23\n\n## 3.13.22\n\n### Patch Changes\n\n- Updated dependencies [[`54d771a`](https://github.com/TanStack/virtual/commit/54d771a7d4c74f6968e8132b5a85f3e04682376a), [`d3416c3`](https://github.com/TanStack/virtual/commit/d3416c386c6446957f413db2eef3211f5fdf3b5f)]:\n  - @tanstack/virtual-core@3.13.22\n\n## 3.13.21\n\n### Patch Changes\n\n- Updated dependencies [[`be89e29`](https://github.com/TanStack/virtual/commit/be89e293ea01654df6334dc6473b65eebed13e51)]:\n  - @tanstack/virtual-core@3.13.21\n\n## 3.13.20\n\n### Patch Changes\n\n- Updated dependencies [[`ff83e94`](https://github.com/TanStack/virtual/commit/ff83e949408ba8a714436fa10cafc3725a56274b)]:\n  - @tanstack/virtual-core@3.13.20\n\n## 3.13.19\n\n### Patch Changes\n\n- Updated dependencies [[`843109c`](https://github.com/TanStack/virtual/commit/843109c5bf780591a762f9767f3808fd15e3f94e)]:\n  - @tanstack/virtual-core@3.13.19\n\n## 3.13.18\n\n### Patch Changes\n\n- Updated dependencies [[`9067574`](https://github.com/TanStack/virtual/commit/9067574f1a0178d30e27bcac70853bdcbf437fec)]:\n  - @tanstack/virtual-core@3.13.18\n\n## 3.13.17\n\n### Patch Changes\n\n- Updated dependencies [[`21d9a46`](https://github.com/TanStack/virtual/commit/21d9a46eac034cb4299872891694965bceed526d)]:\n  - @tanstack/virtual-core@3.13.17\n\n## 3.13.16\n\n### Patch Changes\n\n- Updated dependencies [[`db6df21`](https://github.com/TanStack/virtual/commit/db6df212ed83dd7e4eb6450d1340c95475667b7b)]:\n  - @tanstack/virtual-core@3.13.16\n\n## 3.13.15\n\n### Patch Changes\n\n- Updated dependencies [[`5a273bf`](https://github.com/TanStack/virtual/commit/5a273bf0c0bc0255ca172929f021c3b6e50cb69d)]:\n  - @tanstack/virtual-core@3.13.15\n\n## 3.13.14\n\n### Patch Changes\n\n- Updated dependencies [[`6d9274c`](https://github.com/TanStack/virtual/commit/6d9274c3f0a9e64450b5829872079a65277bc654)]:\n  - @tanstack/virtual-core@3.13.14\n\n## 3.13.13\n\n### Patch Changes\n\n- Fix: Notify framework when count changes to update getTotalSize() ([#1085](https://github.com/TanStack/virtual/pull/1085))\n\n  Fixed an issue where `getTotalSize()` would return stale values when the `count` option changed (e.g., during filtering or search operations). The virtualizer now automatically notifies the framework when measurement-affecting options change, ensuring the UI updates correctly without requiring manual `useMemo` workarounds.\n\n  **Before**: When filtering items, the list container would maintain its previous height, causing excessive blank space (when count decreased) or inaccessible items (when count increased).\n\n  **After**: Height updates automatically when count changes, providing the correct user experience.\n\n  This fix applies to all framework adapters and has minimal performance impact (< 0.1ms per change).\n\n- Updated dependencies [[`2542c5a`](https://github.com/TanStack/virtual/commit/2542c5a3d6820cea956fa3b4f94c42e3526a8d68), [`96e32a6`](https://github.com/TanStack/virtual/commit/96e32a6ffc125743a0172ea4e0fe37ac29c4187b)]:\n  - @tanstack/virtual-core@3.13.13\n\n## 3.13.12\n\n### Patch Changes\n\n- Updated dependencies [[`d21ed98`](https://github.com/TanStack/virtual/commit/d21ed98da3470b9986c9a028ed70fdf0d6189ab4)]:\n  - @tanstack/virtual-core@3.13.12\n\n## 3.13.11\n\n### Patch Changes\n\n- Updated dependencies [[`73fa867`](https://github.com/TanStack/virtual/commit/73fa86752599a4bffba51ec8e4ff2f8cb8283010)]:\n  - @tanstack/virtual-core@3.13.11\n\n## 3.13.10\n\n### Patch Changes\n\n- Updated dependencies [[`b3b7e7d`](https://github.com/TanStack/virtual/commit/b3b7e7dc8b25daeebbd2da61b3b7ae3448babbdb)]:\n  - @tanstack/virtual-core@3.13.10\n\n## 3.13.9\n\n### Patch Changes\n\n- Updated dependencies [[`9e33cdb`](https://github.com/TanStack/virtual/commit/9e33cdb1c8780c2f455aafc11a0aeea58b71fc69)]:\n  - @tanstack/virtual-core@3.13.9\n\n## 3.13.8\n\n### Patch Changes\n\n- Updated dependencies [[`60719f6`](https://github.com/TanStack/virtual/commit/60719f61b589d6f9d886e4f7c093217f6d693faf)]:\n  - @tanstack/virtual-core@3.13.8\n\n## 3.13.7\n\n### Patch Changes\n\n- Updated dependencies [[`e2d93c2`](https://github.com/TanStack/virtual/commit/e2d93c2dcde9ccf60f658e56edccd8d05aefeee6)]:\n  - @tanstack/virtual-core@3.13.7\n\n## 3.13.6\n\n### Patch Changes\n\n- Updated dependencies [[`042616f`](https://github.com/TanStack/virtual/commit/042616f39ced842470db0b4b40fca77f22454b7f)]:\n  - @tanstack/virtual-core@3.13.6\n\n## 3.13.5\n\n### Patch Changes\n\n- Updated dependencies [[`51656d9`](https://github.com/TanStack/virtual/commit/51656d94a2469a065e631f25ffc8ec0288d9f5ec)]:\n  - @tanstack/virtual-core@3.13.5\n\n## 3.13.4\n\n### Patch Changes\n\n- Updated dependencies [[`514b62d`](https://github.com/TanStack/virtual/commit/514b62d04974c2fd59fc8a68ed40f4c1a1547dd2), [`f03d814`](https://github.com/TanStack/virtual/commit/f03d8142c03ea0f5816161a4dad38ca35469841c)]:\n  - @tanstack/virtual-core@3.13.4\n\n## 3.13.3\n\n### Patch Changes\n\n- Updated dependencies [[`02ef309`](https://github.com/TanStack/virtual/commit/02ef3097de4a14ed4077ace2ca901dc411bf81c1)]:\n  - @tanstack/virtual-core@3.13.3\n"
  },
  {
    "path": "packages/vue-virtual/eslint.config.js",
    "content": "// @ts-check\n\nimport rootConfig from '../../eslint.config.js'\n\nexport default [...rootConfig]\n"
  },
  {
    "path": "packages/vue-virtual/package.json",
    "content": "{\n  \"name\": \"@tanstack/vue-virtual\",\n  \"version\": \"3.13.23\",\n  \"description\": \"Headless UI for virtualizing scrollable elements in Vue\",\n  \"author\": \"Tanner Linsley\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/TanStack/virtual.git\",\n    \"directory\": \"packages/vue-virtual\"\n  },\n  \"homepage\": \"https://tanstack.com/virtual\",\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org/\"\n  },\n  \"funding\": {\n    \"type\": \"github\",\n    \"url\": \"https://github.com/sponsors/tannerlinsley\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"vue\",\n    \"solid\",\n    \"svelte\",\n    \"virtual\",\n    \"virtual-core\",\n    \"datagrid\"\n  ],\n  \"scripts\": {\n    \"clean\": \"premove ./dist ./coverage\",\n    \"test:eslint\": \"eslint ./src\",\n    \"test:types\": \"tsc\",\n    \"test:build\": \"publint --strict\",\n    \"build\": \"vite build\"\n  },\n  \"type\": \"module\",\n  \"types\": \"dist/esm/index.d.ts\",\n  \"main\": \"dist/cjs/index.cjs\",\n  \"module\": \"dist/esm/index.js\",\n  \"exports\": {\n    \".\": {\n      \"import\": {\n        \"types\": \"./dist/esm/index.d.ts\",\n        \"default\": \"./dist/esm/index.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/cjs/index.d.cts\",\n        \"default\": \"./dist/cjs/index.cjs\"\n      }\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"sideEffects\": false,\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"dependencies\": {\n    \"@tanstack/virtual-core\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^5.2.4\",\n    \"vue\": \"^3.5.16\"\n  },\n  \"peerDependencies\": {\n    \"vue\": \"^2.7.0 || ^3.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/vue-virtual/src/index.ts",
    "content": "import {\n  Virtualizer,\n  elementScroll,\n  observeElementOffset,\n  observeElementRect,\n  observeWindowOffset,\n  observeWindowRect,\n  windowScroll,\n} from '@tanstack/virtual-core'\nimport {\n  computed,\n  onScopeDispose,\n  shallowRef,\n  triggerRef,\n  unref,\n  watch,\n} from 'vue'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\nimport type { Ref } from 'vue'\n\nexport * from '@tanstack/virtual-core'\n\ntype MaybeRef<T> = T | Ref<T>\n\nfunction useVirtualizerBase<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n>(\n  options: MaybeRef<VirtualizerOptions<TScrollElement, TItemElement>>,\n): Ref<Virtualizer<TScrollElement, TItemElement>> {\n  const virtualizer = new Virtualizer(unref(options))\n  const state = shallowRef(virtualizer)\n\n  const cleanup = virtualizer._didMount()\n\n  watch(\n    () => unref(options).getScrollElement(),\n    (el) => {\n      if (el) {\n        virtualizer._willUpdate()\n      }\n    },\n    {\n      immediate: true,\n    },\n  )\n\n  watch(\n    () => unref(options),\n    (options) => {\n      virtualizer.setOptions({\n        ...options,\n        onChange: (instance, sync) => {\n          triggerRef(state)\n          options.onChange?.(instance, sync)\n        },\n      })\n\n      virtualizer._willUpdate()\n      triggerRef(state)\n    },\n    {\n      immediate: true,\n    },\n  )\n\n  onScopeDispose(cleanup)\n\n  return state\n}\n\nexport function useVirtualizer<\n  TScrollElement extends Element,\n  TItemElement extends Element,\n>(\n  options: MaybeRef<\n    PartialKeys<\n      VirtualizerOptions<TScrollElement, TItemElement>,\n      'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n    >\n  >,\n): Ref<Virtualizer<TScrollElement, TItemElement>> {\n  return useVirtualizerBase<TScrollElement, TItemElement>(\n    computed(() => ({\n      observeElementRect: observeElementRect,\n      observeElementOffset: observeElementOffset,\n      scrollToFn: elementScroll,\n      ...unref(options),\n    })),\n  )\n}\n\nexport function useWindowVirtualizer<TItemElement extends Element>(\n  options: MaybeRef<\n    PartialKeys<\n      VirtualizerOptions<Window, TItemElement>,\n      | 'observeElementRect'\n      | 'observeElementOffset'\n      | 'scrollToFn'\n      | 'getScrollElement'\n    >\n  >,\n): Ref<Virtualizer<Window, TItemElement>> {\n  return useVirtualizerBase<Window, TItemElement>(\n    computed(() => ({\n      getScrollElement: () => (typeof document !== 'undefined' ? window : null),\n      observeElementRect: observeWindowRect,\n      observeElementOffset: observeWindowOffset,\n      scrollToFn: windowScroll,\n      initialOffset: () =>\n        typeof document !== 'undefined' ? window.scrollY : 0,\n      ...unref(options),\n    })),\n  )\n}\n"
  },
  {
    "path": "packages/vue-virtual/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"src\", \"eslint.config.js\", \"vite.config.ts\"]\n}\n"
  },
  {
    "path": "packages/vue-virtual/vite.config.ts",
    "content": "import { defineConfig, mergeConfig } from 'vitest/config'\nimport { tanstackViteConfig } from '@tanstack/vite-config'\nimport vue from '@vitejs/plugin-vue'\n\nconst config = defineConfig({\n  plugins: [vue()],\n})\n\nexport default mergeConfig(\n  config,\n  tanstackViteConfig({\n    entry: './src/index.ts',\n    srcDir: './src',\n  }),\n)\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "cleanupUnusedCatalogs: true\nlinkWorkspacePackages: true\npreferWorkspacePackages: true\n\npackages:\n  - 'packages/*'\n  - 'examples/angular/*'\n  - 'examples/react/*'\n  - 'examples/svelte/*'\n  - 'examples/vue/*'\n  - 'examples/lit/*'\n"
  },
  {
    "path": "prettier.config.js",
    "content": "// @ts-check\n\n/** @type {import('prettier').Config} */\nconst config = {\n  semi: false,\n  singleQuote: true,\n  trailingComma: 'all',\n  plugins: ['prettier-plugin-svelte'],\n  overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }],\n}\n\nexport default config\n"
  },
  {
    "path": "scripts/verify-links.ts",
    "content": "import { existsSync, readFileSync, statSync } from 'node:fs'\nimport { extname, resolve } from 'node:path'\nimport { glob } from 'tinyglobby'\n// @ts-ignore Could not find a declaration file for module 'markdown-link-extractor'.\nimport markdownLinkExtractor from 'markdown-link-extractor'\n\nconst errors: Array<{\n  file: string\n  link: string\n  resolvedPath: string\n  reason: string\n}> = []\n\nfunction isRelativeLink(link: string) {\n  return (\n    !link.startsWith('/') &&\n    !link.startsWith('http://') &&\n    !link.startsWith('https://') &&\n    !link.startsWith('//') &&\n    !link.startsWith('#') &&\n    !link.startsWith('mailto:')\n  )\n}\n\n/** Remove any trailing .md */\nfunction stripExtension(p: string): string {\n  return p.replace(`${extname(p)}`, '')\n}\n\nfunction relativeLinkExists(link: string, file: string): boolean {\n  // Remove hash if present\n  const linkWithoutHash = link.split('#')[0]\n  // If the link is empty after removing hash, it's not a file\n  if (!linkWithoutHash) return false\n\n  // Strip the file/link extensions\n  const filePath = stripExtension(file)\n  const linkPath = stripExtension(linkWithoutHash)\n\n  // Resolve the path relative to the markdown file's directory\n  // Nav up a level to simulate how links are resolved on the web\n  let absPath = resolve(filePath, '..', linkPath)\n\n  // Ensure the resolved path is within /docs\n  const docsRoot = resolve('docs')\n  if (!absPath.startsWith(docsRoot)) {\n    errors.push({\n      link,\n      file,\n      resolvedPath: absPath,\n      reason: 'Path outside /docs',\n    })\n    return false\n  }\n\n  // Check if this is an example path\n  const isExample = absPath.includes('/examples/')\n\n  let exists = false\n\n  if (isExample) {\n    // Transform /docs/framework/{framework}/examples/ to /examples/{framework}/\n    absPath = absPath.replace(\n      /\\/docs\\/framework\\/([^/]+)\\/examples\\//,\n      '/examples/$1/',\n    )\n    // For examples, we want to check if the directory exists\n    exists = existsSync(absPath) && statSync(absPath).isDirectory()\n  } else {\n    // For non-examples, we want to check if the .md file exists\n    if (!absPath.endsWith('.md')) {\n      absPath = `${absPath}.md`\n    }\n    exists = existsSync(absPath)\n  }\n\n  if (!exists) {\n    errors.push({\n      link,\n      file,\n      resolvedPath: absPath,\n      reason: 'Not found',\n    })\n  }\n  return exists\n}\n\nasync function verifyMarkdownLinks() {\n  // Find all markdown files in docs directory\n  const markdownFiles = await glob('docs/**/*.md', {\n    ignore: ['**/node_modules/**'],\n  })\n\n  console.log(`Found ${markdownFiles.length} markdown files\\n`)\n\n  // Process each file\n  for (const file of markdownFiles) {\n    const content = readFileSync(file, 'utf-8')\n    const links: Array<string> = markdownLinkExtractor(content)\n\n    const relativeLinks = links.filter((link: string) => {\n      return isRelativeLink(link)\n    })\n\n    if (relativeLinks.length > 0) {\n      relativeLinks.forEach((link) => {\n        relativeLinkExists(link, file)\n      })\n    }\n  }\n\n  if (errors.length > 0) {\n    console.log(`\\n❌ Found ${errors.length} broken links:`)\n    errors.forEach((err) => {\n      console.log(\n        `${err.file}\\n  link:      ${err.link}\\n  resolved:  ${err.resolvedPath}\\n  why:       ${err.reason}\\n`,\n      )\n    })\n    process.exit(1)\n  } else {\n    console.log('\\n✅ No broken links found!')\n  }\n}\n\nverifyMarkdownLinks().catch(console.error)\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"allowUnreachableCode\": false,\n    \"allowUnusedLabels\": false,\n    \"checkJs\": true,\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"isolatedModules\": true,\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ES2022\"],\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Bundler\",\n    \"noEmit\": true,\n    \"noImplicitReturns\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noUnusedLocals\": false,\n    \"noUnusedParameters\": false,\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"ES2020\"\n  },\n  \"include\": [\"*.config.*\", \"scripts\"]\n}\n"
  }
]