Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Grid table virtualization #93

Merged
merged 24 commits into from
Jun 3, 2021
Merged

feat: Grid table virtualization #93

merged 24 commits into from
Jun 3, 2021

Conversation

stephenh
Copy link
Contributor

@stephenh stephenh commented May 31, 2021

See comments but I played around with:

  • react-window (looks like it was the best, but maintainer has moved on, and I couldn't immediately get it to play nicely with display: contents),
  • react-virtual (headless just-a-hook, looks really small and tight but right away was pretty finicky), and ended up on
  • react-virtuoso (seems like the new "best" and is actively maintained, tons of features, but also largest bundle size I've heard but not actually checked/compared)

Also, specifically react-virtuoso seems to be the only library that does measured per item sizing. react-window let's you provide a size variable (like we would have a per-kind RowStyle[kind].height) but AFAIU (could be wrong, but I think?) you have to know it up-front without measuring it from the DOM. Same with react-aria's useVirtualization afaict (I looked into the source code and eventually got to "these table rows are X height, those are Y height"). So, dunno, I think know-height-ahead-of-time-based-on-row-kind would be an okay limitation for us if we had to go that way, but the "meh just measure it and it magically works" is pretty great imo.

I extended the as from table | div to table | div | virtual, which ended up being a pretty nice/cute slice point.

I did have to patch react-virtuoso to work around "display: contents shows up as height zero":

petyosi/react-virtuoso@master...homebound-team:probe-item-size

And am working on upstreaming it:

petyosi/react-virtuoso#375

UPDATE: ^ is now merged into the latest react-virtuoso link, and I've updated the PR to use that instead of our fork.

@@ -245,7 +243,6 @@ export function GridTable<R extends Kinded, S = {}, X extends Only<GridTableXss,
isCollapsed,
toggleCollapsedId,
...sortProps,
persistCollapse,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prop was being passed but not used

@stephenh stephenh changed the title Grid table virtualization feat: Grid table virtualization May 31, 2021
...((rowStyle?.rowLink || rowStyle?.onClick) && {
// Even though backgroundColor is set on the cellCss (due to display: content), the hover target is the row.
"&:hover > *": Css.cursorPointer.bgColor(maybeDarken(rowStyleCellCss?.backgroundColor, style.rowHoverColor)).$,
}),
...maybeApplyFunction(row, rowStyle?.rowCss),
};

const TableRow = as === "div" ? "div" : "tr";
const Row = as === "table" ? "tr" : "div";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the changes from here down are flipping logic from if div ... else table to be if table ... else div because the "else div" applies to both as div as well as as virtual.

@stephenh stephenh force-pushed the grid-table-virtualization branch 2 times, most recently from a6ce36b to 7088074 Compare June 1, 2021 17:35
...Css
// Apply the between-row styling with `div + div > *` so that we don't have to have conditional
// `if !lastRow add border` CSS applied via JS that would mean the row can't be React.memo'd.
// The `div + div` is also the "owl operator", i.e. don't apply to the 1st row.
.addIn("& > div + div > *, & > tbody > tr ", style.betweenRowsCss)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This renderCssGrid got simpler b/c the table logic got moved into renderTable, which also made things like maybeWrapWith go away, so I think turned out.

Copy link
Contributor

@KoltonG KoltonG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍣 I've checked out the Storybook and it's neat to see the virtualization. I was surprised that it did not keep the already rendered div's though as the work has been done (maybe for really really big tables it's worth removing them).

@@ -120,7 +124,7 @@ export interface GridTableProps<R extends Kinded, S, X> {
* @example
* { header: "Name", data: ({ name }) => name, w: "75px", align: "right" }
*/
as?: TableAs;
as?: RenderAs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on defaulting to Virtual all the time? Is there any benefit not to? And Keeping as the way it was?

Copy link
Contributor Author

@stephenh stephenh Jun 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the as=virtual needs its container to be pinned to "effectively 100% - chrome/padding/whatever", so I don't think we can just flip existing usages over.

Also I think virtual has a slightly worse UX for things like "find in page"...

But, dunno. I'd thought of something like "if number of rows is over 100, flip to virtual by default if as isn't explicitly set to div or table", but I think that'd be even trickier to get right on "my DOM parent sometimes / sometimes doesn't need to set the height in this specific way".

@KoltonG
Copy link
Contributor

KoltonG commented Jun 2, 2021

I see that they mentioned they work with Grid (they are headless) but wondering if the API gives us anything to get rid of the display: content?

🔗 https://virtuoso.dev/grid-responsive-columns/

@stephenh
Copy link
Contributor Author

stephenh commented Jun 2, 2021

I was surprised that it did not keep the already rendered div's though as the work has been done

Yeah, afaict basically none of the virtualization libraries do that, but they each have issues requesting it:

bvaughn/react-window#234

petyosi/react-virtuoso#298

I've also tried to google around about how to "keep DOM nodes that you don't technically want in the DOM" and it seems like React just doesn't like doing that. I.e. tricking the reconciler to keep around the DOM node that is already rendered is really hard to do, at least with a provided-by-React API/approach. So I think you'd have to resort to like "keeping it in the DOM but display: none" or something like that. Which ... would probably work? Dunno.

I agree it seems like an obvious thing to try and do... 🤷 Maybe we can try and do a PR for react-virtuoso on next hack day. :-)

@KoltonG
Copy link
Contributor

KoltonG commented Jun 2, 2021

I think we worked on a few tickets to get our tables to scroll to a certain sections, this would be nice to use https://virtuoso.dev/scroll-to-index/

@KoltonG
Copy link
Contributor

KoltonG commented Jun 2, 2021

I see that they mentioned they work with Grid (they are headless) but wondering if the API gives us anything to get rid of the display: content?

🔗 https://virtuoso.dev/grid-responsive-columns/

This way we can get rid of the table approach!

@stephenh
Copy link
Contributor Author

stephenh commented Jun 2, 2021

https://virtuoso.dev/grid-responsive-columns/

Yeah I saw that, but didn't think it was for "rows in a table", vs. just "a long list of items". Granted, that is what CSS grid is... And yeah, they do mention "css grid" there, which I'd missed... (good catch)

That said, the reason we added the extra "div display: contents in the middle" in the first place was to do row-level hovers and row-levels click targets. So I think we need to keep the display: contents approach to keep supporting those features.

@stephenh
Copy link
Contributor Author

stephenh commented Jun 2, 2021

this would be nice to use https://virtuoso.dev/scroll-to-index/

Yep! Wanted to leave it out of this initial PR but saw that / agreed we should figure out how to use it.

@stephenh stephenh force-pushed the grid-table-virtualization branch from 12ea5ce to 3ae470f Compare June 3, 2021 00:31
@stephenh stephenh merged commit 3af84a5 into main Jun 3, 2021
@stephenh stephenh deleted the grid-table-virtualization branch June 3, 2021 12:49
homebound-team-bot pushed a commit that referenced this pull request Jun 3, 2021
## [1.35.0](v1.34.0...v1.35.0) (2021-06-03)

### Features

* Grid table virtualization ([#93](#93)) ([3af84a5](3af84a5))
@homebound-team-bot
Copy link

🎉 This PR is included in version 1.35.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants