package com.vaadin.demo.component.scroller;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import java.time.LocalDate;
@Route("scroller-basic")
public class ScrollerBasic extends VerticalLayout {
public static final String PERSONAL_TITLE_ID = "personal-title";
public static final String EMPLOYMENT_TITLE_ID = "employment-title";
public ScrollerBasic() {
setAlignItems(Alignment.STRETCH);
setHeight("400px");
setMaxWidth("100%");
setPadding(false);
setSpacing(false);
setWidth("360px");
getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
// Header
Header header = new Header();
header.getStyle().set("align-items", "center")
.set("border-bottom", "1px solid var(--lumo-contrast-20pct)")
.set("display", "flex").set("padding", "var(--lumo-space-m)");
H2 editEmployee = new H2("Edit employee");
editEmployee.getStyle().set("margin", "0");
Icon arrowLeft = VaadinIcon.ARROW_LEFT.create();
arrowLeft.setSize("var(--lumo-icon-size-m)");
arrowLeft.getElement().setAttribute("aria-hidden", "true");
arrowLeft.getStyle().set("box-sizing", "border-box")
.set("margin-right", "var(--lumo-space-m)")
.set("padding", "calc(var(--lumo-space-xs) / 2)");
Anchor goBack = new Anchor("#", arrowLeft);
header.add(goBack, editEmployee);
add(header);
// tag::snippet[]
// Personal information
H3 personalTitle = new H3("Personal information");
personalTitle.setId(PERSONAL_TITLE_ID);
TextField firstName = new TextField("First name");
firstName.setWidthFull();
TextField lastName = new TextField("Last name");
lastName.setWidthFull();
DatePicker birthDate = new DatePicker("Birthdate");
birthDate.setInitialPosition(LocalDate.of(1990, 1, 1));
birthDate.setWidthFull();
Section personalInformation = new Section(personalTitle, firstName,
lastName, birthDate);
personalInformation.getElement().setAttribute("aria-labelledby",
PERSONAL_TITLE_ID);
// Employment information
H3 employmentTitle = new H3("Employment information");
employmentTitle.setId(EMPLOYMENT_TITLE_ID);
TextField position = new TextField("Position");
position.setWidthFull();
TextArea additionalInformation = new TextArea("Additional Information");
additionalInformation.setWidthFull();
Section employmentInformation = new Section(employmentTitle, position,
additionalInformation);
employmentInformation.getElement().setAttribute("aria-labelledby",
EMPLOYMENT_TITLE_ID);
// NOTE
// We are using inline styles here to keep the example simple.
// We recommend placing CSS in a separate style sheet and to
// encapsulating the styling in a new component.
Scroller scroller = new Scroller(
new Div(personalInformation, employmentInformation));
scroller.setScrollDirection(Scroller.ScrollDirection.VERTICAL);
scroller.getStyle()
.set("border-bottom", "1px solid var(--lumo-contrast-20pct)")
.set("padding", "var(--lumo-space-m)");
add(scroller);
// end::snippet[]
// Footer
Button save = new Button("Save");
save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
save.getStyle().set("margin-right", "var(--lumo-space-s)");
Button reset = new Button("Reset");
reset.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
Footer footer = new Footer(save, reset);
footer.getStyle().set("padding", "var(--lumo-space-wide-m)");
add(footer);
}
}
Scroll Direction
Scroller has four different scroll directions: vertical, horizontal, both, and none.
Scroller’s default scroll direction is both.
Vertical
When the scroll position is vertical, the user can scroll vertically if the content overflows the container vertically.
Content that overflows horizontally is clipped and inaccessible, so the width of the content should be 100%.
Horizontal
When the scroll position is horizontal, the user can scroll horizontally if the content overflows the container horizontally.
Content that overflows vertically is clipped and inaccessible, so the height of the content should be 100%.
Note
Use horizontal scrolling with caution, as it’s much less common and may be difficult for users to recognize and use, in particular on non-mobile devices.
Desktop
Excluding Grids, horizontal scrolling isn’t commonly used in desktop and/or business applications, as it can be non-obvious and cumbersome to use.
It’s recommended to use Buttons to help users notice and navigate horizontally scrollable sections.
For horizontally scrollable lists, it’s considered good practice to display the number of items there are in the list, and which items the user is currently viewing.
Mobile
Scrolling horizontally or swiping is more common on mobile, for example for navigation purposes.
It can also be used to conserve vertical space, for example in situations where the user is exploring less-important information, such as shortcuts or images.
When the scroll position is Both (default), the user can scroll vertically and horizontally if the content overflows in both directions.
This scroll direction is best suited to allowing the user to pan around large elements, such as images.
It can also be used as a fallback for a responsive layout that can’t be guaranteed not to overflow in some situations.
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@vaadin/scroller';
import img from '../../../../src/main/resources/images/reindeer.jpg?url';
import { applyTheme } from 'Frontend/generated/theme';
@customElement('scroller-both')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
// Apply custom theme (only supported if your app uses one)
applyTheme(root);
return root;
}
protected override render() {
return html`
<!-- tag::snippet[] -->
<vaadin-scroller style="height: 300px; width: 100%;">
<img src="${img}" alt="A reindeer walking on a snowy lake shore at dusk" />
</vaadin-scroller>
<!-- end::snippet[] -->
`;
}
}
ScrollerBoth.java
package com.vaadin.demo.component.scroller;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
@Route("scroller-both")
public class ScrollerBoth extends Div {
public ScrollerBoth() {
// tag::snippet[]
Scroller scroller = new Scroller();
scroller.setWidthFull();
scroller.setHeight("300px");
StreamResource imageResource = new StreamResource("reindeer+.jpg",
() -> getClass().getResourceAsStream("/images/reindeer.jpg"));
Image img = new Image(imageResource,
"A reindeer walking on a snowy lake shore at dusk");
scroller.setContent(img);
add(scroller);
// end::snippet[]
}
}
None
Use None to hide content that overflows in either direction.
No scrollbars are available to the user to access the clipped content.
None can be used in fixed-size/fixed-layout situations, where overflow would cause issues.