From 9dd70e13cd49639549645f23b1b5ef63a36d84cc Mon Sep 17 00:00:00 2001 From: Adam Wagner Date: Wed, 17 May 2017 07:47:59 +0300 Subject: [PATCH] Make it possible for grid drop target to accept dragged data when grid is empty (#9332) * Make it possible for grid drop target to accept dragged data when grid is empty (#9068) * Make return type of getDropTargetRow() optional --- .../grid/GridDropTargetConnector.java | 4 +- .../DropTargetExtensionConnector.java | 64 +++++++++++++++---- .../advanced/advanced-dragndrop.asciidoc | 11 +++- .../ui/components/grid/GridDropEvent.java | 8 ++- .../VAADIN/themes/valo/components/_grid.scss | 6 ++ .../components/grid/GridDragAndDrop.java | 17 +++-- 6 files changed, 86 insertions(+), 24 deletions(-) diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java index 2ee5c253a9..974e63b9f2 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java @@ -149,7 +149,7 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector { } @Override - protected void addTargetClassIndicator(NativeEvent event) { + protected void addDragOverStyle(NativeEvent event) { getTargetRow(((Element) event.getEventTarget().cast())) .ifPresent(target -> { @@ -187,7 +187,7 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector { } @Override - protected void removeTargetClassIndicator(NativeEvent event) { + protected void removeDragOverStyle(NativeEvent event) { // Remove all possible style names getTargetRow((Element) event.getEventTarget().cast()).ifPresent(e -> { 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 060f2a5167..060a12f658 100644 --- a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java @@ -65,6 +65,11 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { */ protected static final String STYLE_SUFFIX_DRAG_BOTTOM = "-drag-bottom"; + /** + * Style name suffix for indicating that the element is drop target. + */ + protected static final String STYLE_SUFFIX_DROPTARGET = "-droptarget"; + // Create event listeners private final EventListener dragEnterListener = this::onDragEnter; private final EventListener dragOverListener = this::onDragOver; @@ -95,6 +100,9 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { addDropListeners(getDropTargetElement()); ((AbstractComponentConnector) target).onDropTargetAttached(); + + // Add drop target indicator to the drop target element + addDropTargetStyle(); } /** @@ -135,6 +143,9 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { removeDropListeners(getDropTargetElement()); ((AbstractComponentConnector) getParent()).onDropTargetDetached(); + + // Remove drop target indicator + removeDropTargetStyle(); } /** @@ -164,7 +175,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { + STYLE_SUFFIX_DRAG_CENTER; if (isDropAllowed(nativeEvent)) { - addTargetClassIndicator(nativeEvent); + addDragOverStyle(nativeEvent); setDropEffect(nativeEvent); @@ -218,8 +229,8 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { if (isDropAllowed(nativeEvent)) { setDropEffect(nativeEvent); - // Add drop target indicator in case the element doesn't have one - addTargetClassIndicator(nativeEvent); + // Add drag over indicator in case the element doesn't have one + addDragOverStyle(nativeEvent); // Prevent default to allow drop nativeEvent.preventDefault(); @@ -229,8 +240,8 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { nativeEvent.getDataTransfer() .setDropEffect(DataTransfer.DropEffect.NONE); - // Remove drop target indicator - removeTargetClassIndicator(nativeEvent); + // Remove drag over indicator + removeDragOverStyle(nativeEvent); } } @@ -244,7 +255,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { * browser event to be handled */ protected void onDragLeave(Event event) { - removeTargetClassIndicator((NativeEvent) event); + removeDragOverStyle((NativeEvent) event); } /** @@ -276,7 +287,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { .getDropEffect(nativeEvent.getDataTransfer()), nativeEvent); } - removeTargetClassIndicator(nativeEvent); + removeDragOverStyle(nativeEvent); } private boolean isDropAllowed(NativeEvent event) { @@ -316,7 +327,33 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { } /** - * Add class that indicates that the component is a target. + * Add class name for the drop target element indicating that data can be + * dropped onto it. The class name has the following format: + *
+     *     [primaryStyleName]-droptarget
+     * 
+ * The added class name is update + * automatically by the framework when the primary style name changes. + */ + protected void addDropTargetStyle() { + getDropTargetElement().addClassName( + getStylePrimaryName(getDropTargetElement()) + + STYLE_SUFFIX_DROPTARGET); + } + + /** + * Remove class name from the drop target element indication that data can + * be dropped onto it. + */ + protected void removeDropTargetStyle() { + getDropTargetElement().removeClassName( + getStylePrimaryName(getDropTargetElement()) + + STYLE_SUFFIX_DROPTARGET); + } + + /** + * Add class that indicates that the component is a target while data is + * being dragged over it. *

* This is triggered on {@link #onDragEnter(Event) dragenter} and * {@link #onDragOver(Event) dragover} events pending if the drop is @@ -327,12 +364,12 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { * @param event * the dragenter or dragover event that triggered the indication. */ - protected void addTargetClassIndicator(NativeEvent event) { + protected void addDragOverStyle(NativeEvent event) { getDropTargetElement().addClassName(styleDragCenter); } /** - * Remove the drag target indicator class name from the target element. + * Remove the drag over indicator class name from the target element. *

* This is triggered on {@link #onDrop(Event) drop}, * {@link #onDragLeave(Event) dragleave} and {@link #onDragOver(Event) @@ -344,7 +381,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { * @param event * the event that triggered the removal of the indicator */ - protected void removeTargetClassIndicator(NativeEvent event) { + protected void removeDragOverStyle(NativeEvent event) { getDropTargetElement().removeClassName(styleDragCenter); } @@ -358,6 +395,11 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { return new Function('event', script)(event); }-*/; + private native boolean getStylePrimaryName(Element element) + /*-{ + return @com.google.gwt.user.client.ui.UIObject::getStylePrimaryName(Lcom/google/gwt/dom/client/Element;)(element); + }-*/; + @Override public DropTargetState getState() { return (DropTargetState) super.getState(); diff --git a/documentation/advanced/advanced-dragndrop.asciidoc b/documentation/advanced/advanced-dragndrop.asciidoc index d7ef80ac33..2db6e0f18b 100644 --- a/documentation/advanced/advanced-dragndrop.asciidoc +++ b/documentation/advanced/advanced-dragndrop.asciidoc @@ -153,7 +153,9 @@ TODO Add an example of drop criteria === CSS Style Rules -When dragging data over a drop target and the drag over criteria passes, a style name is applied to indicate that the element accepts drops. This style name is the primary style name with `-drag-center` suffix, e.g. `v-label-drag-center`. +Each drop target element have an applied style name, the primary style name with `-droptarget` suffix, e.g. `v-label-droptarget`, to indicate that it is a potential target for data to be dropped onto it. + +When dragging data over a drop target and the drag over criteria passes, a style name is applied to indicate that the element accepts the drop. This style name is the primary style name with `-drag-center` suffix, e.g. `v-label-drag-center`. //// @@ -321,8 +323,11 @@ dropTarget.addGridDropListener(event -> { List items = (List) dataProvider.getItems(); // Calculate the target row's index - int index = items.indexOf(event.getDropTargetRow()) + ( + int index = items.size(); + if (event.getDropTargetRow().isPresent()) { + index = items.indexOf(event.getDropTargetRow().get()) + ( event.getDropLocation() == DropLocation.BELOW ? 1 : 0); + } // Add dragged items to the target Grid items.addAll(index, draggedItems); @@ -337,6 +342,8 @@ dropTarget.addGridDropListener(event -> { ==== CSS Style Rules +A drop target Grid's body has the style name `v-grid-body-droptarget` to indicate that it is a potential target for data to be dropped. + When dragging data over a drop target Grid's row, depending on the drop mode and the mouse position relative to the row, a style name is applied to the row to indicate the drop location. `v-grid-row-drag-center` indicates ON_TOP, `v-grid-row-drag-top` indicates ABOVE and `v-grid-row-drag-bottom` indicates BELOW locations. diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java b/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java index 08d0dad956..80eab4d1fd 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java @@ -16,6 +16,7 @@ package com.vaadin.ui.components.grid; import java.util.Map; +import java.util.Optional; import com.vaadin.shared.ui.dnd.DropEffect; import com.vaadin.shared.ui.grid.DropLocation; @@ -69,10 +70,11 @@ public class GridDropEvent extends DropEvent> { /** * Get the row item source of this event. * - * @return The row item this event was originated from. + * @return The optional row item if the event was originated from a row, + * otherwise an empty optional. */ - public T getDropTargetRow() { - return dropTargetRow; + public Optional getDropTargetRow() { + return Optional.ofNullable(dropTargetRow); } /** diff --git a/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss b/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss index 06376d6a6e..541c38c314 100644 --- a/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss +++ b/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss @@ -876,6 +876,12 @@ $v-grid-drag-indicator-color: $v-focus-color; background: darken($v-grid-drag-indicator-color, 10%); } } + + // Expand Grid's body to cover the whole Grid + .#{$primary-stylename}-body-droptarget { + width: 100%; + height: 100%; + } } 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 9bda8c3a05..ad13e2177a 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 @@ -55,7 +55,7 @@ public class GridDragAndDrop extends AbstractTestUIWithLog { GridDragSource dragSource = applyDragSource(left); // Drop target Grid - Grid right = createGridAndFillWithData(5); + Grid right = createGridAndFillWithData(0); GridDropTarget dropTarget = applyDropTarget(right); // Layout the two grids @@ -158,9 +158,12 @@ public class GridDragAndDrop extends AbstractTestUIWithLog { List items = (List) dataProvider.getItems(); // Calculate the target row's index - int index = items.indexOf(event.getDropTargetRow()) - + (event.getDropLocation() == DropLocation.BELOW ? 1 - : 0); + int index = items.size(); + if (event.getDropTargetRow().isPresent()) { + index = items.indexOf(event.getDropTargetRow().get()) + + (event.getDropLocation() == DropLocation.BELOW + ? 1 : 0); + } // Add dragged items to the target Grid items.addAll(index, draggedItems); @@ -170,8 +173,10 @@ public class GridDragAndDrop extends AbstractTestUIWithLog { + ", dragDataJson=" + event.getDataTransferData("application/json") + ", target=" - + event.getDropTargetRow().getFirstName() + " " - + event.getDropTargetRow().getLastName() + + (event.getDropTargetRow().isPresent() ? + event.getDropTargetRow().get().getFirstName() + " " + + event.getDropTargetRow().get() + .getLastName() : "[BODY]") + ", location=" + event.getDropLocation()); } }); -- 2.39.5