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

io.imunity.vaadin.elements.LocalizedTextFieldDetails Maven / Gradle / Ivy

/*
 * Copyright (c) 2021 Bixbit - Krzysztof Benedyczak. All rights reserved.
 * See LICENCE.txt file for licensing information.
 */

package io.imunity.vaadin.elements;

import static io.imunity.vaadin.elements.CSSVars.BASE_MARGIN;
import static io.imunity.vaadin.elements.CssClassNames.DETAILS_ICON;
import static io.imunity.vaadin.elements.CssClassNames.EMPTY_DETAILS_ICON;
import static io.imunity.vaadin.elements.CssClassNames.SMALL_GAP;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.BiConsumer;

import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.component.customfield.CustomField;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.HasValidator;
import com.vaadin.flow.data.binder.ValidationResult;
import com.vaadin.flow.data.binder.Validator;

public class LocalizedTextFieldDetails extends CustomField> implements HasValidator>
{
	private final Map fields = new LinkedHashMap<>();
	private final Binder> binder = new Binder<>();
	private final LocalizedTextField defaultField;
	private HorizontalLayout summary;
	private Validator validator = (val, context) -> ValidationResult.ok();

	public LocalizedTextFieldDetails(Collection enabledLocales, Locale currentLocale)
	{
		this(enabledLocales, currentLocale, null);
	}

	public LocalizedTextFieldDetails(Collection enabledLocales, Locale currentLocale, String label)
	{
		VerticalLayout content = new VerticalLayout();
		content.setVisible(false);
		content.setPadding(false);
		content.setSpacing(false);

		Icon angleDown = crateIcon(VaadinIcon.ANGLE_DOWN, label);
		angleDown.getStyle().set("margin-top", BASE_MARGIN.value());
		Icon angleUp = crateIcon(VaadinIcon.ANGLE_UP, label);
		angleUp.getStyle().set("margin-top", BASE_MARGIN.value());

		angleUp.setVisible(false);
		angleDown.addClickListener(event ->
		{
			angleDown.setVisible(false);
			angleUp.setVisible(true);
			content.setVisible(true);
		});
		angleUp.addClickListener(event ->
		{
			angleDown.setVisible(true);
			angleUp.setVisible(false);
			content.setVisible(false);
		});

		defaultField = new LocalizedTextField(currentLocale);
		defaultField.setLabel(label);
		fields.put(currentLocale, defaultField);

		summary = new HorizontalLayout(defaultField, angleDown, angleUp);
		summary.setWidthFull();
		summary.setClassName(SMALL_GAP.getName());

		enabledLocales.stream()
				.filter(locale -> !currentLocale.equals(locale))
				.forEach(locale ->
				{
					LocalizedTextField localizedTextField = new LocalizedTextField(locale);
					content.add(localizedTextField);
					fields.put(locale, localizedTextField);
				});
		fields.forEach((locale, field) ->
				binder.forField(field)
				.withValidator((val, context) -> validator.apply(val, context))
				.bind(map -> map.get(locale), (map, val) -> map.put(locale, val))
		);

		setValue(new LinkedHashMap<>());
		add(summary, content);
		propagateValueChangeEventFromNestedTextFieldToThisComponent();
	}

	private void propagateValueChangeEventFromNestedTextFieldToThisComponent()
	{
		fields.values().forEach(localizedTextField -> localizedTextField.addValueChangeListener(event ->
				fireEvent(new ComponentValueChangeEvent<>(this, event.getHasValue(), event.getOldValue(), event.isFromClient()))
		));
	}

	public void addValuesChangeListener(BiConsumer, Integer> consumer)
	{
		fields.values().forEach(field -> field.getElement().addEventListener(
				"keyup",
				e -> field.getElement().executeJs("return this.inputElement.selectionStart")
						.then(Integer.class, pos -> consumer.accept(field, pos)))
		);

		fields.values().forEach(field -> field.addFocusListener(
				e -> field.getElement().executeJs("return this.inputElement.selectionStart")
						.then(Integer.class, pos -> consumer.accept(field, pos)))
		);
	}

	public void setValidator(Validator validator)
	{
		this.validator = validator;
	}

	public Collection getSlottedFields()
	{
		return fields.values();
	}

	private Icon crateIcon(VaadinIcon angleDown, String label)
	{
		Icon icon = angleDown.create();
		icon.addClassName(DETAILS_ICON.getName());
		if(label != null)
			icon.setClassName(EMPTY_DETAILS_ICON.getName());
		return icon;
	}

	@Override
	public void setWidthFull()
	{
		getElement().getStyle().setWidth("100%");
		fields.values().forEach(HasSize::setWidthFull);
	}

	@Override
	public void setWidth(String width)
	{
		fields.values().forEach(f -> f.setWidth(width));
	}

	@Override
	public void focus()
	{
		fields.values().iterator().next().focus();
	}

	@Override
	public void setValue(Map value)
	{
		binder.setBean(new LinkedHashMap<>(value));
	}

	@Override
	public Map getValue()
	{
		return binder.getBean();
	}

	@Override
	public void setReadOnly(boolean readOnly)
	{
		VaadinElementReadOnlySetter.setReadOnly(getElement(), readOnly);
		fields.values().forEach(field -> field.setReadOnly(readOnly));
	}

	@Override
	protected Map generateModelValue()
	{
		return getValue();
	}

	@Override
	protected void setPresentationValue(Map newPresentationValue)
	{
		setValue(newPresentationValue);
	}

	@Override
	public Validator> getDefaultValidator()
	{
		return (value, context) ->
		{
			if (binder.isValid()) {
				return ValidationResult.ok();
			} else {
				return ValidationResult.error("");
			}
		};
	}

	@Override
	public void setLabel(String label)
	{
		defaultField.setLabel(label);
	}

	@Override
	public String getLabel()
	{
		return defaultField.getLabel();
	}

	public void setPlaceholder(String message)
	{
		defaultField.setPlaceholder(message);	
	}
	
	public void centerIcons()
	{
		summary.setAlignItems(Alignment.BASELINE);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy