fi.evolver.ai.vaadin.component.form.StarRatingComponent Maven / Gradle / Ivy
The newest version!
package fi.evolver.ai.vaadin.component.form;
import java.io.Serial;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import org.vaadin.lineawesome.LineAwesomeIcon;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.theme.lumo.LumoUtility;
import fi.evolver.ai.vaadin.component.i18n.VaadinTranslations;
import fi.evolver.ai.vaadin.entity.HasValueGetterSetter;
public class StarRatingComponent extends HorizontalLayout implements HasValueGetterSetter {
@Serial
private static final long serialVersionUID = 1L;
private final List stars = new ArrayList<>();
private Integer value;
private Optional> valueChangeListener = Optional.empty();
private Boolean enabled;
private final VaadinTranslations t;
public StarRatingComponent(Integer value, Integer numStars, Boolean enabled, VaadinTranslations t) {
super();
this.value = value;
this.enabled = enabled;
this.t = t;
initStars(numStars);
setSpacing(false);
add(stars.stream().map(s -> (Component) s).toList());
getElement().executeJs("""
const children = this.children
for (let i = 0; i < children.length; i++) {
children[i].addEventListener("mouseover", _ => {
for (let k = 0; k <= i; k++)
children[k].style = 'color: yellow'
})
children[i].addEventListener("mouseout", _ => {
for (let k = 0; k <= i; k++)
children[k].style = ''
})
}
""");
}
public StarRatingComponent(Integer value, Integer numStars, VaadinTranslations t) {
this(value, numStars, true, t);
}
public StarRatingComponent(Integer value, VaadinTranslations t) {
this(value, 5, t);
}
public StarRatingComponent(VaadinTranslations t) {
this(null, t);
}
@Override
public Integer getValue() {
return value;
}
@Override
public void setValue(Integer value) {
this.value = value;
updateStarIcons();
}
public void addValueChangeListener(Consumer listener) {
valueChangeListener = Optional.of(listener);
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
updateStarIcons();
}
private void initStars(Integer numStars) {
for (int i = 0; i < numStars; i++) {
RatingIconContainer star = new RatingIconContainer(i + 1, value != null && value > i,
this::handleStarClicked,
t);
stars.add(star);
}
}
private void handleStarClicked(Integer starValue) {
if (value != null && value == starValue)
setValue(null);
else
setValue(starValue);
valueChangeListener.ifPresent(l -> l.accept(value));
}
private void updateStarIcons() {
for (int i = 0; i < stars.size(); i++) {
RatingIconContainer star = stars.get(i);
star.setFilled(value != null && value > i);
star.setEnabled(enabled);
}
}
private static class RatingIconContainer extends Div {
@Serial
private static final long serialVersionUID = 1L;
private final int value;
private boolean isFilled;
private Component icon;
public RatingIconContainer(int value, Boolean isFilled, Consumer onClick, VaadinTranslations t) {
this.value = value;
this.isFilled = isFilled;
this.icon = getIcon();
add(icon);
addClassName(LumoUtility.Display.FLEX);
super.addClickListener(e -> onClick.accept(this.value));
setTitle(t.getTranslation("component.starRating.label"));
}
public void setFilled(Boolean value) {
if (isFilled == value)
return;
isFilled = value;
remove(icon);
icon = getIcon();
add(icon);
}
private Component getIcon() {
return isFilled ? LineAwesomeIcon.STAR_SOLID.create() : LineAwesomeIcon.STAR.create();
}
}
}