Repository navigation
Conversation
…s for more React Aria features - Table.ResizableContainer and `allowsResizing` on Table.Head, with a wide hit area on the divider - Table.DropIndicator for drag and drop, and a drag handle column - Table.LoadMoreItem for infinite scroll, and `wrapperClassName` for a fixed-height scroll container - New demos: resizable columns, column visibility, column header menu, expandable rows, drag and drop, infinite scroll, bulk actions, row actions, disabled rows, editable cells, footer row and a live table that inserts rows as they arrive
Built on React Aria's table. Columns are described as data (`field`, `type`, `valueGetter`, `valueFormatter`, `renderCell`, `editable`, ...), and the grid handles the rest: - sorting, a filter panel with operators per column type, and a quick filter - column menu: sort, pin left or right, filter, aggregate, group by, hide, manage and resize columns - resizable, hideable and pinnable columns, with sticky headers - cell editing (double-click, Enter, F2 or typing), with `onRowUpdate` to save or revert - row grouping and aggregation (sum, average, min, max, size) with a totals row - checkbox selection across pages, pagination, three densities and a loading state - toolbar with columns, filters, density, CSV export, print and quick search - keyboard support following MUI: Enter sorts a header, Ctrl+Enter opens its menu Demos reproduce the MUI examples with a seeded commodity dataset.
…ogress bars with ProgressBar React Aria keeps column widths in the table's state, so its resizer re-rendered every row and cell on each pointer move. Dragging a divider now sets the width on the header directly and hands it to React Aria once, on release. React Aria's resizer still handles keyboard resizing from the column menu. Pinned columns read their offsets from CSS variables that a ResizeObserver updates, so measuring them while a column resizes no longer re-renders the grid either. The dividers use logical positions, so they sit on the right side in RTL. The demo's "Filled quantity" column uses our ProgressBar instead of its own colored bars.
An arbitrary ease-[...] class also emits an animation-timing-function rule from the animate plugin, which the inlined plugins of the v0 previews can't reproduce. The transition reads --tw-ease, so the curve is unchanged.
Moves createFilterItem to data-grid-utils and stops exporting densityOptions, so the toolbar file passes the react/only-export-components rule that Vite projects enable.
Table gains generic hooks the grid needs, and the grid's header, rows and cells become Table.Head, Table.Row and Table.Cell: static class names, selectionColumnProps/selectionCellProps, forwarded dependencies, align and contentTrailing on Table.Head, isResizerFocusable, refs on Table.Head and Table.ResizableContainer, and the DOM-based column resizer (for every resizable table).
Table.Virtualizer renders only the rows and columns in view, with React Aria's Virtualizer and a TableLayout subclass that adds sticky columns (left, and right with the grid's CSS) and an optional sticky footer. Table's parts get div styles when virtualized. The grid takes a virtualized prop; the 10,000-row demo scrolls without pages.
… Aria TableContext got a new value on every render, which re-rendered every Table.Row and Table.Cell in React Aria's collection: with 10,000 rows, selecting one took seconds. The grid also stops controlling expandedKeys, which made React Aria copy the whole collection on every render; group ids include the field instead, so regrouping starts collapsed.
The new parts (Table.ResizableContainer, Table.Footer, Table.DropIndicator and Table.LoadMoreItem) move after Table.Cell, and the compound object lists the original parts first. Code is unchanged.
Resizing, the footer, drop indicators, load-more rows and virtualization move after Table.Cell. Code is unchanged.
The data grid's header, rows and cells become Table.Head, Table.Row and Table.Cell, Table gains the hooks the grid needs and Table.Virtualizer, and the grid takes a virtualized prop. # Conflicts: # components/application/table/table.tsx
With 9 rows and 5 per page, the second page had 4 rows and the grid got shorter.
The grid tinted selected rows with the brand color, copied from MUI. Selected rows and pinned cells now use Table's bg-secondary, and rows keep Table's transition.
The features stay. Their look now comes from Table and our components: - Rows, headers, cells and the checkbox column use Table's sizes. The densities are Table's sm and md sizes (compact and standard). - The grid is a TableCard.Root. Headers use Table.Head's label, without the aggregation function under it or the filter icon next to it. - The column menu button is the chevron from Table's column menu example, always shown, after the sort indicator. - The toolbar is laid out like the search and filters bar of Table's examples, and its panels use Dropdown.Popover. - The footer is laid out like PaginationCardMinimal, with a badge for the selected rows. - Loading shows LoadingIndicator, and an empty grid shows EmptyState, with a button that clears the filters. - The commodity columns are wider, so their headers fit with Table's padding. Table: contentTrailing sits right after the sort indicator, and a virtualized table pulls the first column after the checkboxes closer, like a full table does.
… expanded - initialState.expandedGroups expands groups of the initial grouping by their label. React Aria keeps the expansion, so it applies on mount. - The row grouping demo is taller, and its first group starts expanded. The 10,000-row demo is taller too. - New editing demo: every editor type, and an onRowUpdate that rejects an empty name, which reverts the edit. - New live data demo: prices update every second, with a pause button, like the table's live demo. Only the changed rows get new objects.
The docs snippet of the demo then passes Prettier when it's copied into a project.
React Aria builds a node for every row and cell it gets, also on the server. A virtualized grid with 10,000 rows took minutes to render the docs page on the server. It now gets its first 50 rows on the server and while it hydrates, and every row right after.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a data grid component modelled on the MUI X Data Grid, built on our
Table, and table demos for React Aria features we didn't use yet.Tablegains the pieces both need, including virtualization.Table
New parts and options, all opt-in. The 26 existing table stories are pixel-identical before and after.
Table.ResizableContainerandallowsResizingonTable.Head. Dragging the divider resizes the column in the DOM and gives React Aria the new width once, on release; React Aria's own resizer re-rendered every row on each pointer move. Its resizer still handles keyboard resizing.Table.Virtualizer: renders only the rows and columns in view, withstickyColumnsandstickyFooter. It wraps React Aria'sVirtualizerwith aTableLayoutsubclass. Virtualized tables look the same as full ones, down to the narrower gap after the checkbox column.Table.DropIndicatorand a drag handle column for drag and drop,Table.LoadMoreItemfor infinite scroll,Table.Footer, andwrapperClassNamefor a fixed-height scroll container.selectionColumnPropsandselectionCellPropsstyle the checkbox column;align,contentTrailingandisResizerFocusableonTable.Head;refonTable.HeadandTable.ResizableContainer;dependenciesreach the column collections.TableContexthad a new value on every render, which re-rendered every row and cell React Aria builds behind the scenes.Table.Cellintable.tsx, so the main parts stay at the top.Data grid
components/application/data-grid/. It copies MUI's features, not its look.TableCard, and its header, rows and cells areTable.Head,Table.RowandTable.Cell, withTable's sizes, so it looks likeTable.Table's examples, and its menus and panels useDropdown.Table's column menu example.PaginationCardMinimal.LoadingIndicatorandEmptyState.It adds the data layer on top: columns described as data (
field,type,valueGetter,valueFormatter,renderCell,editable, …), and:onRowUpdatesaves the row, or reverts it by throwing.initialState.expandedGroupsexpands groups when the grid mounts.disableRowSelectionOnClick.Table'ssmandmdsizes.virtualizedrenders only the rows and columns in view, sopagination={false}can scroll through thousands of rows.The demos reproduce MUI's examples with a seeded commodity dataset (
commodity-data.ts,commodity-columns.tsx). More demos show cell editing (with anonRowUpdatethat rejects an empty name), live data (prices that update every second), and virtualization, one of them with 10,000 rows.Not reproduced from MUI
Multi-column sorting, column reordering, clipboard copy and paste, Excel export, pivoting, tree data, master-detail rows, row pinning and header filters.
Performance
Production build, input to next paint, median ms (click / select / edit / sort). Measured before the grid took
Table's sizes. Its rows are taller now, so fewer of them render when virtualized:virtualizedWithout virtualization, React Aria re-renders every cell on the page when the focus or the selection changes, so the cost grows with the page size. Virtualized, about 200 cells are in the DOM at any page size. Scrolling holds 60fps in both modes.
Known limits of virtualization
TableLayoutmethods, so a React Aria upgrade could break them.Verification
bun run test(tsc, ESLint, Prettier) passes.Table's look, its results changed only where the look did, such as the page text and the row heights.Tablechanges, and resizing works by drag and keyboard.Breaking changes
None. Every
Tablechange is additive.Website PR with the docs page and the registry: a-peak-works/untitledui-react#363