Vaadin 21:将 Scroller 嵌套到 VerticalLayout 问题

Vaadin 21: Nesting Scroller into VerticalLayout problem

给定一个有 3 个嵌套 VerticalLayoutComposite,每个都有全高
而一个Scroller嵌套到最里面的VerticalLayout,
Scroller 包含第四个 VerticalLayout 和 20 Buttons

Composite 渲染时

然后我希望最外面的 VerticalLayout 在屏幕上完全可见
VerticalLayouts 和滚动条整齐地嵌套
Scroller 尽可能小,所以一切都适合屏幕
并且根本不可能滚动外部 VerticalLayouts(只要屏幕足够大以容纳 4 VerticalLayouts、一个 Scroller 和至少一个 Button

但是最外面的VerticalLayout不适合屏幕
Scroller 允许滚动 Button,但最后一个 Button 只能在滚动 3 个外部 VerticalLayout 时看到(这不应该发生)。
滚动 3 个外部 VerticalLayouts 显示外部 VerticalLayouts 的奇怪重叠。

初始状态

Scroller滚动到底部后

整个canvas滚动到底部后

如何实现所需的 nesting/scrolling 行为?

这是我的代码

@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;
    }
}

我在第一次创建我的 Vaadin 应用程序的主要布局时处理了这个问题。它实际上是 flexbox 布局的样式问题,而不是 Vaadin 特有的。 VerticalLayout 对象的计算最小高度是其内容的高度。如果您在垂直布局上设置 css min-height: 0,它将允许它们小于其内容。

您可以在此处了解更多信息:

由于开发人员忘记设置最小高度,这对我们来说一直是倒退。 VerticalLayout 很方便,但有很多主题和样式融入其中,可以很好地显示单独的控件。如果您正在寻找用于构建页面的可重用 flexbox 组件,请考虑创建一个新组件。

@Tag("column-layout")
@CssImport("layouts.css")
public class ColumnLayout extends Div {
    public ColumnLayout(Component... components) {
        super(components);
    }
}

采用这种风格:

column-layout {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

在您的示例中实现:

@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;
    }
}