Spaces:
Sleeping
Sleeping
Pull Request Number: 4285 | |
Title: Feat(table): virtualization | |
Base Branch: canary | |
Head Branch: feat/eng-1633-virtualization-for-table | |
Author: vinroger | |
URL: https://github.com/nextui-org/nextui/pull/4285 | |
State: OPEN | |
Created At: 2024-12-09T03:47:21Z | |
Merged At: None | |
Participants: vinroger | |
Description: | |
Results: Demo for 10000 rows + Full Page Refresh (Delay reduced significantly) | |
table.virtualized.mp4 | |
π Description | |
This PR introduces virtualization support for the Table component, enabling efficient rendering and handling of large datasets by only rendering visible items in the viewport. The implementation utilizes the @tanstack/react-virtual library and adds several customizable props to enhance usability and performance. | |
β³οΈ Current behavior (updates) | |
The current Table component renders all rows in the DOM, which can lead to significant performance issues when displaying large datasets. | |
π New behavior | |
Virtualization Support: | |
Added isVirtualized prop to enable virtualization. | |
Introduced new props: | |
rowHeight: Specifies the height of each row (default: 40px). | |
maxTableHeight: Sets the maximum height of the table (default: 600px). | |
Documentation and Examples: | |
Added examples demonstrating: | |
Virtualized table with 10,000 rows. | |
Custom rowHeight for variable row sizes. | |
Custom maxTableHeight configurations. | |
Performance Improvements: | |
Virtualization efficiently renders only visible rows, reducing DOM node usage and improving performance. | |
π£ Is this a breaking change (Yes/No): | |
No | |
π Additional Information | |
This feature is powered by the @tanstack/react-virtual library for efficient list rendering. All new props and behavior changes are backward-compatible, ensuring seamless integration with existing implementations. | |
Summary by CodeRabbit | |
Summary by CodeRabbit | |
New Features | |
Introduced virtualization support for the Table component, allowing efficient rendering of large datasets. | |
Added new props: isVirtualized, rowHeight, and maxTableHeight for enhanced configurability. | |
New examples in documentation demonstrating virtualization with 500 and 10,000 rows. | |
Bug Fixes | |
Updated type handling for the TableRowGroup component to improve type safety. | |
Documentation | |
Enhanced documentation for the Table component with new sections on virtualization and updated API details. | |
Chores | |
Updated dependencies in the package configuration. | |
Commits: | |
- feat: baseline virtualization for table\n- merge branch canary\n- fix: table layout\n- fix: calc header height w layouteffect to offset padding\n- Merge branch 'canary' into feat/eng-1633-virtualization-for-table\n- Merge branch 'canary' into feat/eng-1633-virtualization-for-table\n- chore: remove unused files and comments\n- chore: add missing package\n- feat: add shouldVirtualize conditional to render virtualized-table\n- feat: update docs for table\n- feat: use wrapper to support theme styles\n | |
Labels: | |
Comments: | |
- linear: <p><a href="https://linear.app/nextui-inc/issue/ENG-1633/virtualization-for-table">ENG-1633 Virtualization for Table</a></p>\n- changeset-bot: ### β οΈ No Changeset found | |
Latest commit: 3308771a703187232e402ce75ed349dab1e8ea1e | |
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.** | |
<details><summary>This PR includes no changesets</summary> | |
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types | |
</details> | |
[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md). | |
[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/nextui-org/nextui/new/feat/eng-1633-virtualization-for-table?filename=.changeset/lovely-games-provide.md&value=---%0A%22%40nextui-org%2Fdocs%22%3A%20patch%0A%22%40nextui-org%2Ftable%22%3A%20patch%0A---%0A%0A%5BDRAFT%5D%20Feat(table)%3A%20virtualization%0A) | |
\n- vercel: [vc]: #MeTxs2/UFNgUt842By0Go97U3eAyM34HxIFHnUNDxBU=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJuZXh0dWktZG9jcy12MiIsInJvb3REaXJlY3RvcnkiOiJhcHBzL2RvY3MiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vbmV4dHVpLW9yZy9uZXh0dWktZG9jcy12Mi9HUXFvb1BxNXEyYUdUVFk0cEo0YUJ5UG8xUFlyIiwicHJldmlld1VybCI6Im5leHR1aS1kb2NzLXYyLWdpdC1mZWF0LWVuZy0xNjMzLXZpcnR1YWxpemF0aS0zZjcyZDYtbmV4dHVpLW9yZy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6Im5leHR1aS1kb2NzLXYyLWdpdC1mZWF0LWVuZy0xNjMzLXZpcnR1YWxpemF0aS0zZjcyZDYtbmV4dHVpLW9yZy52ZXJjZWwuYXBwIn19LHsibmFtZSI6Im5leHR1aS1zdG9yeWJvb2stdjIiLCJyb290RGlyZWN0b3J5IjoicGFja2FnZXMvc3Rvcnlib29rIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL25leHR1aS1vcmcvbmV4dHVpLXN0b3J5Ym9vay12Mi82WWdjeFFUZkpGdDFpQUJldEd4amFpbjNaZzhKIiwicHJldmlld1VybCI6Im5leHR1aS1zdG9yeWJvb2stdjItZ2l0LWZlYXQtZW5nLTE2MzMtdmlydHVhbC01OWY3YzctbmV4dHVpLW9yZy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6Im5leHR1aS1zdG9yeWJvb2stdjItZ2l0LWZlYXQtZW5nLTE2MzMtdmlydHVhbC01OWY3YzctbmV4dHVpLW9yZy52ZXJjZWwuYXBwIn19XX0= | |
**The latest updates on your projects**. Learn more about [Vercel for Git βοΈ](https://vercel.link/github-learn-more) | |
| Name | Status | Preview | Comments | Updated (UTC) | | |
| :--- | :----- | :------ | :------- | :------ | | |
| **nextui-docs-v2** | β Ready ([Inspect](https://vercel.com/nextui-org/nextui-docs-v2/GQqooPq5q2aGTTY4pJ4aByPo1PYr)) | [Visit Preview](https://nextui-docs-v2-git-feat-eng-1633-virtualizati-3f72d6-nextui-org.vercel.app) | π¬ [**Add feedback**](https://vercel.live/open-feedback/nextui-docs-v2-git-feat-eng-1633-virtualizati-3f72d6-nextui-org.vercel.app?via=pr-comment-feedback-link) | Dec 9, 2024 9:33am | | |
| **nextui-storybook-v2** | β Ready ([Inspect](https://vercel.com/nextui-org/nextui-storybook-v2/6YgcxQTfJFt1iABetGxjain3Zg8J)) | [Visit Preview](https://nextui-storybook-v2-git-feat-eng-1633-virtual-59f7c7-nextui-org.vercel.app) | π¬ [**Add feedback**](https://vercel.live/open-feedback/nextui-storybook-v2-git-feat-eng-1633-virtual-59f7c7-nextui-org.vercel.app?via=pr-comment-feedback-link) | Dec 9, 2024 9:33am | | |
\n- coderabbitai: <!-- This is an auto-generated comment: summarize by coderabbit.ai --> | |
<!-- walkthrough_start --> | |
## Walkthrough | |
The changes in this pull request involve updates to the documentation and functionality of various table components within the NextUI framework. Key modifications include the introduction of virtualization features to enhance performance when rendering large datasets. The `routes.json` configuration has been updated to reflect new and updated content for documentation, while several new React components have been created to support virtualized table rendering. Additionally, the documentation has been enhanced to include new props related to virtualization, ensuring comprehensive guidance for users. | |
## Changes | |
| File Path | Change Summary | | |
|-----------|----------------| | |
| `apps/docs/config/routes.json` | Updated multiple routes with `updated` and `newPost` flags; added `comingSoon` for `toast`. | | |
| `apps/docs/content/components/table/index.ts` | Added imports for virtualization-related features and updated `tableContent` export. | | |
| `apps/docs/content/components/table/virtualization-custom-max-table-height.raw.jsx` | Introduced a new component for a virtualized table with a max height of 300px. | | |
| `apps/docs/content/components/table/virtualization-custom-max-table-height.ts` | Created a module to export the new virtualized table component. | | |
| `apps/docs/content/components/table/virtualization-custom-row-height.raw.jsx` | Added a new component for a virtualized table with a max height of 500px. | | |
| `apps/docs/content/components/table/virtualization-custom-row-height.ts` | Created a module to export the new virtualized table component. | | |
| `apps/docs/content/components/table/virtualization-ten-thousand.raw.jsx` | Introduced a new component for a virtualized table capable of displaying 10,000 rows. | | |
| `apps/docs/content/components/table/virtualization-ten-thousand.ts` | Created a module to export the new virtualized table component. | | |
| `apps/docs/content/components/table/virtualization.raw.jsx` | Added a new component for rendering a virtualized table with 500 rows. | | |
| `apps/docs/content/components/table/virtualization.ts` | Created a module to export the new virtualized table component. | | |
| `apps/docs/content/docs/components/table.mdx` | Enhanced documentation with new props for virtualization and examples for large datasets. | | |
| `packages/components/table/package.json` | Added a new dependency on `@tanstack/react-virtual`. | | |
| `packages/components/table/src/table-row-group.tsx` | Updated the type of the forwarded ref in `TableRowGroup` from `"thead"` to `any`. | | |
| `packages/components/table/src/table.tsx` | Modified `TableProps` to include virtualization-related props and updated rendering logic. | | |
| `packages/components/table/src/virtualized-table-body.tsx` | Introduced `VirtualizedTableBody` component for rendering virtualized table bodies. | | |
| `packages/components/table/src/virtualized-table.tsx` | Created `VirtualizedTable` component to enhance rendering performance through virtualization. | | |
| `packages/components/table/stories/table.stories.tsx` | Added new story templates for virtualized table scenarios with 500 and 10,000 rows. | | |
## Possibly related issues | |
- **nextui-org/nextui#3697**: This PR addresses the feature request for table virtualization, which is directly supported by the changes made in this pull request. | |
## Possibly related PRs | |
- **#4203**: Introduces virtualization support for the `Select` component, aligning with the overall focus on enhancing component functionality. | |
- **#3735**: Adds an `isKeyboardNavigationDisabled` prop to the `Table` component, relevant to the updates in the table documentation. | |
- **#3822**: Adds a forms guide, relevant as it complements the new route for the "Forms" section in the documentation. | |
- **#4207**: Addresses ref handling in various components, including `select`, relevant to the documentation updates for the `select` component. | |
## Suggested reviewers | |
- **jrgarciadev**: Suggested for review due to their expertise in the components being modified. | |
<!-- walkthrough_end --> | |
<!-- internal state start --> | |
<!-- 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 --> | |
<!-- internal state end --> | |
<!-- tips_start --> | |
--- | |
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? | |
<details> | |
<summary>β€οΈ Share</summary> | |
- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) | |
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) | |
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) | |
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code) | |
</details> | |
<details> | |
<summary>πͺ§ Tips</summary> | |
### Chat | |
There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai): | |
- Review comments: Directly reply to a review comment made by CodeRabbit. Example: | |
- `I pushed a fix in commit <commit_id>, please review it.` | |
- `Generate unit testing code for this file.` | |
- `Open a follow-up GitHub issue for this discussion.` | |
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples: | |
- `@coderabbitai generate unit testing code for this file.` | |
- `@coderabbitai modularize this function.` | |
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples: | |
- `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.` | |
- `@coderabbitai read src/utils.ts and generate unit testing code.` | |
- `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.` | |
- `@coderabbitai help me debug CodeRabbit configuration file.` | |
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. | |
### CodeRabbit Commands (Invoked using PR comments) | |
- `@coderabbitai pause` to pause the reviews on a PR. | |
- `@coderabbitai resume` to resume the paused reviews. | |
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository. | |
- `@coderabbitai full review` to do a full review from scratch and review all the files again. | |
- `@coderabbitai summary` to regenerate the summary of the PR. | |
- `@coderabbitai resolve` resolve all the CodeRabbit review comments. | |
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository. | |
- `@coderabbitai help` to get help. | |
### Other keywords and placeholders | |
- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed. | |
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description. | |
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically. | |
### Documentation and Community | |
- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit. | |
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback. | |
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements. | |
</details> | |
<!-- tips_end -->\n | |
Files Changed: | |
- apps/docs/config/routes.json (modified, 3 changes)\n Patch: @@ -410,7 +410,8 @@ | |
"key": "table", | |
"title": "Table", | |
"keywords": "table, data display, grid, spreadsheet", | |
- "path": "/docs/components/table.mdx" | |
+ "path": "/docs/components/table.mdx", | |
+ "updated": true | |
}, | |
{ | |
"key": "tabs",\n- apps/docs/content/components/table/index.ts (modified, 8 changes)\n Patch: @@ -19,6 +19,10 @@ import asyncPagination from "./async-pagination"; | |
import infinitePagination from "./infinite-pagination"; | |
import useCase from "./use-case"; | |
import customStyles from "./custom-styles"; | |
+import virtualization from "./virtualization"; | |
+import virtualizationCustomItemHeight from "./virtualization-custom-row-height"; | |
+import virtualizationCustomMaxTableHeight from "./virtualization-custom-max-table-height"; | |
+import virtualizationTenThousand from "./virtualization-ten-thousand"; | |
export const tableContent = { | |
usage, | |
@@ -42,4 +46,8 @@ export const tableContent = { | |
infinitePagination, | |
useCase, | |
customStyles, | |
+ virtualization, | |
+ virtualizationCustomItemHeight, | |
+ virtualizationCustomMaxTableHeight, | |
+ virtualizationTenThousand, | |
};\n- apps/docs/content/components/table/virtualization-custom-max-table-height.raw.jsx (added, 37 changes)\n Patch: @@ -0,0 +1,37 @@ | |
+import {Table, TableBody, TableCell, TableColumn, TableHeader, TableRow} from "@nextui-org/react"; | |
+ | |
+function generateRows(count) { | |
+ return Array.from({length: count}, (_, index) => ({ | |
+ key: index.toString(), | |
+ name: `Item ${index + 1}`, | |
+ value: `Value ${index + 1}`, | |
+ })); | |
+} | |
+ | |
+export default function App() { | |
+ const rows = generateRows(500); | |
+ const columns = [ | |
+ {key: "name", label: "Name"}, | |
+ {key: "value", label: "Value"}, | |
+ ]; | |
+ | |
+ return ( | |
+ <Table | |
+ isVirtualized | |
+ aria-label="Example of virtualized table with a large dataset" | |
+ maxTableHeight={300} | |
+ rowHeight={40} | |
+ > | |
+ <TableHeader columns={columns}> | |
+ {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>} | |
+ </TableHeader> | |
+ <TableBody items={rows}> | |
+ {(item) => ( | |
+ <TableRow key={item.key}> | |
+ {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} | |
+ </TableRow> | |
+ )} | |
+ </TableBody> | |
+ </Table> | |
+ ); | |
+}\n- apps/docs/content/components/table/virtualization-custom-max-table-height.ts (added, 9 changes)\n Patch: @@ -0,0 +1,9 @@ | |
+import App from "./virtualization-custom-max-table-height.raw.jsx?raw"; | |
+ | |
+const react = { | |
+ "/App.jsx": App, | |
+}; | |
+ | |
+export default { | |
+ ...react, | |
+};\n- apps/docs/content/components/table/virtualization-custom-row-height.raw.jsx (added, 37 changes)\n Patch: @@ -0,0 +1,37 @@ | |
+import {Table, TableBody, TableCell, TableColumn, TableHeader, TableRow} from "@nextui-org/react"; | |
+ | |
+function generateRows(count) { | |
+ return Array.from({length: count}, (_, index) => ({ | |
+ key: index.toString(), | |
+ name: `Item ${index + 1}`, | |
+ value: `Value ${index + 1}`, | |
+ })); | |
+} | |
+ | |
+export default function App() { | |
+ const rows = generateRows(500); | |
+ const columns = [ | |
+ {key: "name", label: "Name"}, | |
+ {key: "value", label: "Value"}, | |
+ ]; | |
+ | |
+ return ( | |
+ <Table | |
+ isVirtualized | |
+ aria-label="Example of virtualized table with a large dataset" | |
+ maxTableHeight={500} | |
+ rowHeight={70} | |
+ > | |
+ <TableHeader columns={columns}> | |
+ {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>} | |
+ </TableHeader> | |
+ <TableBody items={rows}> | |
+ {(item) => ( | |
+ <TableRow key={item.key}> | |
+ {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} | |
+ </TableRow> | |
+ )} | |
+ </TableBody> | |
+ </Table> | |
+ ); | |
+}\n- apps/docs/content/components/table/virtualization-custom-row-height.ts (added, 9 changes)\n Patch: @@ -0,0 +1,9 @@ | |
+import App from "./virtualization-custom-row-height.raw.jsx?raw"; | |
+ | |
+const react = { | |
+ "/App.jsx": App, | |
+}; | |
+ | |
+export default { | |
+ ...react, | |
+};\n- apps/docs/content/components/table/virtualization-ten-thousand.raw.jsx (added, 37 changes)\n Patch: @@ -0,0 +1,37 @@ | |
+import {Table, TableBody, TableCell, TableColumn, TableHeader, TableRow} from "@nextui-org/react"; | |
+ | |
+function generateRows(count) { | |
+ return Array.from({length: count}, (_, index) => ({ | |
+ key: index.toString(), | |
+ name: `Item ${index + 1}`, | |
+ value: `Value ${index + 1}`, | |
+ })); | |
+} | |
+ | |
+export default function App() { | |
+ const rows = generateRows(10000); | |
+ const columns = [ | |
+ {key: "name", label: "Name"}, | |
+ {key: "value", label: "Value"}, | |
+ ]; | |
+ | |
+ return ( | |
+ <Table | |
+ isVirtualized | |
+ aria-label="Example of virtualized table with a large dataset" | |
+ maxTableHeight={500} | |
+ rowHeight={40} | |
+ > | |
+ <TableHeader columns={columns}> | |
+ {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>} | |
+ </TableHeader> | |
+ <TableBody items={rows}> | |
+ {(item) => ( | |
+ <TableRow key={item.key}> | |
+ {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} | |
+ </TableRow> | |
+ )} | |
+ </TableBody> | |
+ </Table> | |
+ ); | |
+}\n- apps/docs/content/components/table/virtualization-ten-thousand.ts (added, 9 changes)\n Patch: @@ -0,0 +1,9 @@ | |
+import App from "./virtualization-ten-thousand.raw.jsx?raw"; | |
+ | |
+const react = { | |
+ "/App.jsx": App, | |
+}; | |
+ | |
+export default { | |
+ ...react, | |
+};\n- apps/docs/content/components/table/virtualization.raw.jsx (added, 37 changes)\n Patch: @@ -0,0 +1,37 @@ | |
+import {Table, TableBody, TableCell, TableColumn, TableHeader, TableRow} from "@nextui-org/react"; | |
+ | |
+function generateRows(count) { | |
+ return Array.from({length: count}, (_, index) => ({ | |
+ key: index.toString(), | |
+ name: `Item ${index + 1}`, | |
+ value: `Value ${index + 1}`, | |
+ })); | |
+} | |
+ | |
+export default function App() { | |
+ const rows = generateRows(500); | |
+ const columns = [ | |
+ {key: "name", label: "Name"}, | |
+ {key: "value", label: "Value"}, | |
+ ]; | |
+ | |
+ return ( | |
+ <Table | |
+ isVirtualized | |
+ aria-label="Example of virtualized table with a large dataset" | |
+ maxTableHeight={500} | |
+ rowHeight={40} | |
+ > | |
+ <TableHeader columns={columns}> | |
+ {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>} | |
+ </TableHeader> | |
+ <TableBody items={rows}> | |
+ {(item) => ( | |
+ <TableRow key={item.key}> | |
+ {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} | |
+ </TableRow> | |
+ )} | |
+ </TableBody> | |
+ </Table> | |
+ ); | |
+}\n- apps/docs/content/components/table/virtualization.ts (added, 9 changes)\n Patch: @@ -0,0 +1,9 @@ | |
+import App from "./virtualization.raw.jsx?raw"; | |
+ | |
+const react = { | |
+ "/App.jsx": App, | |
+}; | |
+ | |
+export default { | |
+ ...react, | |
+};\n- apps/docs/content/docs/components/table.mdx (modified, 48 changes)\n Patch: @@ -317,6 +317,36 @@ import { useAsyncList } from "@react-stately/data"; | |
iframeSrc="/examples/table/infinite-pagination" | |
/> | |
+ | |
+### Virtualization | |
+ | |
+Table supports virtualization, which allows efficient rendering of large lists by only rendering items that are visible in the viewport. You can enable virtualization by setting the `isVirtualized` prop to `true`. | |
+ | |
+<CodeDemo | |
+ title="Virtualization" | |
+ files={tableContent.virtualization} | |
+/> | |
+ | |
+> **Note**: The virtualization strategy is based on the [@tanstack/react-virtual](https://tanstack.com/virtual/latest) package, which provides efficient rendering of large lists by only rendering items that are visible in the viewport. | |
+ | |
+#### Ten Thousand Items | |
+ | |
+Here's an example of using virtualization with 10,000 items. | |
+ | |
+<CodeDemo title="Ten Thousand Items" files={tableContent.virtualizationTenThousand} /> | |
+ | |
+#### Max Table Height | |
+ | |
+The `maxTableHeight` prop is used to set the maximum height of the table. This is required when using virtualization. By default, it's set to `600`. | |
+ | |
+<CodeDemo title="Max Table Height" files={tableContent.virtualizationCustomMaxTableHeight} /> | |
+ | |
+#### Custom Row Height | |
+ | |
+The `rowHeight` prop is used to set the height of each row in the table. This is required when using virtualization. By default, it's set to `40`. | |
+ | |
+<CodeDemo title="Custom Row Height" files={tableContent.virtualizationCustomItemHeight} /> | |
+ | |
### Use Case Example | |
When creating a table, you usually need core functionalities like sorting, pagination, and filtering. In the | |
@@ -456,6 +486,24 @@ You can customize the `Table` component by passing custom Tailwind CSS classes t | |
type: "none | sm | md | lg", | |
description: "The shadow size of the table.", | |
default: "sm" | |
+ }, | |
+ { | |
+ attribute: "maxTableHeight", | |
+ type: "number", | |
+ description: "The maximum height of the table in pixels. Required when using virtualization.", | |
+ default: 600 | |
+ }, | |
+ { | |
+ attribute: "rowHeight", | |
+ type: "number", | |
+ description: "The fixed height of each row item in pixels. Required when using virtualization.", | |
+ default: 40 | |
+ }, | |
+ { | |
+ attribute: "isVirtualized", | |
+ type: "boolean", | |
+ description: "Whether to enable virtualization. By default, it's enabled when the number of items exceeds 50.", | |
+ default: "undefined" | |
}, | |
{ | |
attribute: "hideHeader",\n- packages/components/table/package.json (modified, 3 changes)\n Patch: @@ -53,7 +53,8 @@ | |
"@react-stately/table": "3.13.0", | |
"@react-stately/virtualizer": "4.2.0", | |
"@react-types/grid": "3.2.10", | |
- "@react-types/table": "3.10.3" | |
+ "@react-types/table": "3.10.3", | |
+ "@tanstack/react-virtual": "^3.10.9" | |
}, | |
"devDependencies": { | |
"@nextui-org/theme": "workspace:*",\n- packages/components/table/src/table-row-group.tsx (modified, 2 changes)\n Patch: @@ -11,7 +11,7 @@ export interface TableRowGroupProps extends HTMLNextUIProps<"thead"> { | |
classNames?: ValuesType["classNames"]; | |
} | |
-const TableRowGroup = forwardRef<"thead", TableRowGroupProps>((props, ref) => { | |
+const TableRowGroup = forwardRef<any, TableRowGroupProps>((props, ref) => { | |
const {as, className, children, slots, classNames, ...otherProps} = props; | |
const Component = as || "thead";\n- packages/components/table/src/table.tsx (modified, 23 changes)\n Patch: @@ -3,13 +3,19 @@ import {Spacer} from "@nextui-org/spacer"; | |
import {forwardRef} from "@nextui-org/system"; | |
import {UseTableProps, useTable} from "./use-table"; | |
+import VirtualizedTable from "./virtualized-table"; | |
import TableRowGroup from "./table-row-group"; | |
import TableHeaderRow from "./table-header-row"; | |
import TableColumnHeader from "./table-column-header"; | |
import TableSelectAllCheckbox from "./table-select-all-checkbox"; | |
import TableBody from "./table-body"; | |
-export interface TableProps extends Omit<UseTableProps, "isSelectable" | "isMultiSelectable"> {} | |
+export interface TableProps<T = object> | |
+ extends Omit<UseTableProps<T>, "isSelectable" | "isMultiSelectable"> { | |
+ isVirtualized?: boolean; | |
+ rowHeight?: number; | |
+ maxTableHeight?: number; | |
+} | |
const Table = forwardRef<"table", TableProps>((props, ref) => { | |
const { | |
@@ -30,6 +36,10 @@ const Table = forwardRef<"table", TableProps>((props, ref) => { | |
ref, | |
}); | |
+ const {isVirtualized, rowHeight = 40, maxTableHeight = 600} = props; | |
+ | |
+ const shouldVirtualize = values.collection.size > 50 || isVirtualized; | |
+ | |
const Wrapper = useCallback( | |
({children}: {children: JSX.Element}) => { | |
if (removeWrapper) { | |
@@ -41,6 +51,17 @@ const Table = forwardRef<"table", TableProps>((props, ref) => { | |
[removeWrapper, getWrapperProps], | |
); | |
+ if (shouldVirtualize) { | |
+ return ( | |
+ <VirtualizedTable | |
+ {...(props as TableProps)} | |
+ ref={ref} | |
+ maxTableHeight={maxTableHeight} | |
+ rowHeight={rowHeight} | |
+ /> | |
+ ); | |
+ } | |
+ | |
return ( | |
<div {...getBaseProps()}> | |
{topContentPlacement === "outside" && topContent}\n- packages/components/table/src/virtualized-table-body.tsx (added, 169 changes)\n Patch: @@ -0,0 +1,169 @@ | |
+import {forwardRef, HTMLNextUIProps} from "@nextui-org/system"; | |
+import {useDOMRef} from "@nextui-org/react-utils"; | |
+import {clsx, dataAttr} from "@nextui-org/shared-utils"; | |
+import {useTableRowGroup} from "@react-aria/table"; | |
+import {filterDOMProps} from "@nextui-org/react-utils"; | |
+import {mergeProps} from "@react-aria/utils"; | |
+import {Virtualizer} from "@tanstack/react-virtual"; | |
+ | |
+import TableRow from "./table-row"; | |
+import TableCell from "./table-cell"; | |
+import TableCheckboxCell from "./table-checkbox-cell"; | |
+import {ValuesType} from "./use-table"; | |
+ | |
+// @internal | |
+export interface VirtualizedTableBodyProps extends HTMLNextUIProps<"tbody"> { | |
+ slots: ValuesType["slots"]; | |
+ collection: ValuesType["collection"]; | |
+ state: ValuesType["state"]; | |
+ isSelectable: ValuesType["isSelectable"]; | |
+ color: ValuesType["color"]; | |
+ disableAnimation: ValuesType["disableAnimation"]; | |
+ checkboxesProps: ValuesType["checkboxesProps"]; | |
+ selectionMode: ValuesType["selectionMode"]; | |
+ classNames?: ValuesType["classNames"]; | |
+ rowVirtualizer: Virtualizer<any, Element>; | |
+} | |
+ | |
+const VirtualizedTableBody = forwardRef<"tbody", VirtualizedTableBodyProps>((props, ref) => { | |
+ const { | |
+ as, | |
+ className, | |
+ slots, | |
+ state, | |
+ collection, | |
+ isSelectable, | |
+ color, | |
+ disableAnimation, | |
+ checkboxesProps, | |
+ selectionMode, | |
+ classNames, | |
+ rowVirtualizer, | |
+ ...otherProps | |
+ } = props; | |
+ | |
+ const Component = as || "tbody"; | |
+ const shouldFilterDOMProps = typeof Component === "string"; | |
+ | |
+ const domRef = useDOMRef(ref); | |
+ | |
+ const {rowGroupProps} = useTableRowGroup(); | |
+ | |
+ const tbodyStyles = clsx(classNames?.tbody, className); | |
+ const bodyProps = collection?.body.props; | |
+ | |
+ const isLoading = | |
+ bodyProps?.isLoading || | |
+ bodyProps?.loadingState === "loading" || | |
+ bodyProps?.loadingState === "loadingMore"; | |
+ | |
+ const items = [...collection.body.childNodes]; | |
+ | |
+ const virtualItems = rowVirtualizer.getVirtualItems(); | |
+ | |
+ let emptyContent; | |
+ let loadingContent; | |
+ | |
+ if (collection.size === 0 && bodyProps.emptyContent) { | |
+ emptyContent = ( | |
+ <tr role="row"> | |
+ <td | |
+ className={slots?.emptyWrapper({class: classNames?.emptyWrapper})} | |
+ colSpan={collection.columnCount} | |
+ role="gridcell" | |
+ > | |
+ {!isLoading && bodyProps.emptyContent} | |
+ </td> | |
+ </tr> | |
+ ); | |
+ } | |
+ | |
+ if (isLoading && bodyProps.loadingContent) { | |
+ loadingContent = ( | |
+ <tr role="row"> | |
+ <td | |
+ className={slots?.loadingWrapper({class: classNames?.loadingWrapper})} | |
+ colSpan={collection.columnCount} | |
+ role="gridcell" | |
+ > | |
+ {bodyProps.loadingContent} | |
+ </td> | |
+ {!emptyContent && collection.size === 0 ? ( | |
+ <td className={slots?.emptyWrapper({class: classNames?.emptyWrapper})} /> | |
+ ) : null} | |
+ </tr> | |
+ ); | |
+ } | |
+ | |
+ return ( | |
+ <Component | |
+ ref={domRef} | |
+ {...mergeProps( | |
+ rowGroupProps, | |
+ filterDOMProps(bodyProps, { | |
+ enabled: shouldFilterDOMProps, | |
+ }), | |
+ otherProps, | |
+ )} | |
+ className={slots.tbody?.({class: tbodyStyles})} | |
+ data-empty={dataAttr(collection.size === 0)} | |
+ data-loading={dataAttr(isLoading)} | |
+ > | |
+ {virtualItems.map((virtualRow, index) => { | |
+ const row = items[virtualRow.index]; | |
+ | |
+ if (!row) { | |
+ return null; | |
+ } | |
+ | |
+ return ( | |
+ <TableRow | |
+ key={String(row.key)} | |
+ classNames={classNames} | |
+ isSelectable={isSelectable} | |
+ node={row} | |
+ slots={slots} | |
+ state={state} | |
+ style={{ | |
+ transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`, | |
+ height: `${virtualRow.size}px`, | |
+ }} | |
+ > | |
+ {[...row.childNodes].map((cell) => | |
+ cell.props.isSelectionCell ? ( | |
+ <TableCheckboxCell | |
+ key={String(cell.key)} | |
+ checkboxesProps={checkboxesProps} | |
+ classNames={classNames} | |
+ color={color} | |
+ disableAnimation={disableAnimation} | |
+ node={cell} | |
+ rowKey={row.key} | |
+ selectionMode={selectionMode} | |
+ slots={slots} | |
+ state={state} | |
+ /> | |
+ ) : ( | |
+ <TableCell | |
+ key={String(cell.key)} | |
+ classNames={classNames} | |
+ node={cell} | |
+ rowKey={row.key} | |
+ slots={slots} | |
+ state={state} | |
+ /> | |
+ ), | |
+ )} | |
+ </TableRow> | |
+ ); | |
+ })} | |
+ | |
+ {loadingContent} | |
+ {emptyContent} | |
+ </Component> | |
+ ); | |
+}); | |
+ | |
+VirtualizedTableBody.displayName = "NextUI.VirtualizedTableBody"; | |
+ | |
+export default VirtualizedTableBody;\n- packages/components/table/src/virtualized-table.tsx (added, 155 changes)\n Patch: @@ -0,0 +1,155 @@ | |
+import {useCallback, useLayoutEffect, useRef, useState} from "react"; | |
+import {Spacer} from "@nextui-org/spacer"; | |
+import {forwardRef} from "@nextui-org/system"; | |
+import {useVirtualizer} from "@tanstack/react-virtual"; | |
+ | |
+import {UseTableProps, useTable} from "./use-table"; | |
+import TableRowGroup from "./table-row-group"; | |
+import TableHeaderRow from "./table-header-row"; | |
+import TableColumnHeader from "./table-column-header"; | |
+import TableSelectAllCheckbox from "./table-select-all-checkbox"; | |
+import VirtualizedTableBody from "./virtualized-table-body"; | |
+ | |
+export interface TableProps<T = object> | |
+ extends Omit<UseTableProps<T>, "isSelectable" | "isMultiSelectable"> { | |
+ isVirtualized?: boolean; | |
+ rowHeight?: number; | |
+ maxTableHeight?: number; | |
+} | |
+ | |
+const VirtualizedTable = forwardRef<"table", TableProps>((props, ref) => { | |
+ const { | |
+ BaseComponent, | |
+ Component, | |
+ collection, | |
+ values, | |
+ topContent, | |
+ topContentPlacement, | |
+ bottomContentPlacement, | |
+ bottomContent, | |
+ removeWrapper, | |
+ getBaseProps, | |
+ getWrapperProps, | |
+ getTableProps, | |
+ } = useTable({ | |
+ ...props, | |
+ ref, | |
+ }); | |
+ | |
+ const {rowHeight = 40, maxTableHeight = 600} = props; | |
+ | |
+ const Wrapper = useCallback( | |
+ ({children}: {children: JSX.Element}) => { | |
+ if (removeWrapper) { | |
+ return children; | |
+ } | |
+ | |
+ return ( | |
+ <BaseComponent | |
+ {...getWrapperProps()} | |
+ ref={parentRef} | |
+ /* Display must be block to maintain the scroll "progress" */ | |
+ style={{height: maxTableHeight, display: "block"}} | |
+ > | |
+ {children} | |
+ </BaseComponent> | |
+ ); | |
+ }, | |
+ [removeWrapper, getWrapperProps, maxTableHeight], | |
+ ); | |
+ | |
+ const items = [...collection.body.childNodes]; | |
+ | |
+ const count = items.length; | |
+ | |
+ const parentRef = useRef(null); | |
+ | |
+ const [headerHeight, setHeaderHeight] = useState(0); | |
+ | |
+ const headerRef = useRef<HTMLTableSectionElement>(null); | |
+ | |
+ useLayoutEffect(() => { | |
+ if (headerRef.current) { | |
+ setHeaderHeight(headerRef.current.getBoundingClientRect().height); | |
+ } | |
+ }, [headerRef]); | |
+ | |
+ const rowVirtualizer = useVirtualizer({ | |
+ count, | |
+ getScrollElement: () => parentRef.current, | |
+ estimateSize: () => rowHeight, | |
+ overscan: 5, | |
+ }); | |
+ | |
+ return ( | |
+ <div {...getBaseProps()}> | |
+ {/* We need to add p-1 to make the shadow-sm visible */} | |
+ <Wrapper> | |
+ <> | |
+ {topContentPlacement === "outside" && topContent} | |
+ <div style={{height: `calc(${rowVirtualizer.getTotalSize() + headerHeight}px)`}}> | |
+ <> | |
+ {topContentPlacement === "inside" && topContent} | |
+ <Component {...getTableProps()}> | |
+ <TableRowGroup ref={headerRef} classNames={values.classNames} slots={values.slots}> | |
+ {collection.headerRows.map((headerRow) => ( | |
+ <TableHeaderRow | |
+ key={headerRow?.key} | |
+ classNames={values.classNames} | |
+ node={headerRow} | |
+ slots={values.slots} | |
+ state={values.state} | |
+ > | |
+ {[...headerRow.childNodes].map((column) => | |
+ column?.props?.isSelectionCell ? ( | |
+ <TableSelectAllCheckbox | |
+ key={column?.key} | |
+ checkboxesProps={values.checkboxesProps} | |
+ classNames={values.classNames} | |
+ color={values.color} | |
+ disableAnimation={values.disableAnimation} | |
+ node={column} | |
+ selectionMode={values.selectionMode} | |
+ slots={values.slots} | |
+ state={values.state} | |
+ /> | |
+ ) : ( | |
+ <TableColumnHeader | |
+ key={column?.key} | |
+ classNames={values.classNames} | |
+ node={column} | |
+ slots={values.slots} | |
+ state={values.state} | |
+ /> | |
+ ), | |
+ )} | |
+ </TableHeaderRow> | |
+ ))} | |
+ <Spacer as="tr" tabIndex={-1} y={1} /> | |
+ </TableRowGroup> | |
+ <VirtualizedTableBody | |
+ checkboxesProps={values.checkboxesProps} | |
+ classNames={values.classNames} | |
+ collection={values.collection} | |
+ color={values.color} | |
+ disableAnimation={values.disableAnimation} | |
+ isSelectable={values.isSelectable} | |
+ rowVirtualizer={rowVirtualizer} | |
+ selectionMode={values.selectionMode} | |
+ slots={values.slots} | |
+ state={values.state} | |
+ /> | |
+ </Component> | |
+ {bottomContentPlacement === "inside" && bottomContent} | |
+ </> | |
+ </div> | |
+ {bottomContentPlacement === "outside" && bottomContent} | |
+ </> | |
+ </Wrapper> | |
+ </div> | |
+ ); | |
+}); | |
+ | |
+VirtualizedTable.displayName = "NextUI.VirtualizedTable"; | |
+ | |
+export default VirtualizedTable;\n- packages/components/table/stories/table.stories.tsx (modified, 82 changes)\n Patch: @@ -1110,3 +1110,85 @@ export const TableWithSwitch = { | |
selectionMode: "multiple", | |
}, | |
}; | |
+ | |
+function generateRows(count) { | |
+ return Array.from({length: count}, (_, index) => ({ | |
+ key: index.toString(), | |
+ name: `Item ${index + 1}`, | |
+ value: `Value ${index + 1}`, | |
+ })); | |
+} | |
+ | |
+export const Virtualized = { | |
+ render: (args: TableProps) => { | |
+ const rows = generateRows(500); | |
+ const columns = [ | |
+ {key: "name", label: "Name"}, | |
+ {key: "value", label: "Value"}, | |
+ ]; | |
+ | |
+ return ( | |
+ <div> | |
+ <Table | |
+ aria-label="Example of virtualized table with a large dataset" | |
+ {...args} | |
+ isVirtualized | |
+ maxTableHeight={300} | |
+ rowHeight={40} | |
+ > | |
+ <TableHeader columns={columns}> | |
+ {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>} | |
+ </TableHeader> | |
+ <TableBody items={rows}> | |
+ {(item) => ( | |
+ <TableRow key={item.key}> | |
+ {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} | |
+ </TableRow> | |
+ )} | |
+ </TableBody> | |
+ </Table> | |
+ </div> | |
+ ); | |
+ }, | |
+ args: { | |
+ ...defaultProps, | |
+ className: "max-w-3xl", | |
+ }, | |
+}; | |
+ | |
+export const TenThousandRows = { | |
+ render: (args: TableProps) => { | |
+ const rows = generateRows(10000); | |
+ const columns = [ | |
+ {key: "name", label: "Name"}, | |
+ {key: "value", label: "Value"}, | |
+ ]; | |
+ | |
+ return ( | |
+ <div> | |
+ <Table | |
+ aria-label="Example of virtualized table with a large dataset" | |
+ {...args} | |
+ isVirtualized | |
+ maxTableHeight={300} | |
+ rowHeight={40} | |
+ > | |
+ <TableHeader columns={columns}> | |
+ {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>} | |
+ </TableHeader> | |
+ <TableBody items={rows}> | |
+ {(item) => ( | |
+ <TableRow key={item.key}> | |
+ {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} | |
+ </TableRow> | |
+ )} | |
+ </TableBody> | |
+ </Table> | |
+ </div> | |
+ ); | |
+ }, | |
+ args: { | |
+ ...defaultProps, | |
+ className: "max-w-3xl", | |
+ }, | |
+};\n- pnpm-lock.yaml (modified, 3 changes)\n Patch: @@ -2865,6 +2865,9 @@ importers: | |
'@react-types/table': | |
specifier: 3.10.3 | |
version: 3.10.3([email protected]) | |
+ '@tanstack/react-virtual': | |
+ specifier: ^3.10.9 | |
+ version: 3.10.9([email protected]([email protected]))([email protected]) | |
devDependencies: | |
'@nextui-org/button': | |
specifier: workspace:*\n |