tanderson
tanderson

Reputation: 1219

Vaadin 14: scrollable content area

I'm trying to reproduce the layout below in Vaadin 14.

layout

The content area should fill the available space. How can I have scrollbars on just the content area, if the content is too large?

With the code below, scrollbars are added to both the main page and the content area.

As a workaround, I can get it to work by giving the VerticalLayout returned by getContent() a fixed size, but in practice the content size won't be known.

@Route
public class LayoutTest extends VerticalLayout {

    public LayoutTest() {
        setPadding(false);
        setSpacing(false);
        setSizeFull();
        setDefaultHorizontalComponentAlignment(Alignment.STRETCH);
 
        Div header = new Div(new H2("Title"));
        header.setHeight("4em");
        header.getStyle().set("flex-shrink", "0");

        Div leftMenu = createLeftMenu();
        VerticalLayout content = createContentArea();
        HorizontalLayout layout = new HorizontalLayout(leftMenu, content);
        layout.setPadding(false);
        layout.setSpacing(false);
        layout.setHeight("100px");               
        layout.expand(content);
        expand(layout);
        add(header);
        add(layout);
    }

    private HorizontalLayout createTopMenu() {
        HorizontalLayout menu = new HorizontalLayout(new Button("Menu 1"), new Button("Menu 2"), new Button("Menu 3"));
        menu.setPadding(false);
        menu.getStyle().set("background", "#fafafa");
        return menu;
    }

    private Div createLeftMenu() {
        Div navigation = new Div();
        navigation.setText("Left menu");
        navigation.setWidth("20em");
        navigation.getStyle().set("background", "#fafafa");
        navigation.getStyle().set("flex-shrink", "0");
        return navigation;
    }

    private VerticalLayout createContentArea() {
        VerticalLayout layout = new VerticalLayout();
        layout.setDefaultHorizontalComponentAlignment(Alignment.STRETCH);
        layout.setPadding(false);

        layout.add(createTopMenu());
        H3 subtitle = new H3("Content");
        subtitle.getStyle().set("flex-shrink", "0");
        layout.add(subtitle);

        VerticalLayout content = createContent();
//        Giving the content a fixed size works, but is not desired
//        content.setHeight("10000px");
//        content.setWidth("1000px");
        Div scroller = new Div(content);
        scroller.getStyle().set("overflow", "auto");
        scroller.getStyle().set("border-style", "solid");
        layout.add(scroller);

        HorizontalLayout buttons = new HorizontalLayout(new Button("Button 1"), new Button("Button 2"), new Button("Button 3"));
        buttons.getStyle().set("flex-shrink", "0");
        layout.add(buttons);
        layout.expand(scroller);
        return layout;
    }

    private VerticalLayout createContent() {
        VerticalLayout content = new VerticalLayout();
        for (int i = 0; i < 100; ++i) {
            Div div = new Div(new Span("data" + i));
            div.setWidth("2000px");
            div.setHeight("20px");
            div.getStyle().set("background", "#cccccc");
            content.add(div);
        }
        return content;
    }
}

Upvotes: 1

Views: 559

Answers (1)

tanderson
tanderson

Reputation: 1219

I found a solution by adapting this answer to Make a div fill the height of the remaining screen space

It uses divs with display:table, display:table-row and display:table-cell.

The relevant change is to the createContentArea() method. I would be interested to know if there is a higher-level solution.

This works on Firefox, Chrome and Edge.

@Route
public class LayoutTest2 extends VerticalLayout {

    public LayoutTest2() {
        setPadding(false);
        setSpacing(false);
        setSizeFull();
        setDefaultHorizontalComponentAlignment(Alignment.STRETCH);

        Div header = new Div(new H2("Title"));
        header.setHeight("4em");
        header.getStyle().set("flex-shrink", "0");

        Div leftMenu = createLeftMenu();
        Div content = createContentArea();
        HorizontalLayout layout = new HorizontalLayout(leftMenu, content);
        layout.setPadding(false);
        layout.setSpacing(false);
        layout.setHeight("100px");
        layout.expand(content);
        expand(layout);
        add(header);
        add(layout);
    }

    private HorizontalLayout createTopMenu() {
        HorizontalLayout menu = new HorizontalLayout(new Button("Menu 1"), new Button("Menu 2"), new Button("Menu 3"));
        menu.setPadding(false);
        menu.getStyle().set("background", "#fafafa");
        return menu;
    }

    private Div createLeftMenu() {
        Div navigation = new Div();
        navigation.setText("Left menu");
        navigation.setWidth("20em");
        navigation.getStyle().set("background", "#fafafa");
        navigation.getStyle().set("flex-shrink", "0");
        return navigation;
    }

    private Div createContentArea() {
        Div table = new Div();
        table.getStyle().set("display", "table")
                .set("width", "100%")
                .set("height", "100%");
        Div topMenu = new Div(createTopMenu());
        topMenu.getStyle().set("display", "table-row");
        table.add(topMenu);

        Div subtitle = new Div(new H3("Content"));
        subtitle.getStyle().set("display", "table-row");
        table.add(subtitle);

        Div tableRowBody = new Div();
        tableRowBody.getStyle().set("display", "table-row")
                .set("height", "100%");
        Div outerWrapper = new Div();
        outerWrapper.getStyle().set("display", "table-cell")
                .set("height", "100%");
        tableRowBody.add(outerWrapper);

        Div innerWrapper = new Div();
        innerWrapper.getStyle().set("height", "100%")
                .set("position", "relative")
                .set("overflow", "auto")
                .set("border", "solid");
        outerWrapper.add(innerWrapper);

        Div bodyContent = new Div(createContent());
        bodyContent.getStyle().set("position", "absolute")
                .set("top", "0")
                .set("bottom", "0")
                .set("left", "0")
                .set("right", "0");
        innerWrapper.add(bodyContent);
        table.add(tableRowBody);

        HorizontalLayout buttons = new HorizontalLayout(new Button("Button 1"), new Button("Button 2"), new Button("Button 3"));
        table.add(buttons);
        return table;
    }

    private VerticalLayout createContent() {
        VerticalLayout content = new VerticalLayout();
        for (int i = 0; i < 100; ++i) {
            Div div = new Div(new Span("data" + i));
            div.setWidth("2000px");
            div.setHeight("20px");
            div.getStyle().set("background", "#cccccc");
            content.add(div);
        }
        return content;
    }
}

Upvotes: 1

Related Questions