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

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

There is a newer version: 2.0.4
Show newest version
/*
 * Copyright © 2019 Dominokit
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dominokit.domino.ui.forms;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

import elemental2.dom.HTMLElement;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.dominokit.domino.ui.chips.Chip;
import org.dominokit.domino.ui.dropdown.DropdownAction;
import org.dominokit.domino.ui.grid.flex.FlexItem;
import org.dominokit.domino.ui.style.Color;
import org.dominokit.domino.ui.style.Elevation;
import org.dominokit.domino.ui.utils.ElementUtil;

/**
 * A component that allow selecting multiple options from a DropDownMenu
 *
 * @param  The type of a single option value
 */
public class MultiSelect extends AbstractSelect, T, MultiSelect> {

  private static final String DEFAULT_SEPARATOR = ",";

  private ValueRenderer valueRenderer = this::renderSelectedOptions;
  private List> selectedOptions = new ArrayList<>();
  private String selectedOptionsSeparator;
  private Color color = Color.THEME;

  /**
   * Creates an instance without a label
   *
   * @param  Type the select options value
   * @return new MultiSelect instance
   */
  public static  MultiSelect create() {
    return new MultiSelect<>();
  }

  /**
   * Creates an instance with a label
   *
   * @param label String
   * @param  Type the select options value
   * @return new MultiSelect instance
   */
  public static  MultiSelect create(String label) {
    return new MultiSelect<>(label);
  }

  /**
   * Creates an instance with a label and initialized with a list of options
   *
   * @param label String
   * @param options List of {@link SelectOption}
   * @param  Type the select options value
   * @return new MultiSelect instance
   */
  public static  MultiSelect create(String label, List> options) {
    return new MultiSelect<>(label, options);
  }

  /**
   * Creates an instance without a label and initialized with a list of options
   *
   * @param options List of {@link SelectOption}
   * @param  Type the select options value
   * @return new MultiSelect instance
   */
  public static  MultiSelect create(List> options) {
    return new MultiSelect<>(options);
  }

  /**
   * Creates an instance with a label and initialized with options from an enum values
   *
   * @param label String
   * @param values T[] of enum values
   * @param  type of the enum
   * @return new MultiSelect instance
   */
  public static > MultiSelect ofEnum(String label, T[] values) {
    MultiSelect select = create(label);
    for (T value : values) {
      select.appendChild(SelectOption.create(value, value.name(), value.toString()));
    }
    return select;
  }

  /** Creates an instance without a label */
  public MultiSelect() {
    setup();
  }

  /**
   * Creates an instance with a label
   *
   * @param label String
   */
  public MultiSelect(String label) {
    super(label);
    setup();
  }

  /**
   * Creates an instance with a label and initialized with a list of options
   *
   * @param label String
   * @param options List of {@link SelectOption}
   */
  public MultiSelect(String label, List> options) {
    super(label, options);
    setup();
  }

  /**
   * Creates an instance without a label and initialized with a list of options
   *
   * @param options List of {@link SelectOption}
   */
  public MultiSelect(List> options) {
    super(options);
    setup();
  }

  private void setup() {
    fieldGroup.addCss("multi-select");
    noneOption.addCss("multi-select-option");
    setOptionRenderer(new MultiOptionRenderer());
  }

  /** {@inheritDoc} */
  @Override
  public MultiSelect appendChild(
      SelectOption option, Consumer>> andThen) {
    option.addCss("multi-select-option");
    return super.appendChild(option, andThen);
  }

  /** {@inheritDoc} */
  @Override
  public MultiSelect select(SelectOption option, boolean silent) {
    floatLabel();
    if (this.selectedOptions.contains(option)) {
      option.deselect(silent);
      selectedOptions.remove(option);
    } else {
      this.selectedOptions.add(option);
      option.select();
    }
    valueRenderer.render();
    hidePlaceholder();
    if (!silent) onSelection(option);
    return this;
  }

  /** {@inheritDoc} */
  @Override
  public MultiSelect setValue(List value, boolean silent) {
    if (isNull(value)) return this;
    for (SelectOption option : getOptions()) {
      if (value.contains(option.getValue())) {
        select(option, silent);
      }
    }
    return this;
  }

