Skip to content

Instantly share code, notes, and snippets.

@TatuLund
Last active April 23, 2024 10:19
Show Gist options
  • Select an option

  • Save TatuLund/2cf890b9dd130bbcb9123bb3c7a5bbff to your computer and use it in GitHub Desktop.

Select an option

Save TatuLund/2cf890b9dd130bbcb9123bb3c7a5bbff to your computer and use it in GitHub Desktop.
Example for Vaadin 23 to workaround issues with scrollToIndex in TreeGrid. Note, in Vaadin 24 there is new hierarchical scrollToIndex method, and this example does not work with the latest Vaadin 24.
package org.vaadin.tatu;
import java.util.Collections;
import java.util.LinkedList;
import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.treegrid.TreeGrid;
import com.vaadin.flow.data.provider.hierarchy.TreeData;
import com.vaadin.flow.data.provider.hierarchy.TreeDataProvider;
import com.vaadin.flow.router.Route;
import elemental.json.JsonValue;
@Route("treegrid")
public class TreeGridScrollingEnhancedPage extends Div {
TreeGrid<String> grid = new TreeGrid<>();
TreeData<String> treeData = new TreeData<>();
private Integer index = 0;
public TreeGridScrollingEnhancedPage() {
// Grid with single column
grid.addHierarchyColumn(item -> item);
// Dummy data
treeData.addRootItems("A", "B", "C", "D", "E");
treeData.addItems("A", "A1", "A2", "A3");
treeData.addItems("A3", "A3-1", "A3-2");
treeData.addItems("A3-1", "A3-1-1", "A3-1-2", "A3-1-3");
treeData.addItems("B", "B1", "B2", "B3");
treeData.addItems("C", "C1", "C2", "C3", "C4");
treeData.addItems("D", "D1", "D2");
treeData.addItems("D2", "D2-1", "D2-2", "D2-3", "D2-4");
treeData.addItems("D2-1", "D2-1-1", "D2-1-2", "D2-1-3");
treeData.addItems("E", "E1", "E2", "E3", "E4", "E5");
treeData.addItems("E2", "E2-1", "E2-2", "E2-3", "E2-4");
treeData.addItems("E4", "E4-1", "E4-2", "E4-3");
treeData.addItems("E4-1", "E4-1-1", "E4-1-2", "E4-1-3", "E4-1-4");
treeData.addItems("E4-2", "E4-2-1", "E4-2-2", "E4-2-3");
treeData.addItems("E4-3", "E4-3-1", "E4-3-2");
treeData.addItems("E5", "E5-1", "E5-2");
treeData.addItems("E5-2", "E5-2-1", "E5-2-2", "E5-2-3");
TreeDataProvider<String> provider = new TreeDataProvider<>(treeData);
grid.setDataProvider(provider);
// Delayed scrolling from the cookbook example
grid.getElement()
.executeJs("this.scrollWhenReady = function(index, firstCall){"
+ "if(this.loading || firstCall) {var that = this; setTimeout(function(){that.scrollWhenReady(index, false);}, 200);}"
+ " else {this.scrollToIndex(index);}" + "};");
grid.getElement().executeJs(
"function debounce(func, wait) {let timeout; return () => {if (timeout) {clearTimeout(timeout);} timeout = setTimeout(func, wait);}} const onScroll = debounce(() => $0.$server.firstIndex($1._firstVisibleIndex), 100); $1.$.table.addEventListener('scroll', onScroll);",
getElement(), grid.getElement());
TextField tf = new TextField("Target Item");
tf.setValue("E4-2-1");
Button button = new Button("Scroll to item", e -> {
String targetItem = tf.getValue();
if (treeData.contains(targetItem)) {
expandAndScroll(targetItem);
}
});
Button refresh = new Button("Refresh", e -> {
int savedIndex = index;
provider.refreshAll();
System.out.println("scrollTo: " + savedIndex);
this.getElement().executeJs("return;").then(result -> {
grid.scrollToIndex(savedIndex);
});
});
add(tf, button, refresh, grid);
}
@ClientCallable
private void firstIndex(JsonValue index) {
this.index = Integer.valueOf(index.toJson());
System.out.println("firstIndex: " + this.index);
}
void expandAndScroll(String targetItem) {
LinkedList<String> path = getTreePath(targetItem, new LinkedList<>());
Collections.reverse(path);
path.add(targetItem);
// Row index of the item with fully expanded root item
int itemFlatIndex = expandAndCount(path);
// Calculate the total number of visible items till the root of target
// item
int totalItems = treeData.getRootItems()
.subList(0, treeData.getRootItems().indexOf(path.get(0)))
.stream().map(this::countItems).reduce(Integer::sum).orElse(0);
grid.getElement().executeJs("this.scrollWhenReady($0, true);",
totalItems + itemFlatIndex);
}
// Count child nodes for item
int countItems(String item) {
int leaves = 0;
if (treeData.getChildren(item).size() == 0 || !grid.isExpanded(item)) {
return 1;
} else {
// ADD PARENT ITEM
leaves += 1;
}
for (int i = 0; i < treeData.getChildren(item).size(); i++) {
leaves += countItems(treeData.getChildren(item).get(i));
}
return leaves;
}
// Getting the index of target item and expanding nodes on the way to it
int expandAndCount(LinkedList<String> path) {
int totalCount = 0;
for (int i = 0; i < path.size(); i++) {
if (!grid.isExpanded(path.get(i))) {
grid.expand(path.get(i));
}
if (i + 1 < path.size()) {
totalCount += treeData.getChildren(path.get(i))
.indexOf(path.get(i + 1)) + 1;
}
}
return totalCount;
}
// Generating list of parents from the item till root
LinkedList<String> getTreePath(String item, LinkedList<String> parents) {
String parent = treeData.getParent(item);
if (parent != null) {
parents.add(parent);
return getTreePath(parent, parents);
}
return parents;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment