org.bklab.flow.layout.select.DragSelectLayout Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fluent-vaadin-flow Show documentation
Show all versions of fluent-vaadin-flow Show documentation
Broderick Labs for fluent vaadin flow. Inherits common Vaadin components.
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 super AbstractField.ComponentValueChangeEvent, 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