vaadin / web-components

A set of high-quality standards based web components for enterprise web applications. Part of Vaadin 20+
https://vaadin.com/docs/latest/components
430 stars 82 forks source link

[grid] Calling `recalculateColumnWidths` method automatically scrolls the grid horizontally to the starting position #1273

Open NadeemShakya opened 3 years ago

NadeemShakya commented 3 years ago

Description

I've listened to scroll events and added my own logic with something like grid.$.table.addEventListener('scroll', grid.recalculateColumnWidths()).

This causes the grid columns with auto-width to update.

Now, when I scroll the grid vertically, the code works as expected without any issues.

While scrolling the grid horizontally (I have a large table so have a horizontal scrollbar on the grid), the following happens:

Expected outcome

I wish the grid to scroll horizontally to my desired location and stay fixed.

image

Actual outcome

The grid scrolls back to the starting position and doesn't stick to the location where I scrolled the grid.

image

In the above image, the grid has scrolled automatically to this starting position from my desired location.

Browsers Affected

@tomivirkki @web-padawan

web-padawan commented 3 years ago

Confirmed, can be reproduced using the demo. https://cdn.vaadin.com/vaadin-grid/5.6.10/demo/#grid-columns-demos

tomivirkki commented 3 years ago

Workaround:

grid.$.table.addEventListener('scroll', () => {
  const scrollLeft = grid.$.table.scrollLeft;
  grid.recalculateColumnWidths()
  grid.$.table.scrollLeft = scrollLeft;
});

I recommend using some kind of throttling for this however, as running recalculateColumnWidths on every single scroll event would negatively affect scrolling performance.

NadeemShakya commented 3 years ago

Workaround:

grid.$.table.addEventListener('scroll', () => {
  const scrollLeft = grid.$.table.scrollLeft;
  grid.recalculateColumnWidths()
  grid.$.table.scrollLeft = scrollLeft;
});

I recommend using some kind of throttling for this however, as running recalculateColumnWidths on every single scroll event would negatively affect scrolling performance.

@tomivirkki I'm afraid it won't work. Because what happens is:

Now, this is where the problem begins:

Now,

This way, the problem still remains.

tomivirkki commented 3 years ago

Maybe you can set a flag to disable the listener logic while recalculate is being run:

NadeemShakya commented 3 years ago

@tomivirkki :stop_sign: Ooooops! It didn't work! :cry:

Because, I'm using LitElement and as far as I understand, LitElement performs batch rendering to the updated props. So, in that case,

First batch rendered animation frame:

Second batch animation frame:

tomivirkki commented 3 years ago

Ah, I think what's going on. We're probably using different grid versions. With the newest version, the original workaround works just fine. See it running live at https://productive-neat-manuscript.glitch.me/ (source: https://glitch.com/edit/#!/productive-neat-manuscript)

stefanuebe commented 1 year ago

We have the same issue when using column width recalculation on a column sort. The snippet from Tomi helped to solve the issue in that case.

stefanuebe commented 1 month ago

Update on this issue. Using recalculateCW with auto width columns can lead to mispositioned headers:

image

Not sure, if this is part of this issue or something different, but it relates at least to the given workaround

To fix that, another scroll left helps:

let left = this.$.table.scrollLeft;
let top = this.$.table.scrollTop; 
this.recalculateColumnWidths();
this.$.table.scrollLeft = left;
this.$.table.scrollTop = top;
this.$.table.scrollLeft = this.$.table.scrollLeft - 1;
stefanuebe commented 1 month ago

Some sample code to reproduce the issue with the left scroll. It also contains the basic workaround, that will keep the scroll position. This one will lead to misaligned headers. It also contains the additional workaround to realign the headers correctly (both commented out)

var grid = new Grid<Integer>();
grid.addThemeVariants(GridVariant.LUMO_COLUMN_BORDERS);
grid.setItems(IntStream.range(0, 500).boxed().collect(Collectors.toList()));
grid.setColumnRendering(ColumnRendering.LAZY);
DataProvider<Integer, ?> dataProvider = grid.getDataProvider();

dataProvider.addDataProviderListener(event -> {
    // using this instead of the normal recalc fixes the left jump
    //            grid.getElement().executeJs("""
//                if(this && this.recalculateColumnWidths) {
//                    let left = this.$.table.scrollLeft;
//                    let top = this.$.table.scrollTop;
//                    this.recalculateColumnWidths();
//                    this.$.table.scrollLeft = left;
//                    this.$.table.scrollTop = top;
//
//                    // this fixes the problem with the misaligned headers
//                    left = this.$.table.scrollLeft;
//                    this.$.table.scrollLeft = this.$.table.scrollLeft - 0.1;
//                    setTimeout(() => {
//                        this.$.table.scrollLeft = left;
//                    }, 300);
//                }""");

    grid.recalculateColumnWidths();
});

for (int i = 0; i < 50; i++) {
    grid.addColumn(item -> "very very very very very very very very very very very very very very very long string value").setHeader("HELLO WORLD").setAutoWidth(true);
}
add(new Button("Refresh", e -> grid.getDataProvider().refreshAll()), grid);
grid.setSizeFull();
setSizeFull();