Reputation: 325
Given a Composite
with 3 nested VerticalLayout
s, with full height each
And a Scroller
nested into the innermost VerticalLayout
,
And the Scroller
contains a fourth VerticalLayout
with 20 Button
s
When the Composite
is rendered
Then I would expect that the outermost VerticalLayout
is fully visible on screen
And the VerticalLayout
s and the scroller are neatly nested
And the Scroller
is as small as required so everything fits on screen
And it is not possible to scroll the outer VerticalLayout
s at all (as long as the screen is large enough to hold 4 VerticalLayout
s, a Scroller
, and at least one Button
But the outermost VerticalLayout
does not fit on screen
The Scroller
allows scrolling through the Button
s, but the last Button
can only be seen when the 3 outer VerticalLayout
s are scrolled (which is not supposed to happen).
Scrolling the 3 outer VerticalLayout
s shows a weird overlap of the outer VerticalLayout
s.
Initial State
After scrolling the Scroller
to the bottom
After scrolling the whole canvas to the bottom
How do I achieve the desired nesting/scrolling behavior?
Here is my code
@Route("3_scrollerx")
public class ScrollerxView extends Composite<Component> {
@Override
protected Component initContent() {
final VerticalLayout lvl1 = new VerticalLayout(new Label("lvl1"));
final VerticalLayout lvl2 = new VerticalLayout(new Label("lvl2"));
final VerticalLayout lvl3 = new VerticalLayout(new Label("lvl3"));
final Scroller scroller = new Scroller();
scroller.setContent(buildScrollerContent());
scroller.setHeightFull();
List.of(lvl1, lvl2, lvl3).forEach(layout -> {
layout.setHeightFull();
layout.getStyle().set("border", "1px solid");
});
scroller.getStyle().set("border", "1px solid");
lvl1.add(lvl2);
lvl2.add(lvl3);
lvl3.add(scroller); // always weird
return lvl1;
}
private VerticalLayout buildScrollerContent() {
final var scrollerContent = new VerticalLayout();
for (int i = 1; i <= 20; i++) {
scrollerContent.add(new Button("Button " + i));
}
scrollerContent.setHeightFull();
return scrollerContent;
}
}
Upvotes: 0
Views: 180
Reputation: 448
I dealt with this when first creating the main layouts of my Vaadin application. It is actually a styling concern of the flexbox layout, and not specific to Vaadin. The computed minimum height of the VerticalLayout
objects is the height of their content. If you set the css min-height: 0
on the vertical layouts it will allow them to be smaller than their content.
You can learn more here:
This kept cropping up as regressions for us as developers forgot to set that min height. VerticalLayout is convenient, but has a lot of theming and styling baked into it for displaying individually controls well. If you are looking for a reusable flexbox component for structuring pages, consider creating a new component.
@Tag("column-layout")
@CssImport("layouts.css")
public class ColumnLayout extends Div {
public ColumnLayout(Component... components) {
super(components);
}
}
With this style:
column-layout {
box-sizing: border-box;
display: flex;
flex-direction: column;
min-height: 0;
}
Implemented in your example:
@Route("3_scrollerx")
public class ScrollerxView extends Composite<Component> {
@Override
protected Component initContent() {
final ColumnLayout lvl1 = new ColumnLayout(new Label("lvl1"));
final ColumnLayout lvl2 = new ColumnLayout(new Label("lvl2"));
final ColumnLayout lvl3 = new ColumnLayout(new Label("lvl3"));
VerticalLayout scrollerContent = buildScrollerContent();
List.of(lvl1, lvl2, lvl3, scrollerContent).forEach(layout -> {
layout.setHeightFull();
layout.getStyle().set("border", "1px solid");
});
lvl1.add(lvl2);
lvl2.add(lvl3);
lvl3.add(scrollerContent);
return lvl1;
}
private VerticalLayout buildScrollerContent() {
final var scrollerContent = new VerticalLayout();
for (int i = 1; i <= 20; i++) {
scrollerContent.add(new Button("Button " + i));
}
scrollerContent.getStyle().set("overflow", "auto");
return scrollerContent;
}
}
Upvotes: 1