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

com.vaadin.flow.component.listbox.MultiSelectListBox Maven / Gradle / Ivy

There is a newer version: 24.5.5
Show newest version
/*
 * Copyright 2000-2024 Vaadin Ltd.
 *
 * 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 com.vaadin.flow.component.listbox;

import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.vaadin.flow.data.selection.MultiSelect;
import com.vaadin.flow.data.selection.MultiSelectionEvent;
import com.vaadin.flow.data.selection.MultiSelectionListener;
import com.vaadin.flow.shared.Registration;

import elemental.json.Json;
import elemental.json.JsonArray;

/**
 * Server-side component for the {@code vaadin-list-box} element with
 * multi-selection.
 *
 * @author Vaadin Ltd
 *
 * @param 
 *            the type of the items contained by this component
 * @see ListBox
 */
public class MultiSelectListBox
        extends ListBoxBase, T, Set>
        implements MultiSelect, T> {

    /**
     * Creates a new list box component with multi-selection.
     */
    public MultiSelectListBox() {
        super("selectedValues", JsonArray.class, Collections.emptySet(),
                MultiSelectListBox::presentationToModel,
                MultiSelectListBox::modelToPresentation);
        getElement().setProperty("multiple", true);
    }

    private static  Set presentationToModel(MultiSelectListBox listBox,
            JsonArray presentation) {
        Set modelValue = IntStream.range(0, presentation.length())
                .map(idx -> (int) presentation.getNumber(idx))
                .mapToObj(index -> listBox.getItems().get(index))
                .collect(Collectors.toSet());
        return Collections.unmodifiableSet(modelValue);
    }

    private static  JsonArray modelToPresentation(
            MultiSelectListBox listBox, Set model) {
        JsonArray array = Json.createArray();

        AtomicInteger idx = new AtomicInteger(0);
        listBox.getItems().forEach(item -> {
            int index = idx.getAndIncrement();
            Object itemId = listBox.getItemId(item);
            model.stream()
                    .filter(selectedItem -> itemId
                            .equals(listBox.getItemId(selectedItem)))
                    .findFirst()
                    .ifPresent(ignored -> array.set(array.length(), index));
        });

        return array;
    }

    /**
     * Sets the value of this component. If the new value is not equal to the
     * previous value, fires a value change event.
     * 

* The component doesn't accept {@code null} values. The value of multi * select list box without any selected items is an empty set. You can use * the {@link #clear()} method to set the empty value. * * @param value * the new value to set, not {@code null} * @throws NullPointerException * if value is {@code null} */ @Override public void setValue(Set value) { Objects.requireNonNull(value, "Cannot set a null value to multi select list box. " + "Use the clear-method to reset the component's value to an empty set."); super.setValue(value); } @Override public void updateSelection(Set addedItems, Set removedItems) { Set value = new HashSet<>(getValue()); value.addAll(addedItems); value.removeAll(removedItems); setValue(value); } /** * Returns an immutable set of the currently selected items. It is safe to * invoke other {@code SelectionModel} methods while iterating over the set. *

* There are no guarantees of the iteration order of the returned set of * items. * * @return the items in the current selection, not {@code null} */ @Override public Set getSelectedItems() { return getValue(); } @Override public Registration addSelectionListener( MultiSelectionListener, T> listener) { return addValueChangeListener(event -> listener .selectionChange(new MultiSelectionEvent<>(this, this, event.getOldValue(), event.isFromClient()))); } /** * Compares two value instances to each other to determine whether they are * equal. Equality is used to determine whether to update internal state and * fire an event when {@link #setValue(Object)} or * {@link #setModelValue(Object, boolean)} is called. Subclasses can * override this method to define an alternative comparison method instead * of {@link Objects#equals(Object)}. * * @param value1 * the first set of instance * @param value2 * the second set of instance * @return true if sets are equal in size and also the items; * otherwise false */ @Override protected boolean valueEquals(Set value1, Set value2) { if (value1 == null && value2 == null) return true; if (value1 == null || value2 == null) return false; if (value1.size() != value2.size()) return false; Set ids1 = value1.stream().map(super::getItemId) .collect(Collectors.toSet()); Set ids2 = value2.stream().map(super::getItemId) .collect(Collectors.toSet()); return ids1.equals(ids2); } }