petyosi / react-virtuoso

The most powerful virtual list component for React
https://virtuoso.dev
MIT License
5.25k stars 301 forks source link

Support grid layout? #13

Closed FezVrasta closed 5 years ago

FezVrasta commented 5 years ago

Hi, I was wondering if you do plan to support a grid layout? I think a simple fixed column width grid support would be more than enough to cover most of the use cases.

petyosi commented 5 years ago

Yes, I do. Just like you state, I think that fixed width columns are sufficient enough. I don't want to enter the spreadsheet territory :).

Regarding the grid layout: a thing which I consider mandatory is rendering using a single table tag with thead, tbody, so on. Rendering tables as div elements is a major crime against a11y in my book.

Let me know what you think about that. Cheers!

FezVrasta commented 5 years ago

My use case is to render a list of "cards", all with the same height and width, I don't think table would make sense in my case, since it's not a "tabular data", it's just a list of elements that goes on a new line when there's no more room on the current row.

Also, table would make the grid difficult to make responsive (so that the number of columns adapts to the page width)

petyosi commented 5 years ago

I think I understand what you mean; something like a flexbox / CSS grid-ish layout - for example, https://codepen.io/underlog/pen/joveop. Would virtualizing the layout in this pen work for you?

Some thoughts - these layouts are responsive. Specifying the width/number of columns through JS is a road of pain and futile attempts to re-implement the browser layout engine. I would much rather have the sizing applied through CSS / media queries and have the JS engine infer the number of items necessary to fill in the viewport.

FezVrasta commented 5 years ago

Yes that's the layout I'm talking about. Right now I use react-virtualized but it's something like 600 lines of code just to render that grid 😓

petyosi commented 5 years ago

Hey @FezVrasta - I just published v0.8.0 which includes VirtuosoGrid - I believe that it should address the case you describe. Check the responsive columns demo. Would love to get some feedback on the approach. Cheers!

FezVrasta commented 5 years ago

I will give it a shot on Monday! Thank you so much!

FezVrasta commented 5 years ago

The itemClassName and listClassName properties aren't very CSS-in-JS friendly, with Emotion 10 I need to wrap the component into a ClassNames render-prop to get access to the css method.

petyosi commented 5 years ago

Thanks, What would be the idiomatic way to handle that in the CSS-in-JS world (I am new to it, so pardon my ignorance)? I can expose *Container props which accept custom components, like the ones in the initial Virtuoso components.

FezVrasta commented 5 years ago

That would probably work, the end result would be:

const ItemContainer = styled.div`
  [...]
`;
const ListContainer = styled.div`
  display: flex;
  flex-wrap: wrap;
`;
const Grid = styled(VirtuosoGrid)`
  width: 100%;
  height: 500px;
`;

<Grid itemContainer={ItemContainer} listContainer={ListContainer} />
FezVrasta commented 5 years ago

I tried to switch out my react-virtualized grid with VirtuosoGrid, but as soon I try to render my real elements and scroll a bit, the whole list starts flickering.

(The element contains text inside, I had to hide everything with > * { opacity: 0 } because I'm under NDA)

If I render something simple such as <div>{index}</div> everything works fine. I know it's a long shot, but do you have any idea?

Kapture 2019-07-01 at 14 11 45

petyosi commented 5 years ago

Thanks for the suggestion, I will try the container approach.

Re: the flicker - a wild guess would be elements with margins in the content, that would mess up the calculations.

Another (less likely) is a rounding issue (I have discovered two cases that led me to this fix: https://github.com/petyosi/react-virtuoso/commit/6bbbca0b17a53da9cc2b0d7cc8787e97ff51a165).

A repro case would definitely help me address that. Feel free to reach out on the email from my profile if necessary.

Cheers,

FezVrasta commented 5 years ago

I'm already on 0.8.2 which should include your recent fix.

My grid implementation is:

// @flow
import React, { type Node } from 'react';
import { ClassNames } from '@emotion/core';
import { VirtuosoGrid } from 'react-virtuoso';

const CARD_WIDTH = 364 + 15 * 2;
const CARD_HEIGHT = 247 + 15 * 2;

const Grid = <T>({
  items,
  item,
  fetchMore,
}: {
  items: Array<T>,
  item: (T, number) => Node,
  fetchMore: number => void,
}) => {
  return (
    <ClassNames>
      {({ css }) => (
        <VirtuosoGrid
          totalCount={items.length}
          style={{ height: '100%', width: '100%' }}
          itemClassName={css`
            width: ${CARD_WIDTH}px;
            height: ${CARD_HEIGHT}px;
            flex: none;
          `}
          listClassName={css`
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            &:not(:focus-visible) {
              outline: none;
            }
          `}
          endReached={index => fetchMore(index)}
          item={index => item(items[index], index)}
        />
      )}
    </ClassNames>
  );
};

export default Grid;

I think the CSS is fine, the margins are applied internally the item, not on its container. Unfortunately I can't share the render item function, I'll try to reduce the code until I find the issue.

petyosi commented 5 years ago

Just published v0.9.0 with containers exposed and updated the demo.

Some debugging ideas:

FezVrasta commented 5 years ago

Gonna close this issue since it seems solved for the general public, I'll open a specific one if I ever find out what's going on with my implementation :-)

johnvonneumann7 commented 2 years ago

I need to view the Loading circle with this, but why is there no Footer entry in VirtuosoGrid's components?