  private void renderSelectedOptions() {
    valuesContainer.clearElement();
    selectedOptions.stream()
        .map(
            tSelectOption -> {
              Chip chip =
                  Chip.create(tSelectOption.getDisplayValue())
                      .setRemovable(!isReadOnly())
                      .setColor(color)
                      .setBorderColor(color)
                      .elevate(Elevation.NONE);
              chip.addRemoveHandler(
                  () -> {
                    tSelectOption.deselect();
                    selectedOptions.remove(tSelectOption);
                    chip.remove(true);
                    showPlaceholder();
                    if (isAutoValidation()) validate();
                  });

              return chip;
            })
        .forEach(valuesContainer::appendChild);
  }

  /** @return a List of all {@link SelectOption} selected */
  public List> getSelectedOptions() {
    return selectedOptions;
  }

  /** {@inheritDoc} */
  @Override
  public List getValue() {
    return selectedOptions.stream().map(SelectOption::getValue).collect(Collectors.toList());
  }

  /** @return String separator to display selected options */
  public String getSelectedOptionsSeparator() {
    return selectedOptionsSeparator;
  }

  /** @param selectedOptionsSeparator String separator to display selected options */
  public void setSelectedOptionsSeparator(String selectedOptionsSeparator) {
    this.selectedOptionsSeparator = selectedOptionsSeparator;
  }

  /**
   * {@inheritDoc}
   *
   * @return A comma separated string of all selected options
   */
  @Override
  public String getStringValue() {
    return selectedOptions.stream()
        .map(SelectOption::getDisplayValue)
        .collect(
            Collectors.joining(
                isNull(selectedOptionsSeparator) ? DEFAULT_SEPARATOR : selectedOptionsSeparator));
  }

  /** {@inheritDoc} */
  @Override
  protected void clearValue(boolean silent) {
    selectedOptions.forEach(option -> option.deselect(silent));
    selectedOptions.clear();
  }

  /** @return List of Integer indices of all select options */
  public List getSelectedIndex() {
    return getSelectedOptions().stream()
        .map(option -> options.indexOf(option))
        .collect(Collectors.toList());
  }

  /** @param valueRenderer {@link ValueRenderer} */
  public void setValueRenderer(ValueRenderer valueRenderer) {
    this.valueRenderer = valueRenderer;
  }

  /** {@inheritDoc} */
  @Override
  protected void scrollToSelectedOption() {
    List> selectedOptions = getSelectedOptions();
    if (nonNull(selectedOptions) && !selectedOptions.isEmpty()) {
      ElementUtil.scrollIntoParent(
          selectedOptions.get(0).element(), getOptionsMenu().getMenuElement().element());
    }
  }

  /**
   * Sets the color of the options and the display values
   *
   * @param color A {@link Color}
   * @return same instance
   */
  public MultiSelect setColor(Color color) {
    this.color = color;
    return this;
  }

  @Override
  public MultiSelect setReadOnly(boolean readOnly) {
    super.setReadOnly(readOnly);
    valueRenderer.render();
    return this;
  }

  @Override
  public boolean isEmpty() {
    return isNull(getValue()) || getValue().isEmpty();
  }

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

  @Override
  public boolean isSelected() {
    return nonNull(getSelectedOptions()) && !getSelectedOptions().isEmpty();
  }

  /** a Function to define how we should render the select value */
  @FunctionalInterface
  public interface ValueRenderer {
    void render();
  }

  private class MultiOptionRenderer implements OptionRenderer {

    @Override
    public HTMLElement element(SelectOption option) {
      CheckBox checkMark = CheckBox.create().setColor(color).filledIn();
      FlexItem checkMarkFlexItem = FlexItem.create();
      checkMarkFlexItem.appendChild(checkMark);
      option.getOptionLayoutElement().insertFirst(checkMarkFlexItem);
      option.addSelectionHandler(
          selectable -> {
            if (selectable.isSelected()) {
              checkMark.check(true);
            } else {
              checkMark.uncheck(true);
            }
          });
      checkMark.addChangeHandler(value -> select(option));
      return option.element();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy