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

org.bklab.flow.layout.select.DragSelectLayout Maven / Gradle / Ivy

There is a newer version: 22.0.1
Show newest version
package org.bklab.flow.layout.select;

import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dnd.*;
import com.vaadin.flow.component.html.Div;
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 org.bklab.flow.factory.DivFactory;
import org.bklab.flow.layout.EmptyLayout;
import org.bklab.flow.layout.TitleLayout;
import org.bklab.flow.layout.ToolBar;
import org.bklab.flow.util.lumo.LumoStyles;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

@Tag("drag-select-layout")
public class DragSelectLayout extends Div implements MultiSelect, T> {

    private final Div candidateContainer = new DivFactory().displayFlex().heightFull().get();
    private final Div selectedContainer = new DivFactory().displayFlex().heightFull().get();

    private final ToolBar candidateFooter = new ToolBar();
    private final ToolBar selectedFooter = new ToolBar();

    private final TitleLayout candidateLayout = new TitleLayout("请拖动选择").content(candidateContainer, candidateFooter).heightFull();
    private final TitleLayout selectedLayout = new TitleLayout("已选择").content(selectedContainer, selectedFooter).heightFull();

    private final Set candidateItems = new LinkedHashSet<>();
    private final Set selectedItems = new LinkedHashSet<>();

    private final Map candidateComponentMap = new LinkedHashMap<>();
    private final Map selectedComponentMap = new LinkedHashMap<>();
    private final List, T>> multiSelectionListeners = new ArrayList<>();
    private final List, Set>>> valueChangeListeners = new ArrayList<>();
    private final AtomicReference> oldValues = new AtomicReference<>(null);
    private Function componentRender = t -> new Button(String.valueOf(t));
    private EmptyLayout emptyLayout;

    {
        add(candidateLayout, selectedLayout);

        candidateLayout.getStyle().set("flex-grow", "1").set("min-width", "150px");
        selectedLayout.getStyle().set("width", "150px").set("margin-left", "var(--lumo-space-m)");

        candidateLayout.getContent().getStyle().set("flex-wrap", "wrap");
        selectedLayout.getContent().getStyle().set("flex-wrap", "wrap");

        candidateLayout.header().setMinHeight("38px");
        selectedLayout.header().setMinHeight("38px");
        candidateLayout.getTitleLabel().getStyle().set("line-height", "38px");
        selectedLayout.getTitleLabel().getStyle().set("line-height", "38px");

        candidateContainer.getStyle().set("border", "1px solid #d9d9d9").set("flex-wrap", "wrap").set("min-height", "100px")
                .set("padding", "var(--lumo-space-m)").set("overflow-y", "auto").set("flex-direction", "row")
                .set("align-content", "flex-start").set("box-sizing", "border-box");
        selectedContainer.getStyle().set("border", "1px solid #d9d9d9").set("flex-wrap", "wrap").set("min-height", "100px")
                .set("padding", "var(--lumo-space-m)").set("overflow-y", "auto").set("flex-direction", "row")
                .set("align-content", "flex-start").set("box-sizing", "border-box");

        candidateContainer.addClassName(LumoStyles.Spacing.Right.M);
        selectedContainer.addClassName(LumoStyles.Spacing.Right.M);

        addClassName(LumoStyles.Spacing.Right.M);
        getElement().getStyle().set("display", "flex").set("padding-right", "var(--lumo-space-l)");
    }

    public DragSelectLayout() {

    }

    public DragSelectLayout whenEmpty(String message) {
        emptyLayout = new EmptyLayout(message);
        selectedContainer.add(emptyLayout);
        emptyLayout.setVisible(selectedItems.isEmpty());

        addSelectionListener(event -> emptyLayout.setVisible(event.getValue().isEmpty()));

        return this;
    }

    private void createDragSupport(Component component, T instance) {
        DragSource dragSource = DragSource.create(component);
        dragSource.setEffectAllowed(EffectAllowed.MOVE);
        dragSource.setDragData(instance);
        dragSource.addDragStartListener(e -> e.setDragData(instance));
        dragSource.addDragEndListener(DragEndEvent::clearDragData);

        effectDropTarget(selectedContainer, candidateComponentMap, selectedComponentMap, candidateItems, selectedItems);
        effectDropTarget(candidateContainer, selectedComponentMap, candidateComponentMap, selectedItems, candidateItems);
    }

    private void effectDropTarget(Div container, Map sourceComponentMap,
                                  Map targetComponentMap, Set sourceSet, Set targetSet) {
        DropTarget
candidateTarget = DropTarget.create(container); candidateTarget.setDropEffect(DropEffect.MOVE); candidateTarget.addDropListener(event -> { Component source = event.getDragSourceComponent().orElse(null); if (sourceComponentMap.containsKey(source)) { @SuppressWarnings("unchecked") T data = (T) event.getDragData().orElse(null); if (data == null) return; targetComponentMap.put(source, data); sourceComponentMap.remove(source); container.add(source); sourceSet.remove(data); targetSet.add(data); call(true); } }); } private void call(boolean userOriginated) { MultiSelectionEvent, T> selectionEvent = new MultiSelectionEvent<>(this, this, getOldValues(), userOriginated); multiSelectionListeners.forEach(listener -> listener.selectionChange(selectionEvent)); AbstractField.ComponentValueChangeEvent, Set> changeEvent = new AbstractField.ComponentValueChangeEvent<>(this, this, getOldValues(), userOriginated); valueChangeListeners.forEach(listener -> listener.valueChanged(changeEvent)); oldValues.set(Collections.unmodifiableSet(selectedItems)); } public Set getOldValues() { if (oldValues.get() == null) oldValues.set(Collections.unmodifiableSet(selectedItems)); return oldValues.get(); } public DragSelectLayout componentRender(Function componentRender) { this.componentRender = componentRender; return this; } @SafeVarargs public final DragSelectLayout items(T... candidateItems) { return items(Arrays.asList(candidateItems)); } public DragSelectLayout items(Collection candidateItems) { clearCandidate(); this.candidateItems.addAll(candidateItems); for (T instance : this.candidateItems) { if (selectedItems.contains(instance)) continue; Component c = componentRender.apply(instance); candidateComponentMap.put(c, instance); createDragSupport(c, instance); candidateContainer.add(c); } return this; } public void clearInstance() { candidateItems.clear(); selectedItems.clear(); candidateContainer.removeAll(); selectedContainer.removeAll(); candidateComponentMap.clear(); selectedComponentMap.clear(); call(false); } public void clearCandidate() { candidateItems.clear(); candidateContainer.removeAll(); candidateComponentMap.clear(); } public void clearSelect() { selectedContainer.removeAll(); candidateItems.addAll(selectedItems); candidateContainer.add(selectedComponentMap.keySet().toArray(new Component[]{})); candidateComponentMap.putAll(selectedComponentMap); selectedItems.clear(); selectedComponentMap.clear(); call(false); } @Override public void updateSelection(Set addedItems, Set removedItems) { if (addedItems != null) addedItems.forEach(this::select); if (removedItems != null) removedItems.forEach(this::deselect); } public void select(T instance) { doSelect(instance, candidateComponentMap, candidateContainer, candidateItems, selectedContainer, selectedItems, selectedComponentMap); } public void deselect(T instance) { doSelect(instance, selectedComponentMap, selectedContainer, selectedItems, candidateContainer, candidateItems, candidateComponentMap); } private void doSelect(T instance, Map sourceComponentMap, Div sourceContainer, Set sourceItems, Div targetContainer, Set targetItems, Map targetComponentMap) { Component c = sourceComponentMap.keySet().stream().filter(component -> sourceComponentMap.get(component) == instance).findFirst().orElse(null); if (c == null) return; sourceContainer.remove(c); sourceItems.remove(instance); sourceComponentMap.remove(c); targetContainer.add(c); targetItems.add(instance); targetComponentMap.put(c, instance); call(false); } @Override public Set getSelectedItems() { return Collections.unmodifiableSet(selectedItems); } @Override public Registration addSelectionListener(MultiSelectionListener, T> multiSelectionListener) { multiSelectionListeners.add(multiSelectionListener); return () -> multiSelectionListeners.remove(multiSelectionListener); } @Override public Registration addValueChangeListener(ValueChangeListener, Set>> valueChangeListener) { valueChangeListeners.add(valueChangeListener); return () -> valueChangeListeners.remove(valueChangeListener); } public Div getCandidateContainer() { return candidateContainer; } public Div getSelectedContainer() { return selectedContainer; } public ToolBar getCandidateFooter() { return candidateFooter; } public ToolBar getSelectedFooter() { return selectedFooter; } public ToolBar getCandidateHeader() { return candidateLayout.header(); } public ToolBar getSelectedHeader() { return selectedLayout.header(); } public TitleLayout getCandidateLayout() { return candidateLayout; } public TitleLayout getSelectedLayout() { return selectedLayout; } public Set getCandidateItems() { return candidateItems; } public DragSelectLayout containerHeight(String minHeight, String height, String maxHeight) { this.selectedContainer.setMinHeight(minHeight); this.candidateContainer.setMinHeight(minHeight); this.selectedContainer.setHeight(height); this.candidateContainer.setHeight(height); this.selectedContainer.setMaxHeight(maxHeight); this.candidateContainer.setMaxHeight(maxHeight); return this; } public DragSelectLayout containerHeight(String height) { this.selectedContainer.setHeight(height); this.candidateContainer.setHeight(height); return this; } public DragSelectLayout containerMaxHeight(String height) { this.selectedContainer.setMaxHeight(height); this.candidateContainer.setMaxHeight(height); return this; } public DragSelectLayout containerMinHeight(String height) { this.selectedContainer.setMinHeight(height); this.candidateContainer.setMinHeight(height); return this; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy