diff --git a/pom.xml b/pom.xml
index 9a7b4bd..7ee5d11 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.flowingcode.vaadin.addons
twincolgrid
- 2.9.3-SNAPSHOT
+ 3.0.0-SNAPSHOT
TwinColGrid add-on
14.8.1
diff --git a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/EagerFilterableColumn.java b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/EagerFilterableColumn.java
new file mode 100644
index 0000000..243ef39
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/EagerFilterableColumn.java
@@ -0,0 +1,23 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import com.vaadin.flow.component.ItemLabelGenerator;
+import com.vaadin.flow.function.SerializableBiPredicate;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+@RequiredArgsConstructor
+@Getter
+@Setter
+@Accessors(fluent = true)
+public class EagerFilterableColumn {
+
+ private final ItemLabelGenerator itemLabelGenerator;
+ private final String header;
+ private final String filterPlaceholder;
+ private final boolean enableClearButton;
+ private final SerializableBiPredicate filterCondition;
+ private String key;
+
+}
diff --git a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/EagerTwinColModel.java b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/EagerTwinColModel.java
new file mode 100644
index 0000000..63d7596
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/EagerTwinColModel.java
@@ -0,0 +1,187 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import com.vaadin.flow.component.ComponentEventListener;
+import com.vaadin.flow.component.grid.Grid;
+import com.vaadin.flow.component.grid.Grid.Column;
+import com.vaadin.flow.component.grid.HeaderRow;
+import com.vaadin.flow.component.grid.ItemDoubleClickEvent;
+import com.vaadin.flow.component.grid.dnd.GridDropLocation;
+import com.vaadin.flow.component.html.Label;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.component.textfield.TextField;
+import com.vaadin.flow.data.provider.DataProvider;
+import com.vaadin.flow.data.provider.ListDataProvider;
+import com.vaadin.flow.data.renderer.TextRenderer;
+import com.vaadin.flow.data.value.ValueChangeMode;
+import com.vaadin.flow.shared.Registration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import lombok.NonNull;
+
+public class EagerTwinColModel implements TwinColModel {
+
+ private final Grid grid;
+ private final Label columnLabel = new Label();
+ private final VerticalLayout layout;
+ private HeaderRow headerRow;
+ private boolean droppedInsideGrid = false;
+ private boolean allowReordering = false;
+ private Registration moveItemsByDoubleClick;
+
+ EagerTwinColModel(@NonNull Grid grid, String className) {
+ this.grid = grid;
+ this.grid.setDataProvider(DataProvider.ofCollection(new ArrayList<>()));
+
+ layout = new VerticalLayout(columnLabel, grid);
+
+ layout.setClassName(className);
+ grid.setClassName("twincol-grid-items");
+ columnLabel.setClassName("twincol-grid-label");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ListDataProvider getDataProvider() {
+ return (ListDataProvider) grid.getDataProvider();
+ }
+
+ @Override
+ public boolean isReorderingEnabled() {
+ return allowReordering && grid.getSortOrder().isEmpty();
+ }
+
+ @Override
+ public Grid getGrid() {
+ return grid;
+ }
+
+ @Override
+ public Label getColumnLabel() {
+ return columnLabel;
+ }
+
+ @Override
+ public boolean isDroppedInsideGrid() {
+ return droppedInsideGrid;
+ }
+
+ @Override
+ public void setDroppedInsideGrid(boolean droppedInsideGrid) {
+ this.droppedInsideGrid = droppedInsideGrid;
+ }
+
+ @Override
+ public void setAllowReordering(boolean allowReordering) {
+ this.allowReordering = allowReordering;
+ }
+
+ @Override
+ public boolean isAllowReordering() {
+ return allowReordering;
+ }
+
+ @Override
+ public HeaderRow getHeaderRow() {
+ return headerRow;
+ }
+
+ @Override
+ public void setHeaderRow(HeaderRow headerRow) {
+ this.headerRow = headerRow;
+ }
+
+ @Override
+ public Registration getMoveItemsByDoubleClick() {
+ return moveItemsByDoubleClick;
+ }
+
+ @Override
+ public Registration addItemDoubleClickListener(
+ ComponentEventListener> listener) {
+ moveItemsByDoubleClick = grid.addItemDoubleClickListener(listener);
+ return moveItemsByDoubleClick;
+ }
+
+ @Override
+ public void removeItemDoubleClickListener() {
+ if (moveItemsByDoubleClick != null) {
+ moveItemsByDoubleClick.remove();
+ moveItemsByDoubleClick = null;
+ }
+ }
+
+ @Override
+ public VerticalLayout getLayout() {
+ return layout;
+ }
+
+ @Override
+ public void addAll(Collection items) {
+ getDataProvider().getItems().addAll(items);
+ }
+
+ @Override
+ public void removeAll(Collection items) {
+ getDataProvider().getItems().removeAll(items);
+ }
+
+ @Override
+ public void clear() {
+ getDataProvider().getItems().clear();
+ }
+
+ public void addFilterableColumn(EagerFilterableColumn filter) {
+ Column column =
+ grid.addColumn(new TextRenderer<>(filter.itemLabelGenerator())).setHeader(filter.header());
+
+ Optional.ofNullable(filter.key()).ifPresent(column::setKey);
+
+ TextField filterField = new TextField();
+ filterField.setClearButtonVisible(filter.enableClearButton());
+
+ filterField.addValueChangeListener(
+ event -> getDataProvider()
+ .addFilter(item -> filter.filterCondition().test(item, filterField.getValue())));
+
+ if (headerRow == null) {
+ setHeaderRow(grid.appendHeaderRow());
+ }
+
+ headerRow.getCell(column).setComponent(filterField);
+ filterField.setValueChangeMode(ValueChangeMode.EAGER);
+ filterField.setSizeFull();
+ filterField.setPlaceholder(filter.filterPlaceholder());
+ }
+
+ public void addItems(Collection draggedItems,
+ T dropOverItem, GridDropLocation dropLocation) {
+ if (dropOverItem != null) {
+ Collection collection = getDataProvider().getItems();
+ List list = new ArrayList<>(collection);
+ int dropIndex = list.indexOf(dropOverItem) + (dropLocation == GridDropLocation.BELOW ? 1 : 0);
+ list.addAll(dropIndex, draggedItems);
+ clear();
+ addAll(list);
+ } else {
+ addAll(draggedItems);
+ }
+ grid.getDataProvider().refreshAll();
+ }
+
+ public Collection getItems() {
+ return getDataProvider().getItems();
+ }
+
+ @Override
+ public TwinColModelMode getMode() {
+ return TwinColModelMode.EAGER;
+ }
+
+ @Override
+ public boolean supportsAddAll() {
+ return true;
+ }
+
+}
diff --git a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilter.java b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilter.java
new file mode 100644
index 0000000..ef5e841
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilter.java
@@ -0,0 +1,14 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import java.util.Collection;
+import java.util.HashSet;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class LazyFilter {
+
+ private Collection excludedItems = new HashSet<>();
+
+}
diff --git a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilterableColumn.java b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilterableColumn.java
new file mode 100644
index 0000000..163f8e8
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilterableColumn.java
@@ -0,0 +1,30 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import com.vaadin.flow.component.ItemLabelGenerator;
+import com.vaadin.flow.function.SerializableBiPredicate;
+import com.vaadin.flow.function.SerializableConsumer;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+@RequiredArgsConstructor
+@Getter
+@Accessors(fluent = true)
+@Setter
+public class LazyFilterableColumn {
+
+ private final ItemLabelGenerator itemLabelGenerator;
+ private final String header;
+ private final String filterPlaceholder;
+ private final boolean enableClearButton;
+ private String key;
+ private SerializableConsumer lazyFilterAttribute;
+ private SerializableBiPredicate eagerFilterCondition;
+
+ public EagerFilterableColumn asEager() {
+ return new EagerFilterableColumn<>(itemLabelGenerator, header, filterPlaceholder,
+ enableClearButton, eagerFilterCondition);
+ }
+
+}
diff --git a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyTwinColModel.java b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyTwinColModel.java
new file mode 100644
index 0000000..b50480b
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/LazyTwinColModel.java
@@ -0,0 +1,198 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import com.vaadin.flow.component.ComponentEventListener;
+import com.vaadin.flow.component.grid.Grid;
+import com.vaadin.flow.component.grid.Grid.Column;
+import com.vaadin.flow.component.grid.HeaderRow;
+import com.vaadin.flow.component.grid.ItemDoubleClickEvent;
+import com.vaadin.flow.component.grid.dnd.GridDropLocation;
+import com.vaadin.flow.component.html.Label;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.component.textfield.TextField;
+import com.vaadin.flow.data.provider.ConfigurableFilterDataProvider;
+import com.vaadin.flow.data.provider.DataProvider;
+import com.vaadin.flow.data.renderer.TextRenderer;
+import com.vaadin.flow.data.value.ValueChangeMode;
+import com.vaadin.flow.shared.Registration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import lombok.NonNull;
+
+public class LazyTwinColModel implements TwinColModel {
+
+ private final Grid grid;
+ private final Label columnLabel = new Label();
+ private final VerticalLayout layout;
+ private HeaderRow headerRow;
+ private boolean droppedInsideGrid = false;
+ private boolean allowReordering = false;
+ private Registration moveItemsByDoubleClick;
+ private LazyFilter lazyFilter;
+ private final ConfigurableFilterDataProvider> dataProviderWrapper;
+
+ LazyTwinColModel(@NonNull Grid grid, String className) {
+ this.grid = grid;
+ layout = new VerticalLayout(columnLabel, grid);
+
+ layout.setClassName(className);
+ grid.setClassName("twincol-grid-items");
+ columnLabel.setClassName("twincol-grid-label");
+
+ dataProviderWrapper = (ConfigurableFilterDataProvider>) grid
+ .getDataProvider().withConfigurableFilter();
+ lazyFilter = new LazyFilter<>();
+ dataProviderWrapper.setFilter(lazyFilter);
+ grid.setDataProvider(dataProviderWrapper);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public DataProvider> getDataProvider() {
+ return (DataProvider>) grid.getDataProvider();
+ }
+
+
+ @Override
+ public boolean isReorderingEnabled() {
+ return allowReordering && grid.getSortOrder().isEmpty();
+ }
+
+ @Override
+ public Grid getGrid() {
+ return grid;
+ }
+
+ @Override
+ public Label getColumnLabel() {
+ return columnLabel;
+ }
+
+ @Override
+ public boolean isDroppedInsideGrid() {
+ return droppedInsideGrid;
+ }
+
+ @Override
+ public void setDroppedInsideGrid(boolean droppedInsideGrid) {
+ this.droppedInsideGrid = droppedInsideGrid;
+ }
+
+ @Override
+ public void setAllowReordering(boolean allowReordering) {
+ this.allowReordering = allowReordering;
+ }
+
+ @Override
+ public boolean isAllowReordering() {
+ return allowReordering;
+ }
+
+ @Override
+ public HeaderRow getHeaderRow() {
+ return headerRow;
+ }
+
+ @Override
+ public void setHeaderRow(HeaderRow headerRow) {
+ this.headerRow = headerRow;
+ }
+
+ @Override
+ public Registration getMoveItemsByDoubleClick() {
+ return moveItemsByDoubleClick;
+ }
+
+ @Override
+ public Registration addItemDoubleClickListener(
+ ComponentEventListener> listener) {
+ moveItemsByDoubleClick = grid.addItemDoubleClickListener(listener);
+ return moveItemsByDoubleClick;
+ }
+
+ @Override
+ public void removeItemDoubleClickListener() {
+ if (moveItemsByDoubleClick != null) {
+ moveItemsByDoubleClick.remove();
+ moveItemsByDoubleClick = null;
+ }
+ }
+
+ @Override
+ public VerticalLayout getLayout() {
+ return layout;
+ }
+
+
+ @Override
+ public void addAll(Collection items) {
+ lazyFilter.getExcludedItems().removeAll(items);
+ }
+
+ @Override
+ public void removeAll(Collection items) {
+ lazyFilter.getExcludedItems().addAll(items);
+ }
+
+ @Override
+ public void clear() {
+ lazyFilter.getExcludedItems().clear();
+ }
+
+
+ public void addFilterableColumn(LazyFilterableColumn filter) {
+ Column column = grid.addColumn(new TextRenderer<>(filter.itemLabelGenerator()))
+ .setHeader(filter.header());
+
+ Optional.ofNullable(filter.key()).ifPresent(column::setKey);
+
+ TextField filterField = new TextField();
+ filterField.setClearButtonVisible(filter.enableClearButton());
+
+ filterField.addValueChangeListener(
+ event -> {
+ filter.lazyFilterAttribute().accept(filterField.getValue());
+ getDataProvider().refreshAll();
+ });
+
+ if (headerRow == null) {
+ setHeaderRow(grid.appendHeaderRow());
+ }
+
+ headerRow.getCell(column).setComponent(filterField);
+ filterField.setValueChangeMode(ValueChangeMode.EAGER);
+ filterField.setSizeFull();
+ filterField.setPlaceholder(filter.filterPlaceholder());
+ }
+
+ public void addItems(Collection draggedItems,
+ T dropOverItem, GridDropLocation dropLocation) {
+ if (dropOverItem != null) {
+ Collection collection = lazyFilter.getExcludedItems();
+ List list = new ArrayList<>(collection);
+ int dropIndex = list.indexOf(dropOverItem) + (dropLocation == GridDropLocation.BELOW ? 1 : 0);
+ list.addAll(dropIndex, draggedItems);
+ addAll(list);
+ } else {
+ addAll(draggedItems);
+ }
+ grid.getDataProvider().refreshAll();
+ }
+
+ public > void setFilter(C filter) {
+ lazyFilter = filter;
+ dataProviderWrapper.setFilter(filter);
+ }
+
+ @Override
+ public TwinColModelMode getMode() {
+ return TwinColModelMode.LAZY;
+ }
+
+ @Override
+ public boolean supportsAddAll() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/TwinColGrid.java b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/TwinColGrid.java
index 2045bdc..2b7f74c 100644
--- a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/TwinColGrid.java
+++ b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/TwinColGrid.java
@@ -20,6 +20,7 @@
package com.flowingcode.vaadin.addons.twincolgrid;
+import com.flowingcode.vaadin.addons.twincolgrid.TwinColModel.TwinColModelMode;
import com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent;
import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.Component;
@@ -32,28 +33,20 @@
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.grid.Grid;
-import com.vaadin.flow.component.grid.Grid.Column;
import com.vaadin.flow.component.grid.Grid.SelectionMode;
import com.vaadin.flow.component.grid.GridNoneSelectionModel;
-import com.vaadin.flow.component.grid.HeaderRow;
-import com.vaadin.flow.component.grid.dnd.GridDropLocation;
import com.vaadin.flow.component.grid.dnd.GridDropMode;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
-import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.provider.DataProvider;
+import com.vaadin.flow.data.provider.InMemoryDataProvider;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.data.renderer.TextRenderer;
-import com.vaadin.flow.data.selection.SelectionListener;
-import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.function.SerializableComparator;
-import com.vaadin.flow.function.SerializableFunction;
import com.vaadin.flow.shared.Registration;
-import java.io.Serializable;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -70,7 +63,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.NonNull;
-import org.apache.commons.lang3.StringUtils;
@SuppressWarnings("serial")
@JsModule(value = "./src/fc-twin-col-grid-auto-resize.js")
@@ -80,61 +72,14 @@
public class TwinColGrid extends VerticalLayout
implements HasValue>, Set>, HasComponents, HasSize {
- private static final class TwinColModel implements Serializable {
- final Grid grid;
- final Label columnLabel = new Label();
- final VerticalLayout layout;
- HeaderRow headerRow;
- boolean droppedInsideGrid = false;
- boolean allowReordering = false;
- Registration moveItemsByDoubleClick;
-
- TwinColModel(@NonNull Grid grid, String className) {
- this.grid = grid;
- layout = new VerticalLayout(columnLabel, grid);
-
- layout.setClassName(className);
- grid.setClassName("twincol-grid-items");
- columnLabel.setClassName("twincol-grid-label");
- }
-
- @SuppressWarnings("unchecked")
- ListDataProvider getDataProvider() {
- return (ListDataProvider) grid.getDataProvider();
- }
-
- Collection getItems() {
- return getDataProvider().getItems();
- }
-
- boolean isReorderingEnabled() {
- return allowReordering && grid.getSortOrder().isEmpty();
- }
- }
-
/** enumeration of all available orientation for TwinGolGrid component */
public enum Orientation {
- HORIZONTAL,
- VERTICAL,
- HORIZONTAL_REVERSE,
- VERTICAL_REVERSE;
+ HORIZONTAL, VERTICAL, HORIZONTAL_REVERSE, VERTICAL_REVERSE;
}
private final TwinColModel available;
- private final TwinColModel selection;
-
- /** @deprecated Use getAvailableGrid() */
- @Deprecated protected final Grid leftGrid;
-
- /** @deprecated Use getSelectionGrid() */
- @Deprecated protected final Grid rightGrid;
-
- /** @deprecated Use getAvailableGrid().getDataProvider() */
- @Deprecated protected ListDataProvider leftGridDataProvider;
-
- /** @deprecated Use getSelectionGrid().getDataProvider() */
- @Deprecated protected ListDataProvider rightGridDataProvider;
+ private final EagerTwinColModel selection;
private Label captionLabel;
@@ -148,8 +93,6 @@ public enum Orientation {
private Component buttonContainer;
- private Grid draggedGrid;
-
private Label fakeButtonContainerLabel = new Label();
private Orientation orientation = Orientation.HORIZONTAL;
@@ -158,10 +101,6 @@ public enum Orientation {
private boolean isFromClient = false;
- private static ListDataProvider emptyDataProvider() {
- return DataProvider.ofCollection(new LinkedHashSet<>());
- }
-
/** Constructs a new TwinColGrid with an empty {@link ListDataProvider}. */
public TwinColGrid() {
this(Grid::new);
@@ -171,44 +110,6 @@ public TwinColGrid() {
* Constructs a new empty TwinColGrid with caption
*
* @param caption the component caption
- * @deprecated Use {@link TwinColGrid#TwinColGrid()} and {{@link #setCaption(String)}
- */
- @Deprecated
- public TwinColGrid(String caption) {
- this(Grid::new);
- setCaption(caption);
- }
-
- /**
- * Constructs a new TwinColGrid with data provider for options.
- *
- * @param dataProvider the data provider, not {@code null}
- * @param caption the component caption
- * @deprecated Use {@link #TwinColGrid()} and {@link #setDataProvider(ListDataProvider)},
- * {@link #setCaption(String)}
- */
- @Deprecated
- public TwinColGrid(final ListDataProvider dataProvider, String caption) {
- this(Grid::new);
- setDataProvider(dataProvider);
- setCaption(caption);
- }
-
- /**
- * Constructs a new empty TwinColGrid, using the specified supplier for instantiating both grids.
- *
- * @param caption the component caption
- * @param gridSupplier a supplier for instantiating both grids
- * @deprecated Use {@link TwinColGrid#TwinColGrid(Supplier)} and {@link #setCaption(String)}
- */
- @Deprecated
- public TwinColGrid(String caption, Supplier> gridSupplier) {
- this(gridSupplier.get(), gridSupplier.get());
- setCaption(caption);
- }
-
- /**
- * Constructs a new empty TwinColGrid, using the specified supplier for instantiating both grids.
*
* @param gridSupplier a supplier for instantiating both grids
*/
@@ -216,22 +117,6 @@ public TwinColGrid(Supplier> gridSupplier) {
this(gridSupplier.get(), gridSupplier.get());
}
- /**
- * Constructs a new empty TwinColGrid, using the specified grids for each side.
- *
- * @param caption the component caption
- * @param availableGrid the grid that contains the available items
- * @param selectionGrid the grid that contains the selected items
- *
- * @deprecated Use {@link TwinColGrid#TwinColGrid(Grid, Grid)} and {@link #setCaption(String)}
- */
- @Deprecated
- public TwinColGrid(String caption, @NonNull Grid availableGrid,
- @NonNull Grid selectionGrid) {
- this(availableGrid, selectionGrid);
- setCaption(caption);
- }
-
/**
* Constructs a new empty TwinColGrid, using the specified grids for each side.
*
@@ -244,24 +129,23 @@ public TwinColGrid(@NonNull Grid availableGrid, @NonNull Grid selectionGri
throw new IllegalArgumentException("Grids must be different");
}
- available = new TwinColModel<>(availableGrid, "twincol-grid-available");
- selection = new TwinColModel<>(selectionGrid, "twincol-grid-selection");
+ if (!(selectionGrid.getDataProvider() instanceof InMemoryDataProvider)) {
+ throw new IllegalArgumentException("Selected Grid only support InMemoryDataProvider");
+ }
- leftGrid = available.grid;
- rightGrid = selection.grid;
+ available = createModel(availableGrid, "");
+ selection = new EagerTwinColModel<>(selectionGrid, "twincol-grid-selection");
setClassName("twincol-grid");
setMargin(false);
setPadding(false);
- setDataProvider(emptyDataProvider());
- rightGridDataProvider = DataProvider.ofCollection(new LinkedHashSet<>());
- getSelectionGrid().setDataProvider(rightGridDataProvider);
-
getAvailableGrid().setWidth("100%");
getSelectionGrid().setWidth("100%");
+ addAllButton
+ .setVisible(addAllButton.isVisible() && available.supportsAddAll());
addAllButton.addClickListener(
e -> {
List filteredItems = available.getDataProvider().withConfigurableFilter()
@@ -270,8 +154,7 @@ public TwinColGrid(@NonNull Grid availableGrid, @NonNull Grid selectionGri
});
addButton.addClickListener(
- e ->
- updateSelection(
+ e -> updateSelection(
new LinkedHashSet<>(getAvailableGrid().getSelectedItems()), new HashSet<>(), true));
removeButton.addClickListener(
@@ -279,26 +162,33 @@ public TwinColGrid(@NonNull Grid availableGrid, @NonNull Grid selectionGri
removeAllButton.addClickListener(
e -> {
- List filteredItems= selection.getDataProvider().withConfigurableFilter().fetch(new Query<>()).collect(Collectors.toList());
+ List filteredItems = selection.getDataProvider().withConfigurableFilter()
+ .fetch(new Query<>()).collect(Collectors.toList());
updateSelection(new HashSet<>(), new HashSet<>(filteredItems), true);
});
getElement().getStyle().set("display", "flex");
- forEachSide(
- side -> {
- side.grid.setSelectionMode(SelectionMode.MULTI);
- side.columnLabel.setVisible(false);
- side.layout.setSizeFull();
- side.layout.setMargin(false);
- side.layout.setPadding(false);
- side.layout.setSpacing(false);
- });
+ forEachSide(TwinColModel::init);
add(createContainerLayout());
setSizeUndefined();
}
+ /**
+ * Constructs a new TwinColGrid with the given options.
+ *
+ * @param options the options, cannot be {@code null}
+ */
+ public TwinColGrid(final Collection options) {
+ this();
+ setDataProvider(DataProvider.ofCollection(new LinkedHashSet<>(options)));
+ }
+
+ private TwinColModel createModel(@NonNull Grid grid, String className) {
+ return grid.getDataProvider().isInMemory() ? new EagerTwinColModel<>(grid, className)
+ : new LazyTwinColModel<>(grid, className);
+ }
/**
* Sets the component caption.
@@ -339,8 +229,8 @@ public TwinColGrid withOrientation(Orientation orientation) {
if (this.orientation != orientation) {
this.orientation = orientation;
updateContainerLayout();
- available.grid.getDataProvider().refreshAll();
- selection.grid.getDataProvider().refreshAll();
+ available.getGrid().getDataProvider().refreshAll();
+ selection.getGrid().getDataProvider().refreshAll();
}
return this;
}
@@ -350,7 +240,7 @@ public Orientation getOrientation() {
}
private void updateContainerLayout() {
- Component oldContainerComponent = available.layout.getParent().get();
+ Component oldContainerComponent = available.getLayout().getParent().get();
Component newContainerComponent = createContainerLayout();
replace(oldContainerComponent, newContainerComponent);
}
@@ -389,9 +279,9 @@ private HorizontalLayout createHorizontalContainer(boolean reverse) {
buttonContainer = getVerticalButtonContainer();
HorizontalLayout hl;
if (reverse) {
- hl = new HorizontalLayout(selection.layout, buttonContainer, available.layout);
+ hl = new HorizontalLayout(selection.getLayout(), buttonContainer, available.getLayout());
} else {
- hl = new HorizontalLayout(available.layout, buttonContainer, selection.layout);
+ hl = new HorizontalLayout(available.getLayout(), buttonContainer, selection.getLayout());
}
hl.getElement().getStyle().set("min-height", "0px");
hl.getElement().getStyle().set("flex", "1 1 0px");
@@ -404,9 +294,9 @@ private VerticalLayout createVerticalContainer(boolean reverse) {
buttonContainer = getHorizontalButtonContainer();
VerticalLayout vl;
if (reverse) {
- vl = new VerticalLayout(selection.layout, buttonContainer, available.layout);
+ vl = new VerticalLayout(selection.getLayout(), buttonContainer, available.getLayout());
} else {
- vl = new VerticalLayout(available.layout, buttonContainer, selection.layout);
+ vl = new VerticalLayout(available.getLayout(), buttonContainer, selection.getLayout());
}
vl.getElement().getStyle().set("min-width", "0px");
vl.getElement().getStyle().set("flex", "1 1 0px");
@@ -440,34 +330,12 @@ private HorizontalLayout getHorizontalButtonContainer() {
/** Return the grid that contains the available items. */
public Grid getAvailableGrid() {
- return available.grid;
+ return available.getGrid();
}
/** Return the grid that contains the selected items. */
public Grid getSelectionGrid() {
- return selection.grid;
- }
-
- /**
- * Return the left grid component.
- *
- * @deprecated Use {@link #getAvailableGrid()}. Depending on the orientation, the "left grid" may
- * not be located at the left side.
- */
- @Deprecated
- public Grid getLeftGrid() {
- return leftGrid;
- }
-
- /**
- * Return the right grid component.
- *
- * @deprecated Use {@link #getSelectionGrid()}. Depending on the orientation, the "right grid" may
- * not be located at the right side.
- */
- @Deprecated
- public Grid getRightGrid() {
- return rightGrid;
+ return selection.getGrid();
}
private void forEachSide(Consumer> consumer) {
@@ -476,91 +344,17 @@ private void forEachSide(Consumer> consumer) {
}
public final void forEachGrid(Consumer> consumer) {
- consumer.accept(available.grid);
- consumer.accept(selection.grid);
- }
-
- public void setItems(Collection items) {
- setDataProvider(DataProvider.ofCollection(items));
- }
-
- public void setItems(Stream items) {
- setDataProvider(DataProvider.fromStream(items));
- }
-
- /** @deprecated Use {@code getAvailableGrid().setClassName(classname)} */
- @Deprecated
- public void setLeftGridClassName(String classname) {
- getAvailableGrid().setClassName(classname);
- }
-
- /** @deprecated Use {@code getAvailableGrid().addClassName(classname)} */
- @Deprecated
- public void addLeftGridClassName(String classname) {
- getAvailableGrid().addClassName(classname);
- }
-
- /** @deprecated Use {@code getAvailableGrid().removeClassName(classname)} */
- @Deprecated
- public void removeLeftGridClassName(String classname) {
- getAvailableGrid().removeClassName(classname);
- }
-
- /** @deprecated Use {@code getSelectionGrid().setClassName(classname)} */
- @Deprecated
- public void setRightGridClassName(String classname) {
- getSelectionGrid().setClassName(classname);
- }
-
- /** @deprecated Use {@code getSelectionGrid().addClassName(classname)} */
- @Deprecated
- public void addRightGridClassName(String classname) {
- getSelectionGrid().addClassName(classname);
- }
-
- /** @deprecated Use {@code getSelectionGrid().removeClassName(classname)} */
- @Deprecated
- public void removeRightGridClassName(String classname) {
- getSelectionGrid().removeClassName(classname);
- }
-
- public void clearAll() {
- updateSelection(new HashSet<>(), new HashSet<>(selection.getItems()), false);
+ consumer.accept(available.getGrid());
+ consumer.accept(selection.getGrid());
}
private void setDataProvider(ListDataProvider dataProvider) {
- leftGridDataProvider = dataProvider;
getAvailableGrid().setDataProvider(dataProvider);
if (selection.getDataProvider() != null) {
- selection.getItems().clear();
- selection.getDataProvider().refreshAll();
+ selection.clear();
}
}
- /**
- * Constructs a new TwinColGrid with the given options.
- *
- * @param options the options, cannot be {@code null}
- */
- public TwinColGrid(final Collection options) {
- this();
- setDataProvider(DataProvider.ofCollection(new LinkedHashSet<>(options)));
- }
-
- /**
- * Constructs a new TwinColGrid with caption and the given options.
- *
- * @param caption the caption to set, can be {@code null}
- * @param options the options, cannot be {@code null}
- *
- * @deprecated Use {@link #TwinColGrid(Collection)} and {{@link #setCaption(String)}
- */
- @Deprecated
- public TwinColGrid(final Collection options, final String caption) {
- this(options);
- setCaption(caption);
- }
-
/**
* Sets the text shown above the grid with the available items. {@code null} clears the caption.
*
@@ -568,8 +362,8 @@ public TwinColGrid(final Collection options, final String caption) {
* @return this instance
*/
public TwinColGrid withAvailableGridCaption(final String caption) {
- available.columnLabel.setText(caption);
- available.columnLabel.setVisible(true);
+ available.getColumnLabel().setText(caption);
+ available.getColumnLabel().setVisible(true);
fakeButtonContainerLabel.setVisible(true);
return this;
}
@@ -581,39 +375,15 @@ public TwinColGrid withAvailableGridCaption(final String caption) {
* @return this instance
*/
public TwinColGrid withSelectionGridCaption(final String caption) {
- selection.columnLabel.setText(caption);
- selection.columnLabel.setVisible(true);
+ selection.getColumnLabel().setText(caption);
+ selection.getColumnLabel().setVisible(true);
fakeButtonContainerLabel.setVisible(true);
return this;
}
/**
- * Sets the text shown above the grid with the available items. {@code null} clears the caption.
- *
- * @param caption The text to show, {@code null} to clear
- * @return this instance
- * @deprecated Use {@link #withAvailableGridCaption(String)}
- */
- @Deprecated
- public TwinColGrid withRightColumnCaption(final String caption) {
- return withSelectionGridCaption(caption);
- }
-
- /**
- * Sets the text shown above the grid with the available items. {@code null} clears the caption.
- *
- * @param caption The text to show, {@code null} to clear
- * @return this instance
- * @deprecated Use {@link #withSelectionGridCaption(String)}
- */
- @Deprecated
- public TwinColGrid withLeftColumnCaption(final String caption) {
- return withAvailableGridCaption(caption);
- }
-
- /**
- * Adds a new text column to this {@link Grid} with a value provider. The column will use a {@link
- * TextRenderer}. The value is converted to a String using the provided {@code
+ * Adds a new text column to this {@link Grid} with a value provider. The column will use a
+ * {@link TextRenderer}. The value is converted to a String using the provided {@code
* itemLabelGenerator}.
*
* @param itemLabelGenerator the value provider
@@ -642,10 +412,10 @@ public TwinColGrid addSortableColumn(
Comparator comparator,
final String header) {
forEachGrid(grid -> grid
- .addColumn(new TextRenderer<>(itemLabelGenerator))
- .setHeader(header)
- .setComparator(comparator)
- .setSortable(true));
+ .addColumn(new TextRenderer<>(itemLabelGenerator))
+ .setHeader(header)
+ .setComparator(comparator)
+ .setSortable(true));
return this;
}
@@ -666,11 +436,11 @@ public TwinColGrid addSortableColumn(
final String header,
final String key) {
forEachGrid(grid -> grid
- .addColumn(new TextRenderer<>(itemLabelGenerator))
- .setHeader(header)
- .setComparator(comparator)
- .setSortable(true)
- .setKey(key));
+ .addColumn(new TextRenderer<>(itemLabelGenerator))
+ .setHeader(header)
+ .setComparator(comparator)
+ .setSortable(true)
+ .setKey(key));
return this;
}
@@ -730,7 +500,7 @@ public TwinColGrid withDragAndDropSupport() {
* @return The text shown or {@code null} if not set.
*/
public String getAvailableGridCaption() {
- return available.columnLabel.getText();
+ return available.getColumnLabel().getText();
}
/**
@@ -739,7 +509,7 @@ public String getAvailableGridCaption() {
* @return The text shown or {@code null} if not set.
*/
public String getSelectionGridCaption() {
- return selection.columnLabel.getText();
+ return selection.getColumnLabel().getText();
}
/**
@@ -800,7 +570,7 @@ public Set getValue() {
*/
C collectValue(Collector collector) {
Stream stream = selection.getItems().stream();
- SerializableComparator comparator = createSortingComparator(selection.grid);
+ SerializableComparator comparator = createSortingComparator(selection.getGrid());
if (comparator != null) {
return stream.sorted(comparator).collect(collector);
} else {
@@ -830,7 +600,8 @@ public Registration addValueChangeListener(
.addDataProviderListener(
e -> {
ComponentValueChangeEvent, Set> e2 =
- new ComponentValueChangeEvent<>(TwinColGrid.this, TwinColGrid.this, null, isFromClient);
+ new ComponentValueChangeEvent<>(TwinColGrid.this, TwinColGrid.this, null,
+ isFromClient);
listener.valueChanged(e2);
});
}
@@ -860,105 +631,84 @@ public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) {
getElement().setAttribute("required", requiredIndicatorVisible);
}
- private void updateSelection(final Set addedItems, final Set removedItems, boolean isFromClient) {
+ private void updateSelection(final Set addedItems, final Set removedItems,
+ boolean isFromClient) {
this.isFromClient = isFromClient;
- available.getItems().addAll(removedItems);
- available.getItems().removeAll(addedItems);
+ available.addAll(removedItems);
+ available.removeAll(addedItems);
- selection.getItems().addAll(addedItems);
- selection.getItems().removeAll(removedItems);
+ selection.addAll(addedItems);
+ selection.removeAll(removedItems);
forEachGrid(grid -> {
grid.getDataProvider().refreshAll();
grid.getSelectionModel().deselectAll();
- });
+ });
}
- private void configDragAndDrop(
- final TwinColModel sourceModel, final TwinColModel targetModel) {
+ private void configDragAndDrop(TwinColModel sourceModel, TwinColModel targetModel) {
+ Set draggedItems = new LinkedHashSet<>();
- final Set draggedItems = new LinkedHashSet<>();
+ Grid draggedGrid = sourceModel.getGrid();
- sourceModel.grid.setRowsDraggable(true);
- sourceModel.grid.addDragStartListener(
+ sourceModel.getGrid().setRowsDraggable(true);
+ sourceModel.getGrid().addDragStartListener(
event -> {
- draggedGrid = sourceModel.grid;
-
- if (!(sourceModel.grid.getSelectionModel() instanceof GridNoneSelectionModel)) {
+ if (!(sourceModel.getGrid().getSelectionModel() instanceof GridNoneSelectionModel)) {
draggedItems.addAll(event.getDraggedItems());
}
- sourceModel.grid
+ sourceModel.getGrid()
.setDropMode(sourceModel.isReorderingEnabled() ? GridDropMode.BETWEEN : null);
- targetModel.grid.setDropMode(
+ targetModel.getGrid().setDropMode(
targetModel.isReorderingEnabled() ? GridDropMode.BETWEEN : GridDropMode.ON_GRID);
});
- sourceModel.grid.addDragEndListener(
+ sourceModel.getGrid().addDragEndListener(
event -> {
- if (targetModel.droppedInsideGrid
- && sourceModel.grid == draggedGrid
+ if (targetModel.isDroppedInsideGrid()
+ && sourceModel.getGrid() == draggedGrid
&& !draggedItems.isEmpty()) {
- final ListDataProvider dragGridSourceDataProvider = sourceModel.getDataProvider();
+ sourceModel.removeAll(draggedItems);
- dragGridSourceDataProvider.getItems().removeAll(draggedItems);
- dragGridSourceDataProvider.refreshAll();
-
- targetModel.droppedInsideGrid = false;
+ targetModel.setDroppedInsideGrid(false);
draggedItems.clear();
- sourceModel.grid.deselectAll();
-
- sourceModel.grid.setDropMode(null);
- targetModel.grid.setDropMode(null);
+ sourceModel.getGrid().deselectAll();
+ sourceModel.getDataProvider().refreshAll();
+ sourceModel.getGrid().setDropMode(null);
+ targetModel.getGrid().setDropMode(null);
}
draggedItems.clear();
});
- targetModel.grid.addDropListener(
+ targetModel.getGrid().addDropListener(
event -> {
if (!draggedItems.isEmpty()) {
isFromClient = true;
- targetModel.droppedInsideGrid = true;
+ targetModel.setDroppedInsideGrid(true);
T dropOverItem = event.getDropTargetItem().orElse(null);
- addItems(targetModel, draggedItems, dropOverItem, event.getDropLocation());
+ targetModel.addItems(draggedItems, dropOverItem, event.getDropLocation());
}
});
- sourceModel.grid.addDropListener(event -> {
+ sourceModel.getGrid().addDropListener(event -> {
event.getDropTargetItem().ifPresent(dropOverItem -> {
if (sourceModel.isReorderingEnabled()
&& event.getSource() == draggedGrid
&& !draggedItems.contains(dropOverItem)
&& !draggedItems.isEmpty()) {
isFromClient = true;
- sourceModel.getItems().removeAll(draggedItems);
- addItems(sourceModel, draggedItems, dropOverItem, event.getDropLocation());
+ sourceModel.removeAll(draggedItems);
+ sourceModel.addItems(draggedItems, dropOverItem, event.getDropLocation());
draggedItems.clear();
- draggedGrid = null;
}
});
});
}
- private void addItems(TwinColModel model, Collection draggedItems,
- T dropOverItem, GridDropLocation dropLocation) {
- if (dropOverItem != null) {
- Collection collection = model.getItems();
- List list = new ArrayList<>(collection);
- int dropIndex = list.indexOf(dropOverItem) + (dropLocation == GridDropLocation.BELOW ? 1 : 0);
- list.addAll(dropIndex, draggedItems);
- model.getItems().clear();
- model.getItems().addAll(list);
- model.getDataProvider().refreshAll();
- } else {
- model.getItems().addAll(draggedItems);
- model.getDataProvider().refreshAll();
- }
- }
-
/** Allow drag-and-drop within the selection grid. */
public TwinColGrid withSelectionGridReordering() {
setSelectionGridReorderingAllowed(true);
@@ -967,83 +717,30 @@ public TwinColGrid withSelectionGridReordering() {
/** Configure whether drag-and-drop within the selection grid is allowed. */
public void setSelectionGridReorderingAllowed(boolean value) {
- selection.allowReordering = value;
+ selection.setAllowReordering(value);
}
/** Return whether drag-and-drop within the selection grid is allowed. */
public boolean isSelectionGridReorderingAllowed() {
- return selection.allowReordering;
- }
-
- /** @deprecated Use {@code getAvailableGrid().addSelectionListener(listener);} */
- @Deprecated
- public void addLeftGridSelectionListener(SelectionListener, T> listener) {
- getAvailableGrid().addSelectionListener(listener);
+ return selection.isAllowReordering();
}
- /** @deprecated Use {@code getSelectionGrid().addSelectionListener(listener);} */
- @Deprecated
- public void addRightGridSelectionListener(SelectionListener, T> listener) {
- getSelectionGrid().addSelectionListener(listener);
- }
-
- public TwinColGrid addFilterableColumn(
- final ItemLabelGenerator itemLabelGenerator,
- SerializableFunction filterableValue,
- final String header,
- String filterPlaceholder,
- boolean enableClearButton, String key) {
- forEachSide(
- side -> {
- Column column =
- side.grid.addColumn(new TextRenderer<>(itemLabelGenerator)).setHeader(header);
-
- Optional.ofNullable(key).ifPresent(column::setKey);
-
- TextField filterTF = new TextField();
- filterTF.setClearButtonVisible(enableClearButton);
-
- filterTF.addValueChangeListener(
- event ->
- side.getDataProvider()
- .addFilter(
- filterableEntity ->
- StringUtils.containsIgnoreCase(
- filterableValue.apply(filterableEntity), filterTF.getValue())));
-
- if (side.headerRow == null) {
- side.headerRow = side.grid.appendHeaderRow();
- }
-
- side.headerRow.getCell(column).setComponent(filterTF);
- filterTF.setValueChangeMode(ValueChangeMode.EAGER);
- filterTF.setSizeFull();
- filterTF.setPlaceholder(filterPlaceholder);
- });
-
+ public TwinColGrid addFilterableColumn(LazyFilterableColumn filter) {
+ if (filter.lazyFilterAttribute() != null) {
+ availableAsLazy().addFilterableColumn(filter);
+ }
+ if (filter.eagerFilterCondition() != null) {
+ selection.addFilterableColumn(filter.asEager());
+ }
return this;
}
- public TwinColGrid addFilterableColumn(
- final ItemLabelGenerator itemLabelGenerator,
- final String header,
- String filterPlaceholder,
- boolean enableClearButton) {
- return addFilterableColumn(itemLabelGenerator, itemLabelGenerator, header, filterPlaceholder,
- enableClearButton, null);
- }
-
- public TwinColGrid addFilterableColumn(ItemLabelGenerator itemLabelGenerator,
- SerializableFunction filterableValue, String header, String filterPlaceholder,
- boolean enableClearButton) {
- return addFilterableColumn(itemLabelGenerator, filterableValue, header, filterPlaceholder,
- enableClearButton, null);
- }
-
- public TwinColGrid addFilterableColumn(ItemLabelGenerator itemLabelGenerator, String header,
- String filterPlaceholder, boolean enableClearButton, String key) {
- return addFilterableColumn(itemLabelGenerator, itemLabelGenerator, header, filterPlaceholder,
- enableClearButton, key);
+ public TwinColGrid addFilterableColumn(EagerFilterableColumn filter) {
+ if (filter.filterCondition() != null) {
+ availableAsEager().addFilterableColumn(filter);
+ selection.addFilterableColumn(filter);
+ }
+ return this;
}
public TwinColGrid selectRowOnClick() {
@@ -1051,14 +748,14 @@ public TwinColGrid selectRowOnClick() {
grid.addClassName("hide-selector-col");
grid.addItemClickListener(
- c -> {
+ c -> {
if (grid.getSelectedItems().contains(c.getItem())) {
grid.deselect(c.getItem());
- } else {
+ } else {
grid.select(c.getItem());
- }
- });
- });
+ }
+ });
+ });
return this;
}
@@ -1107,8 +804,8 @@ public void setAutoResize(boolean autoResize) {
*/
public void setMoveItemsByDoubleClick(boolean value) {
forEachSide(side -> {
- if (value && side.moveItemsByDoubleClick == null) {
- side.moveItemsByDoubleClick = side.grid.addItemDoubleClickListener(ev -> {
+ if (value && side.getMoveItemsByDoubleClick() == null) {
+ side.addItemDoubleClickListener(ev -> {
Set item = Collections.singleton(ev.getItem());
if (side == available) {
updateSelection(item, Collections.emptySet(), true);
@@ -1118,9 +815,8 @@ public void setMoveItemsByDoubleClick(boolean value) {
}
});
}
- if (!value && side.moveItemsByDoubleClick != null) {
- side.moveItemsByDoubleClick.remove();
- side.moveItemsByDoubleClick = null;
+ if (!value) {
+ side.removeItemDoubleClickListener();
}
});
}
@@ -1134,4 +830,22 @@ private void updateOrientationOnResize(int width, int height) {
}
}
+ public > void setLazyFilter(F filter) {
+ availableAsLazy().setFilter(filter);
+ }
+
+ private LazyTwinColModel availableAsLazy() {
+ if (!TwinColModelMode.LAZY.equals(available.getMode())) {
+ throw new IllegalStateException("Available model is not in lazy mode");
+ }
+ return (LazyTwinColModel) available;
+ }
+
+ private EagerTwinColModel availableAsEager() {
+ if (!TwinColModelMode.EAGER.equals(available.getMode())) {
+ throw new IllegalStateException("Available model is not in eager mode");
+ }
+ return (EagerTwinColModel) available;
+ }
+
}
diff --git a/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/TwinColModel.java b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/TwinColModel.java
new file mode 100644
index 0000000..fc7b026
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/addons/twincolgrid/TwinColModel.java
@@ -0,0 +1,71 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import com.vaadin.flow.component.ComponentEventListener;
+import com.vaadin.flow.component.grid.Grid;
+import com.vaadin.flow.component.grid.Grid.SelectionMode;
+import com.vaadin.flow.component.grid.HeaderRow;
+import com.vaadin.flow.component.grid.ItemDoubleClickEvent;
+import com.vaadin.flow.component.grid.dnd.GridDropLocation;
+import com.vaadin.flow.component.html.Label;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.data.provider.DataProvider;
+import com.vaadin.flow.shared.Registration;
+import java.io.Serializable;
+import java.util.Collection;
+
+interface TwinColModel extends Serializable {
+
+ enum TwinColModelMode {
+ EAGER, LAZY;
+ }
+
+ TwinColModelMode getMode();
+
+ > D getDataProvider();
+
+ boolean isReorderingEnabled();
+
+ Grid getGrid();
+
+ Label getColumnLabel();
+
+ boolean isDroppedInsideGrid();
+
+ void setDroppedInsideGrid(boolean droppedInsideGrid);
+
+ void setAllowReordering(boolean allowReordering);
+
+ boolean isAllowReordering();
+
+ HeaderRow getHeaderRow();
+
+ void setHeaderRow(HeaderRow headerRow);
+
+ Registration getMoveItemsByDoubleClick();
+
+ Registration addItemDoubleClickListener(ComponentEventListener> listener);
+
+ void removeItemDoubleClickListener();
+
+ VerticalLayout getLayout();
+
+ default void init() {
+ getGrid().setSelectionMode(SelectionMode.MULTI);
+ getColumnLabel().setVisible(false);
+ getLayout().setSizeFull();
+ getLayout().setMargin(false);
+ getLayout().setPadding(false);
+ getLayout().setSpacing(false);
+ }
+
+ void addAll(Collection items);
+
+ void removeAll(Collection items);
+
+ void clear();
+
+ void addItems(Collection draggedItems, T dropOverItem, GridDropLocation dropLocation);
+
+ boolean supportsAddAll();
+
+}
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/Book.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/Book.java
index 66719b7..cbb3b70 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/Book.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/Book.java
@@ -19,37 +19,19 @@
*/
package com.flowingcode.vaadin.addons.twincolgrid;
-import java.util.Objects;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+@RequiredArgsConstructor
+@Getter
+@EqualsAndHashCode
public class Book {
private final String isbn;
private final String title;
- public Book(final String isbn, final String title) {
- this.isbn = isbn;
- this.title = title;
- }
-
- public String getIsbn() {
- return isbn;
- }
-
- public String getTitle() {
- return title;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(isbn, title);
- }
-
- @Override
- public boolean equals(final Object obj) {
- return ObjectUtils.equals(this, (Book) obj, Book::getIsbn, Book::getTitle);
- }
-
@Override
public String toString() {
return "[Book " + isbn + " - " + title + "]";
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BookFilter.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BookFilter.java
new file mode 100644
index 0000000..d758ceb
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BookFilter.java
@@ -0,0 +1,14 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class BookFilter extends LazyFilter {
+
+ private String isbn;
+
+ private String title;
+
+}
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BookService.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BookService.java
new file mode 100644
index 0000000..cf13d74
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BookService.java
@@ -0,0 +1,59 @@
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import com.vaadin.flow.function.SerializableBiPredicate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+
+public class BookService {
+
+ public static final int BOOKS_COUNT = 500;
+
+ private final List availableBooks = new ArrayList<>();
+
+ private final SerializableBiPredicate isbnFilterPredicate =
+ (book, filter) -> filter == null ? true
+ : StringUtils.containsIgnoreCase(book.getIsbn(), filter);
+
+ private final SerializableBiPredicate titleFilterPredicate =
+ (book, filter) -> filter == null ? true
+ : StringUtils.containsIgnoreCase(book.getTitle(), filter);
+
+
+ public BookService() {
+ initializeData();
+ }
+
+ private void initializeData() {
+ for (int i = 0; i <= BOOKS_COUNT; i++) {
+ availableBooks.add(new Book(RandomStringUtils.randomNumeric(8), "Vaadin Recipes " + i));
+ }
+ }
+
+ public Stream fetch(int offset, int limit, BookFilter bookFilter) {
+ return availableBooks.stream()
+ .filter(book -> !bookFilter.getExcludedItems().contains(book))
+ .filter(book -> isbnFilterPredicate.test(book, bookFilter.getIsbn()))
+ .filter(book -> titleFilterPredicate.test(book, bookFilter.getTitle()))
+ .collect(Collectors.toList())
+ .subList(Math.min(availableBooks.size() - 1, offset),
+ Math.min(availableBooks.size(), offset + limit))
+ .stream();
+ }
+
+ public int count(BookFilter bookFilter) {
+ return (int) availableBooks.stream()
+ .filter(book -> !bookFilter.getExcludedItems().contains(book))
+ .filter(book -> isbnFilterPredicate.test(book, bookFilter.getIsbn()))
+ .filter(book -> titleFilterPredicate.test(book, bookFilter.getTitle()))
+ .count();
+ }
+
+ public Book getAny() {
+ int index = (int) (Math.random() * (BookService.BOOKS_COUNT - 1));
+ return availableBooks.get(index);
+ }
+}
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BoundDemo.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BoundDemo.java
index 6f6fd09..41b46d2 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BoundDemo.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/BoundDemo.java
@@ -47,8 +47,7 @@ public BoundDemo() {
// Binded
final TwinColGrid twinColGrid =
- new TwinColGrid<>(
- availableBooks, "TwinColGrid demo with Binder and row select without checkbox")
+ new TwinColGrid<>(availableBooks)
.addSortableColumn(Book::getIsbn, Comparator.comparing(Book::getIsbn), "ISBN")
.addSortableColumn(Book::getTitle, Comparator.comparing(Book::getTitle), "Title")
.withAvailableGridCaption("Available books")
@@ -57,6 +56,8 @@ public BoundDemo() {
.withSizeFull()
.selectRowOnClick();
+ twinColGrid.setCaption("TwinColGrid demo with Binder and row select without checkbox");
+
final Binder binder = new Binder<>();
binder.forField(twinColGrid.asList()).asRequired().bind(Library::getBooks, Library::setBooks);
binder.setBean(library);
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/DoubleClickDemo.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/DoubleClickDemo.java
index e6bc74f..3617945 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/DoubleClickDemo.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/DoubleClickDemo.java
@@ -43,7 +43,7 @@ public DoubleClickDemo() {
initializeData();
final TwinColGrid twinColGrid =
- new TwinColGrid<>(availableBooks, null)
+ new TwinColGrid<>(availableBooks)
.addSortableColumn(Book::getIsbn, Comparator.comparing(Book::getIsbn), "ISBN")
.addSortableColumn(Book::getTitle, Comparator.comparing(Book::getTitle), "Title")
.withAvailableGridCaption("Available books")
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/FilterableDemo.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/FilterableDemo.java
index 779dae3..a8c2f83 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/FilterableDemo.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/FilterableDemo.java
@@ -28,6 +28,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
@SuppressWarnings("serial")
@PageTitle("Filterable")
@@ -41,14 +42,26 @@ public class FilterableDemo extends VerticalLayout {
public FilterableDemo() {
initializeData();
+ EagerFilterableColumn isbnFilterableColumn =
+ new EagerFilterableColumn<>(Book::getIsbn, "ISBN", "ISBN Filter", true,
+ (item, filter) -> StringUtils.isBlank(filter)
+ || StringUtils.containsIgnoreCase(item.getIsbn(), filter));
+
+ EagerFilterableColumn titleFilterableColumn =
+ new EagerFilterableColumn<>(Book::getTitle, "Title", "Title Filter", true,
+ (item, filter) -> StringUtils.isBlank(filter)
+ || StringUtils.containsIgnoreCase(item.getTitle(), filter));
+
final TwinColGrid twinColGrid =
- new TwinColGrid<>(availableBooks, "TwinColGrid demo with filtering support")
- .addFilterableColumn(Book::getIsbn, Book::getIsbn, "ISBN", "ISBN Filter", true)
- .addFilterableColumn(Book::getTitle, "Title", "Title filter", false)
+ new TwinColGrid<>(availableBooks)
+ .addFilterableColumn(isbnFilterableColumn)
+ .addFilterableColumn(titleFilterableColumn)
.withAvailableGridCaption("Available books")
.withSelectionGridCaption("Added books")
.withoutAddAllButton()
.withSizeFull();
+
+ twinColGrid.setCaption("TwinColGrid demo with filtering support");
twinColGrid.setValue(selectedBooks);
add(twinColGrid);
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilterableDemo.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilterableDemo.java
new file mode 100644
index 0000000..282f1df
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/LazyFilterableDemo.java
@@ -0,0 +1,107 @@
+/*-
+ * #%L
+ * TwinColGrid add-on
+ * %%
+ * Copyright (C) 2017 - 2022 Flowing Code
+ * %%
+ * 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.
+ * #L%
+ */
+
+package com.flowingcode.vaadin.addons.twincolgrid;
+
+import com.flowingcode.vaadin.addons.demo.DemoSource;
+import com.vaadin.flow.component.button.Button;
+import com.vaadin.flow.component.grid.Grid;
+import com.vaadin.flow.component.notification.Notification;
+import com.vaadin.flow.component.notification.Notification.Position;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.data.binder.Binder;
+import com.vaadin.flow.data.provider.DataProvider;
+import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.Route;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+
+@SuppressWarnings("serial")
+@PageTitle("Lazy Filterable")
+@DemoSource
+@Route(value = "twincolgrid/lazyfilterable", layout = TwincolDemoView.class)
+public class LazyFilterableDemo extends VerticalLayout {
+
+ public LazyFilterableDemo() {
+ BookService bookService = new BookService();
+
+ DataProvider availableDataProvider =
+ DataProvider.fromFilteringCallbacks(
+ query -> bookService.fetch(query.getOffset(), query.getLimit(),
+ query.getFilter().orElseGet(BookFilter::new)),
+ query -> bookService.count(query.getFilter().orElseGet(BookFilter::new)));
+
+ Grid availableGrid = new Grid<>();
+ availableGrid.setDataProvider(availableDataProvider);
+
+ Grid selectionGrid = new Grid<>();
+ BookFilter bookFilter = new BookFilter();
+
+ LazyFilterableColumn isbnFilterableColumn =
+ new LazyFilterableColumn<>(Book::getIsbn, "ISBN", "ISBN Filter", true)
+ .lazyFilterAttribute(bookFilter::setIsbn)
+ .eagerFilterCondition((item, filter) -> StringUtils.isBlank(filter)
+ || StringUtils.containsIgnoreCase(item.getIsbn(), filter));
+
+ LazyFilterableColumn titleFilterableColumn =
+ new LazyFilterableColumn<>(Book::getTitle, "Title", "Title Filter", true)
+ .lazyFilterAttribute(bookFilter::setTitle)
+ .eagerFilterCondition((item, filter) -> StringUtils.isBlank(filter)
+ || StringUtils.containsIgnoreCase(item.getTitle(), filter));
+
+ final TwinColGrid twinColGrid =
+ new TwinColGrid<>(availableGrid, selectionGrid)
+ .addFilterableColumn(isbnFilterableColumn)
+ .addFilterableColumn(titleFilterableColumn)
+ .withAvailableGridCaption("Available books")
+ .withSelectionGridCaption("Added books")
+ .withDragAndDropSupport()
+ .withSizeFull();
+
+ twinColGrid.setCaption("TwinColGrid demo with lazy loading, binder and drag and drop support");
+ twinColGrid.setMoveItemsByDoubleClick(true);
+ twinColGrid.setLazyFilter(bookFilter);
+
+ Set selectedBooks = new HashSet<>();
+ selectedBooks.add(bookService.getAny());
+ selectedBooks.add(bookService.getAny());
+ selectedBooks.add(bookService.getAny());
+
+ Binder binder = new Binder<>();
+ binder.forField(twinColGrid.asList()).asRequired().bind(Library::getBooks, Library::setBooks);
+ Library library = new Library("Public Library", new ArrayList<>(selectedBooks));
+ binder.setBean(library);
+
+ add(twinColGrid);
+ add(new Button("Get values", ev -> {
+ binder.getBean().getBooks()
+ .forEach(book -> Notification.show(book.getTitle(), 3000, Position.BOTTOM_START));
+ }));
+
+ add(new Button("Clear TwinColGrid", ev -> {
+ twinColGrid.clear();
+ }));
+
+ setSizeFull();
+ }
+
+}
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/OrientationDemo.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/OrientationDemo.java
index c8b4a01..625efa6 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/OrientationDemo.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/OrientationDemo.java
@@ -45,7 +45,7 @@ public OrientationDemo() {
initializeData();
final TwinColGrid twinColGrid =
- new TwinColGrid<>(availableBooks, null)
+ new TwinColGrid<>(availableBooks)
.addSortableColumn(Book::getIsbn, Comparator.comparing(Book::getIsbn), "ISBN")
.addSortableColumn(Book::getTitle, Comparator.comparing(Book::getTitle), "Title")
.withAvailableGridCaption("Available books")
diff --git a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/TwincolDemoView.java b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/TwincolDemoView.java
index b882e5d..e166e59 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/TwincolDemoView.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/twincolgrid/TwincolDemoView.java
@@ -37,6 +37,7 @@ public class TwincolDemoView extends TabbedDemo {
public TwincolDemoView() {
addDemo(DragAndDropDemo.class);
addDemo(FilterableDemo.class);
+ addDemo(LazyFilterableDemo.class);
addDemo(BoundDemo.class);
addDemo(OrientationDemo.class);
addDemo(DoubleClickDemo.class);