diff --git a/documentation/components/tutorial-flow-grid.asciidoc b/documentation/components/tutorial-flow-grid.asciidoc index 63a81239d..9b9a6bcf7 100644 --- a/documentation/components/tutorial-flow-grid.asciidoc +++ b/documentation/components/tutorial-flow-grid.asciidoc @@ -782,3 +782,169 @@ grid.addSortListener(event -> { currentSortOrder, event.isFromClient())); }); ---- + +== Styling + +Styling a Grid - or any other Flow Component - is straightforward, but requires some knowledge of web components and +shadow-dom. Styling depends on the components position in the DOM. If component is in shadow dom, styling needs to be +done within that component or by variables. If the component is not in the shadow dom, then it's in the "normal" DOM +and all normal css applies. Also, many Vaadin components support theme -attribute, which allows to quickly customize the +component. + +Example grid: + +[source, java] +---- +Grid grid = new Grid<>(); +grid.setItems(Celebrity.getPeople()); +grid.addClassName("styled"); +grid.addColumn(new ComponentRenderer<>(person -> { + TextField textField = new TextField(); + textField.setValue(person.getName()); + textField.addClassName("style-" + person.getGender()); + textField.addValueChangeListener(event -> + person.setName(event.getValue())); + return textField; +})).setHeader("Name"); + +grid.addColumn(new ComponentRenderer<>(person -> { + DatePicker datePicker = new DatePicker(); + datePicker.setValue(person.getDob()); + datePicker.addValueChangeListener(event -> { + person.setDob(event.getValue()); + }); + datePicker.addClassName("style-" + person.getGender()); + return datePicker; +})).setHeader("DOB"); + +grid.addColumn(new ComponentRenderer<>(person -> { + Image image = new Image(person.getImgUrl(), person.getName()); + return image; +})).setHeader("Image"); + +---- + +=== Styling with theme property + +Vaadin components have different Lumo Theme variations usable. Using those with Flow is simple: just give needed +variation, one or more. + +[source, java] +---- +grid.addThemeNames("no-border", "no-row-borders", "row-stripes"); +---- + + +=== Styling with css + +The Grid itself is in the shadow-dom, but actual values (cells) have slots which are in the light dom, so normal css +styling applies. + +For example, lets set max size for images within grid: + +[source, css] +---- +vaadin-grid vaadin-grid-cell-content img { + max-height: 4em; +} +---- + +_vaadin-grid-cell-content_ is the slot that's in the light dom, so selector _vaadin-grid vaadin-grid-cell-content_ +points to grids cells. + +And we could of course set some fancier grid - a grid with "styled" -class - which would have rounded images, +positioned in the center and also capped with max size (size comes with previous css): + +[source, css] +---- +vaadin-grid.styled vaadin-grid-cell-content img { + border-radius: 2em; + margin-left: 50%; + transform: translate(-50%); +} +---- + +=== Styling with overriding component styles + +Styling of the grid itself can be done within custom styles by overriding grid-styling: + +[source, html] +---- + + + +---- + +The _theme-for="vaadin-grid"_ indicates that it's overriding vaadin-grid -components styling. In the the css the +_:host(.styled)_ is a selector for vaadin-grid that has "styled" as class. So outside of shadow dom that would be +_vaadin-grid.styled_, but the shadow dom is boxed in it's own dom (ergo, the shadow dom) it must be selected with +:host([selector]). + +In this example we're setting special styles for vaadin-grid with "styled" class. A grid without "styled" class remains +normal. + +=== Styling with variables + +Although the shadow-dom is boxed and cannot be affected outside, there's one method to pass info to the shadow-dow: +CSS variables. + +CSS variables will pass through all levels of dom, whether it's light or shadow dom. So, if variable is set, then that +value is available everywhere under that dom, regardless of shadow / light. + +In order for variables to work, they need to be supported by component. So with given grid example, let's make our +styled grid have gender-wise colorization of text-fields. + +First, lets introduce variable usage for wanted component (textfield): + +[source, html] +---- + + + +---- + +This is overriding vaadin-text-field styles. The only change made is that, if there's a variable +_--custom-text-field-bg_ available, use it, otherwise fall back to normal. + +Now, changing the variable, based on our person gender + +[source, css] +---- +.styled .style-female { + --custom-text-field-bg: #ff99cc; +} +.styled .style-male { + --custom-text-field-bg: #99ccff; +} +---- + +After this change, the text-field - anywhere - under the _.styled .style-female/male_ uses special background color. + +This does not only apply in the text-fields of the grid, but - because date-field uses the text-field internally - also +the date-fields. + +Now the look and feel of used component - the textfield - is always the same, whether it's directly +used or wrapped within another component. diff --git a/documentation/pom.xml b/documentation/pom.xml index 6f7753c92..d83cede5e 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -99,7 +99,7 @@ com.vaadin vaadin-grid-flow - 1.1-SNAPSHOT + 1.2-SNAPSHOT com.vaadin diff --git a/documentation/src/main/css/GridTheming.css b/documentation/src/main/css/GridTheming.css new file mode 100644 index 000000000..b249026cb --- /dev/null +++ b/documentation/src/main/css/GridTheming.css @@ -0,0 +1,20 @@ +tutorial::components/tutorial-flow-grid.asciidoc + +vaadin-grid vaadin-grid-cell-content img { + max-height: 4em; +} + + +vaadin-grid.styled vaadin-grid-cell-content img { + border-radius: 2em; + margin-left: 50%; + transform: translate(-50%); +} + +.styled .style-female { + --custom-text-field-bg: #ff99cc; +} +.styled .style-male { + --custom-text-field-bg: #99ccff; +} + diff --git a/documentation/src/main/html/GridStyles.html b/documentation/src/main/html/GridStyles.html new file mode 100644 index 000000000..e3451e710 --- /dev/null +++ b/documentation/src/main/html/GridStyles.html @@ -0,0 +1,34 @@ +tutorial::components/tutorial-flow-grid.asciidoc + + + + + + + + + + diff --git a/documentation/src/main/java/com/vaadin/flow/tutorial/components/GridBasic.java b/documentation/src/main/java/com/vaadin/flow/tutorial/components/GridBasic.java index f274bcd3c..0c279d5be 100644 --- a/documentation/src/main/java/com/vaadin/flow/tutorial/components/GridBasic.java +++ b/documentation/src/main/java/com/vaadin/flow/tutorial/components/GridBasic.java @@ -15,21 +15,27 @@ */ package com.vaadin.flow.tutorial.components; +import java.time.LocalDate; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.datepicker.DatePicker; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid.Column; import com.vaadin.flow.component.grid.Grid.SelectionMode; import com.vaadin.flow.component.grid.GridMultiSelectionModel; import com.vaadin.flow.component.grid.GridSingleSelectionModel; import com.vaadin.flow.component.grid.HeaderRow; +import com.vaadin.flow.component.html.Image; import com.vaadin.flow.component.html.Label; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.provider.QuerySortOrder; +import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.data.renderer.TemplateRenderer; import com.vaadin.flow.function.ValueProvider; import com.vaadin.flow.tutorial.annotations.CodeFor; @@ -230,6 +236,109 @@ public void gridSorting() { column.isSortable(); } + public void gridTheming() { + Grid grid = new Grid<>(); + grid.setItems(Celebrity.getPeople()); + grid.addClassName("styled"); + grid.addColumn(new ComponentRenderer<>(person -> { + TextField textField = new TextField(); + textField.setValue(person.getName()); + textField.addClassName("style-" + person.getGender()); + textField.addValueChangeListener(event -> + person.setName(event.getValue())); + return textField; + })).setHeader("Name"); + + grid.addColumn(new ComponentRenderer<>(person -> { + DatePicker datePicker = new DatePicker(); + datePicker.setValue(person.getDob()); + datePicker.addValueChangeListener(event -> { + person.setDob(event.getValue()); + }); + datePicker.addClassName("style-" + person.getGender()); + return datePicker; + })).setHeader("DOB"); + + grid.addColumn(new ComponentRenderer<>(person -> { + Image image = new Image(person.getImgUrl(), person.getName()); + return image; + })).setHeader("Image"); + + grid.addThemeNames("no-border", "no-row-borders", "row-stripes"); + + } + + // Bean class for gridTheming + public static class Celebrity { + enum Gender { + MALE("male"), + FEMALE("female"); + + private String value; + + Gender(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override public String toString() { + return value; + } + } + + private Gender gender; + private String name; + private LocalDate dob; + + public Celebrity(Gender gender, String firstName, String lastName, LocalDate dob) { + this.gender = gender; + this.name = firstName + " " + lastName; + this.dob = dob; + } + + public static List getPeople() { + ArrayList list = new ArrayList<>(); + list.add(new Celebrity(Gender.FEMALE, "Aretha", "Franklin", LocalDate.of(1942,3,25))); + list.add(new Celebrity(Gender.MALE, "Alan", "Moore", LocalDate.of(1953,11,18))); + list.add(new Celebrity(Gender.MALE, "Freddie", "Mercury", LocalDate.of(1946,9,5))); + + return list; + } + + public Gender getGender() { + return gender; + } + + public void setGender(Gender gender) { + this.gender = gender; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDate getDob() { + return dob; + } + + public void setDob(LocalDate dob) { + this.dob = dob; + } + + public String getImgUrl() { + return "/img/" + name.toLowerCase().replaceAll(" ","") + ".jpg"; + } + + } + + //@formatter:off /* * code of commented lines