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

org.dominokit.domino.ui.forms.ValueBox Maven / Gradle / Ivy

There is a newer version: 1.0.139
Show newest version
package org.dominokit.domino.ui.forms;

import elemental2.dom.HTMLDivElement;
import elemental2.dom.HTMLElement;
import elemental2.dom.HTMLLabelElement;
import elemental2.dom.Node;
import org.dominokit.domino.ui.grid.flex.FlexItem;
import org.dominokit.domino.ui.grid.flex.FlexLayout;
import org.dominokit.domino.ui.style.Color;
import org.dominokit.domino.ui.utils.*;
import org.jboss.elemento.IsElement;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static org.jboss.elemento.Elements.label;
import static org.jboss.elemento.Elements.span;

public abstract class ValueBox, E extends HTMLElement, V> extends BasicFormElement implements
        Focusable, HasPlaceHolder, IsReadOnly, HasChangeHandlers {

    public static final String FOCUSED = "focused";
    public static final String FLOATING = "floating";
    public static final String DISABLED = "disabled";

    private DominoElement inputElement;

    protected DominoElement fieldGroup = DominoElement.div();
    protected DominoElement fieldContainer = DominoElement.div();
    protected FlexItem inputContainer = FlexItem.create();
    private DominoElement notesContainer = DominoElement.div();

    private FlexLayout leftAddOnsContainer = FlexLayout.create();
    private FlexLayout rightAddOnsContainer = FlexLayout.create();

    private FlexItem helpItem;
    private FlexItem countItem;
    private FlexItem errorItem;

    private FlexItem prefixItem = FlexItem.create();
    private FlexItem postFixItem = FlexItem.create();

    private DominoElement labelElement;
    private DominoElement labelTextElement = DominoElement.of(span());

    private Color focusColor = Color.BLUE;
    private String placeholder;
    private AutoValidator autoValidator;
    protected List> changeHandlers = new ArrayList<>();
    private final List> onClearHandlers = new ArrayList<>();

    private boolean pauseChangeHandlers = false;
    private boolean valid = true;
    private boolean floating;
    private boolean readOnly;
    private String prefix;
    private String postfix;
    private FlexItem mandatoryAddOn;
    private boolean validateOnFocusLost = true;
    private FieldStyle fieldStyle = DominoFields.INSTANCE.getDefaultFieldsStyle();
    private FlexLayout fieldInnerContainer;
    private boolean permaFloating = false;
    private FlexLayout additionalInfoContainer;

    public ValueBox(String type, String label) {
        helpItem = FlexItem.create();
        countItem = FlexItem.create().hide();
        errorItem = FlexItem.create();
        labelElement = createLabelElement();

        init((T) this);
        inputElement = DominoElement.of(createInputElement(type));
        inputElement.addEventListener("change", evt -> {
                    callChangeHandlers();
                }
        );
        inputElement.addEventListener("input", evt -> {
                    if (isEmpty()) {
                        showPlaceholder();
                    }
                }
        );

        layout();
        setFocusColor(focusColor);
        addFocusListeners();
        setLabel(label);
        setSpellCheck(true);
        fieldStyle.apply(this);
    }

    public FieldStyle getFieldStyle() {
        return fieldStyle;
    }

    public T setFieldStyle(FieldStyle fieldStyle) {
        if (nonNull(fieldStyle)) {
            fieldStyle.apply(this);
            this.fieldStyle = fieldStyle;
        }

        return (T) this;
    }

    private void layout() {
        fieldGroup.css("field-group");
        fieldContainer
                .css("field-cntr");
        notesContainer
                .css("notes-cntr");

        leftAddOnsContainer
                .css("field-lft-addons");
        rightAddOnsContainer
                .css("field-rgt-addons");

        prefixItem.css("field-prefix");
        postFixItem.css("field-postfix");

        linkLabelToField();

        fieldInnerContainer = FlexLayout.create();
        additionalInfoContainer = FlexLayout.create();
        fieldGroup
                .appendChild(fieldContainer
                        .appendChild(fieldInnerContainer
                                .appendChild(inputContainer
                                        .css("field-input-cntr")
                                        .setFlexGrow(1)
                                        .appendChild(labelElement)
                                        .appendChild(inputElement)
                                )
                                .apply(self -> {
                                    mandatoryAddOn = createMandatoryAddOn();
                                    if (nonNull(mandatoryAddOn)) {
                                        self.appendChild(mandatoryAddOn.css("field-mandatory-addon"));
                                    }
                                })
                        )
                )
                .appendChild(notesContainer
                        .css("field-note")
                        .appendChild(additionalInfoContainer
                                .appendChild(helpItem
                                        .css("field-helper")
                                        .setFlexGrow(1)
                                )
                                .appendChild(errorItem
                                        .hide()
                                        .css("field-errors")
                                        .setFlexGrow(1)
                                )
                                .appendChild(countItem
                                        .css("field-counter")
                                )
                        )
                );
    }

    @Override
    public T setFixErrorsPosition(boolean fixPosition) {
        if (fixPosition) {
            errorItem.show();
            errorItem.style().setMinHeight("25px");
        } else {
            errorItem.style().setMinHeight("0px");
        }
        return super.setFixErrorsPosition(fixPosition);
    }

    protected void linkLabelToField() {
        if (!inputElement.hasAttribute("id")) {
            inputElement.setAttribute("id", inputElement.getAttribute(BaseDominoElement.DOMINO_UUID));
        }
        labelElement.setAttribute("for", inputElement.getAttribute("id"));
    }

    protected void callChangeHandlers() {
        if (!pauseChangeHandlers) {
            changeHandlers.forEach(changeHandler -> changeHandler.onValueChanged(getValue()));
        }
    }

    protected DominoElement createLabelElement() {
        return DominoElement.of(label().css("field-label"));
    }

    protected abstract E createInputElement(String type);

    private void addFocusListeners() {
        inputElement.addEventListener("focus", evt -> doFocus());
        inputElement.addEventListener("focusout", evt -> {
            doUnfocus();
            if (isAutoValidation() && validateOnFocusLost) {
                validate();
            }
        });
        labelElement.addEventListener("click", evt -> {
            if (!isDisabled()) {
                focus();
            } else {
                evt.stopPropagation();
                evt.preventDefault();
            }
        });
    }

    public T setFloating(boolean floating) {
        if (floating) {
            floating();
        } else {
            nonfloating();
        }
        return (T) this;
    }

    public T floating() {
        floatLabel();
        this.permaFloating = true;
        showPlaceholder();
        return (T) this;
    }

    public T nonfloating() {
        unfloatLabel();
        this.permaFloating = false;
        hidePlaceholder();
        return (T) this;
    }

    public boolean isFloating() {
        return floating;
    }

    public FlexItem getMandatoryAddOn() {
        return mandatoryAddOn;
    }

    @Override
    public T focus() {
        if (!isDisabled()) {
            if (!isAttached()) {
                ElementUtil.onAttach(getInputElement(), mutationRecord -> {
                    tryFocus();
                });
            } else {
                tryFocus();
            }
        }
        return (T) this;
    }

    private void tryFocus() {
        getInputElement().element().focus();
        doFocus();
    }

    @Override
    public T unfocus() {
        if (!isAttached()) {
            ElementUtil.onAttach(getInputElement(), mutationRecord -> {
                getInputElement().element().blur();
                doUnfocus();
            });
        } else {
            getInputElement().element().blur();
            doUnfocus();
        }
        return (T) this;
    }

    protected void doFocus() {
        if (!isDisabled()) {
            fieldGroup.style().add(FOCUSED);
            floatLabel();
            if (valid) {
                if (isAddFocusColor()) {
                    fieldContainer.style().add("fc-" + focusColor.getStyle());
                }
                setLabelColor(focusColor);
            }
            showPlaceholder();
        }
    }

    protected boolean isAddFocusColor() {
        return true;
    }

    protected void doUnfocus() {
        fieldGroup.style().remove(FOCUSED);
        fieldContainer.style().remove("fc-" + focusColor.getStyle(), FOCUSED);
        unfloatLabel();
        removeLabelColor(focusColor);
        hidePlaceholder();
    }

    @Override
    protected void updateLabel(String label) {
        if (isNull(labelTextElement)) {
            labelTextElement = DominoElement.of(span());
        }
        labelTextElement.remove();
        labelTextElement.setTextContent(label);
        getLabelElement().appendChild(labelTextElement);
    }

    @Override
    public DominoElement getLabelTextElement() {
        return labelTextElement;
    }

    public T hideLabelText() {
        this.labelTextElement.hide();
        return (T) this;
    }

    public T showLabelText() {
        this.labelTextElement.show();
        return (T) this;
    }

    public T setLabelTextVisible(boolean visible) {
        this.labelTextElement.toggleDisplay(visible);
        return (T) this;
    }

    @Override
    public boolean isFocused() {
        return fieldGroup.style().contains(FOCUSED);
    }

    private void setLabelColor(Color color) {
        labelElement.style().add(color.getStyle());
    }

    private void removeLabelColor(Color color) {
        labelElement.style().remove(color.getStyle());
    }

    @Override
    public T enable() {
        super.enable();
        fieldGroup.style().remove(DISABLED);
        return (T) this;
    }

    @Override
    public T disable() {
        super.disable();
        fieldGroup.style().add(DISABLED);
        return (T) this;
    }

    @Override
    public T setLabel(String label) {
        super.setLabel(label);
        hidePlaceholder();
        return (T) this;
    }

    @Override
    public T setPlaceholder(String placeholder) {
        this.placeholder = placeholder;
        showPlaceholder();
        return (T) this;
    }

    private void showPlaceholder() {
        if (placeholder != null && shouldShowPlaceholder()) {
            inputElement.setAttribute("placeholder", placeholder);
        }
    }

    private void hidePlaceholder() {
        if (placeholder != null && !shouldShowPlaceholder()) {
            inputElement.removeAttribute("placeholder");
        }
    }

    private boolean shouldShowPlaceholder() {
        return isEmpty() && floating;
    }

    @Override
    public String getPlaceholder() {
        return this.placeholder;
    }

    @Override
    public T setFocusColor(Color focusColor) {
        removeLabelColor(this.focusColor);
        if (isFocused()) {
            setLabelColor(focusColor);
        }
        this.focusColor = focusColor;
        return (T) this;
    }

    @Override
    public DominoElement getFieldInputContainer() {
        return DominoElement.of(inputContainer);
    }

    @Override
    public DominoElement getFieldContainer() {
        return DominoElement.of(fieldContainer);
    }

    public T addLeftAddOn(FlexItem addon) {
        leftAddOnsContainer.appendChild(addon);
        if (!leftAddOnsContainer.isAttached()) {
            fieldInnerContainer.insertFirst(leftAddOnsContainer);
        }
        return (T) this;
    }

    public T addLeftAddOn(IsElement addon) {
        return addLeftAddOn(FlexItem.create().appendChild(addon));
    }

    public T addLeftAddOn(Node addon) {
        return addLeftAddOn(FlexItem.create().appendChild(addon));
    }

    public T addRightAddOn(FlexItem rightAddon) {
        rightAddOnsContainer.appendChild(rightAddon);
        if (!rightAddOnsContainer.isAttached()) {
            fieldInnerContainer.appendChild(rightAddOnsContainer);
        }
        return (T) this;
    }

    public T addRightAddOn(IsElement addon) {
        addRightAddOn(FlexItem.create().appendChild(addon));
        return (T) this;
    }

    public T addRightAddOn(Node addon) {
        addRightAddOn(FlexItem.create().appendChild(addon));
        return (T) this;
    }

    public T removeRightAddOn(FlexItem addon) {
        return removeRightAddOn(addon.element());
    }

    public T removeRightAddOn(IsElement addon) {
        return removeRightAddOn(addon.element());
    }

    public T removeRightAddOn(Node addon) {
        return removeAddOn(rightAddOnsContainer, addon);
    }

    public T removeRightAddOn(int index) {
        if (index >= 0 && index < rightAddOnsContainer.childNodes().length) {
            return removeAddOn(rightAddOnsContainer, rightAddOnsContainer.childNodes().getAt(index));
        }
        return (T) this;
    }

    public T removeLeftAddOn(FlexItem addon) {
        return removeLeftAddOn(addon.element());
    }

    public T removeLeftAddOn(IsElement addon) {
        return removeLeftAddOn(addon.element());
    }

    public T removeLeftAddOn(Node addon) {
        return removeAddOn(leftAddOnsContainer, addon);
    }

    public T removeLeftAddOn(int index) {
        if (index >= 0 && index < leftAddOnsContainer.childNodes().length) {
            return removeAddOn(leftAddOnsContainer, leftAddOnsContainer.childNodes().getAt(index));
        }
        return (T) this;
    }

    private T removeAddOn(FlexLayout container, Node addon) {
        if (container.isAttached() && container.contains(addon)) {
            if (container.hasDirectChild(addon)) {
                container.removeChild(addon);
            } else {
                return removeAddOn(container, addon.parentNode);
            }
        }
        return (T) this;
    }

    public T removeRightAddOns() {
        rightAddOnsContainer.clearElement();
        return (T) this;
    }

    public T removeLeftAddOns() {
        leftAddOnsContainer.clearElement();
        return (T) this;
    }

    @Override
    public DominoElement getInputElement() {
        return DominoElement.of(inputElement);
    }

    @Override
    protected DominoElement getHelperContainer() {
        return DominoElement.of(helpItem.element());
    }

    @Override
    protected DominoElement getErrorsContainer() {
        return DominoElement.of(errorItem.element());
    }

    @Override
    public V getValue() {
        return null;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public String getStringValue() {
        return null;
    }

    @Override
    public DominoElement getLabelElement() {
        return DominoElement.of(labelElement);
    }

    @Override
    public HTMLElement element() {
        return fieldGroup.element();
    }

    @Override
    public T invalidate(String errorMessage) {
        this.valid = false;
        updateValidationStyles();
        return super.invalidate(errorMessage);
    }

    public T asTableField() {
        setTableField(true);
        return (T) this;
    }

    public T setTableField(boolean asTableField) {
        if (asTableField) {
            css("table-field");
        } else {
            removeCss("table-field");
        }
        return (T) this;
    }

    @Override
    public T invalidate(List errorMessages) {
        this.valid = false;
        updateValidationStyles();
        return super.invalidate(errorMessages);
    }

    private void updateValidationStyles() {
        fieldContainer.style().remove("fc-" + focusColor.getStyle());
        fieldContainer.style().add("fc-" + Color.RED.getStyle());
        removeLabelColor(focusColor);
        setLabelColor(Color.RED);
        changeLabelFloating();
    }

    public T pauseFocusValidation() {
        this.validateOnFocusLost = false;
        return (T) this;
    }

    public T resumeFocusValidation() {
        this.validateOnFocusLost = true;
        return (T) this;
    }

    @Override
    public T clearInvalid() {
        this.valid = true;
        fieldContainer.style().add("fc-" + focusColor.getStyle());
        fieldContainer.style().remove("fc-" + Color.RED.getStyle());
        removeLabelColor(Color.RED);
        if (isFocused()) {
            doFocus();
        } else {
            doUnfocus();
        }
        changeLabelFloating();
        return super.clearInvalid();
    }

    @Override
    public T setAutoValidation(boolean autoValidation) {
        if (autoValidation) {
            if (autoValidator == null) {
                autoValidator = createAutoValidator(this::validate);
            }
            autoValidator.attach();
        } else {
            if (nonNull(autoValidator)) {
                autoValidator.remove();
            }
            autoValidator = null;
        }
        return (T) this;
    }

    @Override
    public boolean isAutoValidation() {
        return nonNull(autoValidator);
    }

    protected abstract AutoValidator createAutoValidator(AutoValidate autoValidate);

    @Override
    public T clear() {
        clearValue();
        autoValidate();
        onClearHandlers.forEach(handler -> handler.accept((T) ValueBox.this));
        return (T) this;
    }

    @Override
    public T value(V value) {
        doSetValue(value);
        changeLabelFloating();
        autoValidate();
        callChangeHandlers();
        return (T) this;
    }

    @Override
    public T setReadOnly(boolean readOnly) {
        this.readOnly = readOnly;
        if (readOnly) {
            getInputElement().setAttribute(DISABLED, "true");
            getInputElement().setAttribute("readonly", "true");
            getInputElement().setAttribute(FLOATING, isFloating());
            css("readonly");
            floating();
        } else {
            getInputElement().removeAttribute(DISABLED);
            getInputElement().removeAttribute("readonly");
            removeCss("readonly");
            boolean floating;
            if (getInputElement().hasAttribute(FLOATING)) {
                floating = Boolean.parseBoolean(getInputElement().getAttribute(FLOATING));
            } else {
                floating = isFloating();
            }
            setFloating(floating);
            changeLabelFloating();
        }
        return (T) this;
    }

    @Override
    public boolean isReadOnly() {
        return readOnly;
    }

    protected void changeLabelFloating() {
        if (!isEmpty() || isFocused())
            floatLabel();
        else
            unfloatLabel();
    }

    protected void floatLabel() {
        if (!floating || permaFloating) {
            fieldGroup.style().add(FLOATING);
            this.floating = true;
        }
    }

    protected void unfloatLabel() {
        if ((floating && !permaFloating) && isEmpty()) {
            fieldGroup.style().remove(FLOATING);
            this.floating = false;
        }
    }

    protected void autoValidate() {
        if (isAutoValidation())
            validate();
    }

    @Override
    public T addChangeHandler(ChangeHandler changeHandler) {
        changeHandlers.add(changeHandler);
        return (T) this;
    }

    @Override
    public T removeChangeHandler(ChangeHandler changeHandler) {
        changeHandlers.remove(changeHandler);
        return (T) this;
    }

    public T setPauseChangeHandlers(boolean pauseChangeHandlers) {
        this.pauseChangeHandlers = pauseChangeHandlers;
        return (T) this;
    }

    public T pauseChangeHandlers() {
        return setPauseChangeHandlers(true);
    }

    public T resumeChangeHandlers() {
        return setPauseChangeHandlers(false);
    }

    public FlexLayout getLeftAddonContainer() {
        return leftAddOnsContainer;
    }

    public FlexLayout getRightAddonContainer() {
        return rightAddOnsContainer;
    }

    @Override
    public boolean hasChangeHandler(ChangeHandler changeHandler) {
        return changeHandlers.contains(changeHandler);
    }

    public T setSpellCheck(boolean spellCheck) {
        inputElement.setAttribute("spellcheck", spellCheck);
        return (T) this;
    }

    public T addOnClearHandler(Consumer onClearHandler) {
        this.onClearHandlers.add(onClearHandler);
        return (T) this;
    }

    public T removeOnClearHandler(Consumer onClearHandler) {
        this.onClearHandlers.remove(onClearHandler);
        return (T) this;
    }

    public List> getOnClearHandlers() {
        return new ArrayList<>(onClearHandlers);
    }

    public T setPrefix(String prefix) {
        if (!prefixItem.isAttached()) {
            fieldInnerContainer.insertBefore(prefixItem, inputContainer);
        }
        this.prefixItem.setTextContent(prefix);
        this.prefix = prefix;
        return (T) this;
    }

    public String getPrefix() {
        return prefix;
    }

    public T setPostFix(String postfix) {
        if (!postFixItem.isAttached()) {
            fieldInnerContainer.insertAfter(postFixItem, inputContainer);
        }
        this.postFixItem.setTextContent(postfix);
        this.postfix = postfix;

        return (T) this;
    }

    public DominoElement getFieldGroup() {
        return fieldGroup;
    }

    public FlexItem getInputContainer() {
        return inputContainer;
    }

    public DominoElement getNotesContainer() {
        return notesContainer;
    }

    @Override
    public DominoElement getAdditionalInfoContainer() {
        return DominoElement.of(additionalInfoContainer);
    }

    public FlexLayout getLeftAddOnsContainer() {
        return leftAddOnsContainer;
    }

    public FlexLayout getRightAddOnsContainer() {
        return rightAddOnsContainer;
    }

    public FlexItem getHelpItem() {
        return helpItem;
    }

    public FlexItem getCountItem() {
        return countItem;
    }

    public FlexItem getErrorItem() {
        return errorItem;
    }

    public FlexItem getPrefixItem() {
        return prefixItem;
    }

    public FlexItem getPostFixItem() {
        return postFixItem;
    }

    public Color getFocusColor() {
        return focusColor;
    }

    public String getPostfix() {
        return postfix;
    }

    protected abstract void clearValue();

    protected abstract void doSetValue(V value);

    public T condense() {
        removeCss("condensed");
        css("condensed");
        return (T) this;
    }

    public T spread() {
        removeCss("condensed");
        return (T) this;
    }

    protected FlexItem createMandatoryAddOn() {
        return null;
    }

    public interface AutoValidate {
        void apply();
    }

    public abstract static class AutoValidator {
        protected AutoValidate autoValidate;

        public AutoValidator(AutoValidate autoValidate) {
            this.autoValidate = autoValidate;
        }

        public abstract void attach();

        public abstract void remove();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy