
org.dominokit.domino.ui.forms.MultiSelect Maven / Gradle / Ivy
/*
* 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