All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fi.evolver.ai.vaadin.component.form.StarRatingComponent Maven / Gradle / Ivy

There is a newer version: 1.5.5
Show 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();
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy