diff options
author | Adam Wagner <wbadam@users.noreply.github.com> | 2017-04-07 09:52:06 +0300 |
---|---|---|
committer | Henri Sara <henri.sara@gmail.com> | 2017-04-12 14:58:11 +0300 |
commit | a773c8c7b365e4041db87b5d9ddaad0bdc244a91 (patch) | |
tree | 422ce4db5bde123c7dc775d70e49e259c17b2dce | |
parent | 659313e8c35e68d14fe2599b9bbb4dbba35fb4a3 (diff) | |
download | vaadin-framework-a773c8c7b365e4041db87b5d9ddaad0bdc244a91.tar.gz vaadin-framework-a773c8c7b365e4041db87b5d9ddaad0bdc244a91.zip |
Make it possible to drop things between Grid rows (#8979)
Fixes #8401
9 files changed, 284 insertions, 49 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetExtensionConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetExtensionConnector.java index 9c25f5acb4..dacc8316ad 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetExtensionConnector.java @@ -15,18 +15,21 @@ */ package com.vaadin.client.connectors.grid; -import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.TableRowElement; +import com.google.gwt.user.client.Window; import com.vaadin.client.ServerConnector; +import com.vaadin.client.WidgetUtil; import com.vaadin.client.extensions.DropTargetExtensionConnector; import com.vaadin.client.widget.escalator.RowContainer; import com.vaadin.client.widgets.Escalator; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.DropLocation; +import com.vaadin.shared.ui.grid.DropMode; import com.vaadin.shared.ui.grid.GridDropTargetExtensionRpc; import com.vaadin.shared.ui.grid.GridDropTargetExtensionState; import com.vaadin.shared.ui.grid.GridState; @@ -46,6 +49,21 @@ import elemental.json.JsonObject; public class GridDropTargetExtensionConnector extends DropTargetExtensionConnector { + // Drag over class name suffixes + private final static String CLASS_SUFFIX_BEFORE = "-before"; + private final static String CLASS_SUFFIX_AFTER = "-after"; + + // Drag over class names + private final static String CLASS_DRAG_OVER_BEFORE = + CLASS_DRAG_OVER + CLASS_SUFFIX_BEFORE; + private final static String CLASS_DRAG_OVER_AFTER = + CLASS_DRAG_OVER + CLASS_SUFFIX_AFTER; + + /** + * Current drag over class name + */ + private String dragOverClassName; + private GridConnector gridConnector; @Override @@ -60,15 +78,19 @@ public class GridDropTargetExtensionConnector extends Event dropEvent) { String rowKey = null; + DropLocation dropLocation = null; + Optional<TableRowElement> targetRow = getTargetRow( (Element) dropEvent.getTarget()); if (targetRow.isPresent()) { rowKey = getRowData(targetRow.get()) .getString(GridState.JSONKEY_ROWKEY); + dropLocation = getDropLocation(targetRow.get(), + (NativeEvent) dropEvent); } getRpcProxy(GridDropTargetExtensionRpc.class) - .drop(dataTransferText, rowKey); + .drop(dataTransferText, rowKey, dropLocation); } private JsonObject getRowData(TableRowElement row) { @@ -77,16 +99,71 @@ public class GridDropTargetExtensionConnector extends return gridConnector.getDataSource().getRow(rowIndex); } + /** + * Returns the location of the event within the row. + */ + private DropLocation getDropLocation(Element target, NativeEvent event) { + if (getState().dropMode == DropMode.BETWEEN) { + if (getRelativeY(target, event) < (target.getOffsetHeight() / 2)) { + return DropLocation.ABOVE; + } else { + return DropLocation.BELOW; + } + } + return DropLocation.ON_TOP; + } + + private int getRelativeY(Element element, NativeEvent event) { + int relativeTop = element.getAbsoluteTop() - Window.getScrollTop(); + return WidgetUtil.getTouchOrMouseClientY(event) - relativeTop; + } + @Override - protected void addTargetIndicator(Event event) { - getTargetRow(((Element) event.getTarget())) - .ifPresent(e -> e.addClassName(CLASS_DRAG_OVER)); + protected void setTargetIndicator(Event event) { + getTargetRow(((Element) event.getTarget())).ifPresent(target -> { + + // Get required class name + String className = getTargetClassName(target, (NativeEvent) event); + + // Add or replace class name if changed + if (!target.hasClassName(className)) { + if (dragOverClassName != null) { + target.removeClassName(dragOverClassName); + } + target.addClassName(className); + dragOverClassName = className; + } + }); + } + + private String getTargetClassName(Element target, NativeEvent event) { + String classSuffix; + + switch (getDropLocation(target, event)) { + case ABOVE: + classSuffix = CLASS_SUFFIX_BEFORE; + break; + case BELOW: + classSuffix = CLASS_SUFFIX_AFTER; + break; + case ON_TOP: + default: + classSuffix = ""; + break; + } + + return CLASS_DRAG_OVER + classSuffix; } @Override protected void removeTargetIndicator(Event event) { - getTargetRow(((Element) event.getTarget())) - .ifPresent(e -> e.removeClassName(CLASS_DRAG_OVER)); + + // Remove all possible drag over class names + getTargetRow((Element) event.getTarget()).ifPresent(e -> { + e.removeClassName(CLASS_DRAG_OVER); + e.removeClassName(CLASS_DRAG_OVER_BEFORE); + e.removeClassName(CLASS_DRAG_OVER_AFTER); + }); } private Optional<TableRowElement> getTargetRow(Element source) { diff --git a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java index 473b72237e..8cba063572 100644 --- a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java @@ -117,7 +117,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { * browser event to be handled */ protected void onDragEnter(Event event) { - addTargetIndicator(event); + setTargetIndicator(event); } /** @@ -137,7 +137,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { } // Add drop target indicator in case the element doesn't have one - addTargetIndicator(event); + setTargetIndicator(event); // Prevent default to allow drop nativeEvent.preventDefault(); @@ -230,7 +230,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { * @param event * The drag enter or dragover event that triggered the indication. */ - protected void addTargetIndicator(Event event) { + protected void setTargetIndicator(Event event) { getDropTargetElement().addClassName(CLASS_DRAG_OVER); } diff --git a/server/src/main/java/com/vaadin/event/dnd/grid/GridDropEvent.java b/server/src/main/java/com/vaadin/event/dnd/grid/GridDropEvent.java index 07ba1327a6..98d1b3495b 100644 --- a/server/src/main/java/com/vaadin/event/dnd/grid/GridDropEvent.java +++ b/server/src/main/java/com/vaadin/event/dnd/grid/GridDropEvent.java @@ -17,11 +17,12 @@ package com.vaadin.event.dnd.grid; import com.vaadin.event.dnd.DragSourceExtension; import com.vaadin.event.dnd.DropEvent; +import com.vaadin.shared.ui.grid.DropLocation; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.Grid; /** - * Server side drop event on an HTML5 drop target {@link Grid} row. + * Drop event on an HTML5 drop target {@link Grid} row. * * @param <T> * The Grid bean type. @@ -32,9 +33,10 @@ import com.vaadin.ui.Grid; public class GridDropEvent<T> extends DropEvent<Grid<T>> { private final T dropTargetRow; + private final DropLocation dropLocation; /** - * Creates a server side Grid row drop event. + * Creates a Grid row drop event. * * @param target * Grid that received the drop. @@ -44,16 +46,18 @@ public class GridDropEvent<T> extends DropEvent<Grid<T>> { * @param dragSourceExtension * Drag source extension of the component that initiated the drop * event. - * @param dropTargetRowKey - * Key of the target row that received the drop. + * @param dropTargetRow + * Target row that received the drop. + * @param dropLocation + * Location of the drop within the target row. */ public GridDropEvent(Grid<T> target, String dataTransferText, DragSourceExtension<? extends AbstractComponent> dragSourceExtension, - String dropTargetRowKey) { + T dropTargetRow, DropLocation dropLocation) { super(target, dataTransferText, dragSourceExtension); - dropTargetRow = target.getDataCommunicator().getKeyMapper() - .get(dropTargetRowKey); + this.dropTargetRow = dropTargetRow; + this.dropLocation = dropLocation; } /** @@ -64,4 +68,13 @@ public class GridDropEvent<T> extends DropEvent<Grid<T>> { public T getDropTargetRow() { return dropTargetRow; } + + /** + * Get the location of the drop within the row. + * + * @return Location of the drop within the row. + */ + public DropLocation getDropLocation() { + return dropLocation; + } } diff --git a/server/src/main/java/com/vaadin/ui/GridDropTargetExtension.java b/server/src/main/java/com/vaadin/ui/GridDropTargetExtension.java index 1fcb7160f4..17633fa5ce 100644 --- a/server/src/main/java/com/vaadin/ui/GridDropTargetExtension.java +++ b/server/src/main/java/com/vaadin/ui/GridDropTargetExtension.java @@ -19,6 +19,7 @@ import com.vaadin.event.dnd.DropTargetExtension; import com.vaadin.event.dnd.grid.GridDropEvent; import com.vaadin.event.dnd.grid.GridDropListener; import com.vaadin.shared.Registration; +import com.vaadin.shared.ui.grid.DropMode; import com.vaadin.shared.ui.grid.GridDropTargetExtensionRpc; import com.vaadin.shared.ui.grid.GridDropTargetExtensionState; @@ -32,18 +33,47 @@ import com.vaadin.shared.ui.grid.GridDropTargetExtensionState; * @since */ public class GridDropTargetExtension<T> extends DropTargetExtension<Grid<T>> { - public GridDropTargetExtension(Grid<T> target) { + + /** + * Extends a Grid and makes it's rows drop targets for HTML5 drag and drop. + * + * @param target + * Grid to be extended. + * @param dropMode + * Drop mode that describes the allowed drop locations within the + * Grid's row. + * @see GridDropEvent#getDropLocation() + */ + public GridDropTargetExtension(Grid<T> target, DropMode dropMode) { super(target); + + setDropMode(dropMode); } - @Override - protected void registerDropTargetRpc(Grid<T> target) { - registerRpc((GridDropTargetExtensionRpc) (dataTransferText, rowKey) -> { - GridDropEvent<T> event = new GridDropEvent<>(target, - dataTransferText, getUI().getActiveDragSource(), rowKey); + /** + * Sets the drop mode of this drop target. + * + * @param dropMode + * Drop mode that describes the allowed drop locations within the + * Grid's row. + * @see GridDropEvent#getDropLocation() + */ + public void setDropMode(DropMode dropMode) { + if (dropMode == null) { + throw new IllegalArgumentException("Drop mode cannot be null"); + } + + getState().dropMode = dropMode; + } - fireEvent(event); - }); + /** + * Gets the drop mode of this drop target. + * + * @return Drop mode that describes the allowed drop locations within the + * Grid's row. + */ + public DropMode getDropMode() { + return getState(false).dropMode; } /** @@ -61,6 +91,22 @@ public class GridDropTargetExtension<T> extends DropTargetExtension<Grid<T>> { } @Override + protected void registerDropTargetRpc(Grid<T> target) { + registerRpc( + (GridDropTargetExtensionRpc) (dataTransferText, rowKey, dropLocation) -> { + + T dropTargetRow = target.getDataCommunicator() + .getKeyMapper().get(rowKey); + + GridDropEvent<T> event = new GridDropEvent<>(target, + dataTransferText, getUI().getActiveDragSource(), + dropTargetRow, dropLocation); + + fireEvent(event); + }); + } + + @Override protected GridDropTargetExtensionState getState() { return (GridDropTargetExtensionState) super.getState(); } diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java b/shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java new file mode 100644 index 0000000000..1453c05c71 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2016 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.shared.ui.grid; + +/** + * Defines drop locations within a Grid row. + * + * @author Vaadin Ltd. + * @since + */ +public enum DropLocation { + + /** + * Drop on top of the row. + */ + ON_TOP, + + /** + * Drop above or before the row. + */ + ABOVE, + + /** + * Drop below or after the row. + */ + BELOW +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java b/shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java new file mode 100644 index 0000000000..2db639be7d --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2016 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.shared.ui.grid; + +/** + * Defines the locations within the Grid row where an element can be dropped. + * + * @author Vaadin Ltd. + * @since + */ +public enum DropMode { + + /** + * The drop event can happen between Grid rows. The drop is above a row + * when the cursor is over the top 50% of a row, otherwise below the + * row. + */ + BETWEEN, + + /** + * The drop event can happen on top of Grid rows. The target of the drop + * is the row under the cursor at the time of the drop event. + */ + ON_TOP, +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionRpc.java b/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionRpc.java index ef0dc405b5..212081d486 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionRpc.java +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionRpc.java @@ -34,6 +34,9 @@ public interface GridDropTargetExtensionRpc extends ServerRpc { * object. * @param rowKey * Key of the row on which the drop event occured. + * @param dropLocation + * Location of the drop within the row. */ - public void drop(String dataTransferText, String rowKey); + public void drop(String dataTransferText, String rowKey, + DropLocation dropLocation); } diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionState.java b/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionState.java index c911d529d0..50b50a5d24 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionState.java @@ -25,4 +25,8 @@ import com.vaadin.shared.ui.dnd.DropTargetState; */ public class GridDropTargetExtensionState extends DropTargetState { + /** + * Stores the drop mode of the drop target Grid. + */ + public DropMode dropMode; } diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java index 83adbad218..9c89177167 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java @@ -20,16 +20,16 @@ import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; -import com.vaadin.event.dnd.grid.GridDropListener; import com.vaadin.server.Page; import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.grid.DropMode; import com.vaadin.tests.components.AbstractTestUIWithLog; -import com.vaadin.ui.ComboBox; import com.vaadin.ui.Grid; import com.vaadin.ui.GridDragSourceExtension; import com.vaadin.ui.GridDropTargetExtension; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Layout; +import com.vaadin.ui.RadioButtonGroup; import elemental.json.Json; import elemental.json.JsonObject; @@ -37,9 +37,10 @@ import elemental.json.JsonObject; public class GridDragAndDrop extends AbstractTestUIWithLog { @Override protected void setup(VaadinRequest request) { + // Drag source Grid Grid<Bean> dragSourceComponent = new Grid<>(); - dragSourceComponent.setItems(createItems(50)); + dragSourceComponent.setItems(createItems(50, "left")); dragSourceComponent.addColumn(Bean::getId).setCaption("ID"); dragSourceComponent.addColumn(Bean::getValue).setCaption("Value"); @@ -47,48 +48,61 @@ public class GridDragAndDrop extends AbstractTestUIWithLog { dragSourceComponent); dragSource.setDragDataGenerator(bean -> { JsonObject ret = Json.createObject(); - ret.put("val", bean.getValue()); + ret.put("generatedId", bean.getId()); + ret.put("generatedValue", bean.getValue()); return ret; }); // Drop target Grid Grid<Bean> dropTargetComponent = new Grid<>(); - dropTargetComponent.setItems(createItems(5)); + dropTargetComponent.setItems(createItems(5, "right")); dropTargetComponent.addColumn(Bean::getId).setCaption("ID"); dropTargetComponent.addColumn(Bean::getValue).setCaption("Value"); GridDropTargetExtension<Bean> dropTarget = new GridDropTargetExtension<>( - dropTargetComponent); + dropTargetComponent, DropMode.ON_TOP); dropTarget.addGridDropListener(event -> { log(event.getDataTransferText() + ", targetId=" + event - .getDropTargetRow().getId()); + .getDropTargetRow().getId() + ", location=" + event + .getDropLocation()); }); - // Layout grids - Layout layout = new HorizontalLayout(); - layout.addComponents(dragSourceComponent, dropTargetComponent); - - // Selection mode combo box - ComboBox<Grid.SelectionMode> selectionModeSwitch = new ComboBox<>( - "Change selection mode"); - selectionModeSwitch.setItems(Arrays.asList(Grid.SelectionMode.SINGLE, - Grid.SelectionMode.MULTI)); - selectionModeSwitch.setEmptySelectionAllowed(false); - selectionModeSwitch.addValueChangeListener(event -> dragSourceComponent + // Layout the two grids + Layout grids = new HorizontalLayout(); + grids.addComponents(dragSourceComponent, dropTargetComponent); + + // Selection modes + List<Grid.SelectionMode> selectionModes = Arrays.asList( + Grid.SelectionMode.SINGLE, Grid.SelectionMode.MULTI); + RadioButtonGroup<Grid.SelectionMode> selectionModeSelect = new RadioButtonGroup<>( + "Selection mode", selectionModes); + selectionModeSelect.setSelectedItem(Grid.SelectionMode.SINGLE); + selectionModeSelect.addValueChangeListener(event -> dragSourceComponent .setSelectionMode(event.getValue())); - selectionModeSwitch.setSelectedItem(Grid.SelectionMode.SINGLE); - addComponents(selectionModeSwitch, layout); + // Drop locations + List<DropMode> dropLocations = Arrays.asList(DropMode.values()); + RadioButtonGroup<DropMode> dropLocationSelect = new RadioButtonGroup<>( + "Allowed drop location", dropLocations); + dropLocationSelect.setSelectedItem(DropMode.ON_TOP); + dropLocationSelect.addValueChangeListener( + event -> dropTarget.setDropMode(event.getValue())); + + Layout controls = new HorizontalLayout(selectionModeSelect, + dropLocationSelect); + + addComponents(controls, grids); // Set dragover styling Page.getCurrent().getStyles().add(".v-drag-over {color: red;}"); } - private List<Bean> createItems(int num) { + private List<Bean> createItems(int num, String prefix) { List<Bean> items = new ArrayList<>(num); IntStream.range(0, num) - .forEach(i -> items.add(new Bean("id_" + i, "value_" + i))); + .forEach(i -> items + .add(new Bean(prefix + "_" + i, "value_" + i))); return items; } |