Autodesk / react-base-table

A react table component to display large datasets with high performance and flexibility
https://autodesk.github.io/react-base-table/
MIT License
1.5k stars 165 forks source link

scrollToLeft not stopping current scroll, and no possibility to add initial offset #285

Open epaillous opened 3 years ago

epaillous commented 3 years ago

Hello here, First I would like to thank you for this great library, I am doing a complex screen with scroll vertical, horizontal and drag n drop, I tried to do it with react window but I was not able to have a fixed column easily, I discovered your awesome library and it simply save my life ! So thanks a lot 🙏

To add some context, I am doing this screen : planning This component displays planning of users. Among the features I have to implement, here are some elements :

I have implemented a solution and here is a codesandbox grouping only problematics on scroll (forget about drag and drop for the moment, I will do it once I solve my problems of scrolling).

So, several problems on my side : I have created a Base Table with n rows (n = numbers of users, n is growing each time we scroll vertically) and 3 columns : 1 column for user name (fixed), 1 (big) column for user planning, and 1 column blank to manage the scroll left (let's call this column "Loading").

So, first I would like to have an initial scroll offset of size "Loading column", giving to the user the impression he can scroll left. Each time the user scroll left, I will :

(same for scroll right, adding + 1 week)

I do not know how to add this initial scroll left, I am currently using :

 useEffect(
    function addInitialScrollOffset() {
      if (tableRef.current !== null) {
        tableRef.current?.scrollToLeft(loadingColumnWidth);
      }
    },
    [tableRef.current]
  );

But this does not work since tableRef.current is a mutable value and changing it does not re-render component. Adding no dependencies on the useEffect does not work either.

Moreover, here is my current onScroll method :

 const onScroll = (
    props: StaffingScrollProps,
    tableRef: RefObject<BaseTable<any>>
  ) => {
    if (
      tableRef.current === null ||
      scrollRef.current === null ||
      props.scrollUpdateWasRequested
    ) {
      return;
    }
    if (
      props.scrollLeft - SCROLL_MARGIN ===
      tableRef.current.getTotalColumnsWidth() - scrollRef.current.scrollWidth
    ) {
      const newRange = range.clone();
      newRange.end.add(1, "week");
      newRange.start.add(1, "week");
      setRange(newRange);
      tableRef.current?.scrollToLeft(WIDTH_OF_LOADING_COLUMN);
    } else if (props.scrollLeft === 0) {
      const newRange = range.clone();
      newRange.start.subtract(1, "week");
      newRange.end.subtract(1, "week");
      setRange(newRange);
      tableRef.current?.scrollToLeft(WIDTH_OF_LOADING_COLUMN);
    }
  };

The problem with this code is that sometimes when I scroll left, scrollToLeft works correctly and I get stuck on offset left = loading column, which is the behaviour I want to have. And sometimes the scroll goes to offset left = loading column, but it is not "stopped", so it continues to scroll left until 0. So there is no offset left anymore, and if user wants to scroll left again, is has to first scroll right, then scroll left to trigger the fetching of past planning.

One thing to know : I did not have the problem using react-window directly :

  tableRef.current.scrollToItem({
    columnIndex: 1,
    align: "start"
  });

worked

Finally, for the right scroll, I am using props.scrollLeft - SCROLL_MARGIN === tableRef.current.getTotalColumnsWidth() - scrollRef.current.scrollWidth to get a condition to fetch new data for future planning (where SCROLL_MARGIN is an arbitrary value of 15px), but I think this does not work well on all device screens.

Do you have any idea on how could I resolve my problems ? 😢

Thank you !

nihgwu commented 3 years ago

Hi, thanks for the detailed information, first of all I'd BaseTable would not be a good choice for your case, as it doesn't provide builtin column virtualization rendering (but you can achieve a similar behavior https://github.com/Autodesk/react-base-table/issues/36)

I'm not fully understand your problem, but yes scroll methods won't cause table re-rendering, but if you change columns it will. I don't know why would you use a very big column for the planning, it would have performance issue, why not render them as separated columns, and for loading part as well, you can have a lot of loading columns as placeholders, and fill them with data while scrolling, not sure if useIsScrolling would help here

for scrollbar width you can use getScrollbarSize

epaillous commented 3 years ago

Thanks for your answer !

At the beginning I was thinking to use react-window directly to virtualize columns for each day, but :

Because of these features it was complicated to considered each day as a single column. So my solution was to consider a "window" of 3 weeks as the main column (so the column width is 21 days * DAY_WIDTH = 1680px). Each time we scroll in the past (or the future), i shift my window 1 week in the past (or 1 week in the future). So the size of the window (and so the size of column) is always the same. With this solution I do not really need virtualisation on columns, in terms of rendering / performance I have the feeling that I will not have gained a lot using virtual columns (but I could be wrong).

I will try getScrollbarSize to get the scroll width.

Thank you for the time you took to answer me, and for this awesome library :)