diff options
author | Pekka Hyvönen <pekka@vaadin.com> | 2017-05-24 10:43:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-24 10:43:55 +0300 |
commit | 642b0adb2c4e078d766f6b5a4beac1b962dc7c91 (patch) | |
tree | faabc66ebc48a468a61a95e4b9c94cd1dde64d45 | |
parent | 00adf36ec3502634a87419bd5485e1c883a98f21 (diff) | |
download | vaadin-framework-642b0adb2c4e078d766f6b5a4beac1b962dc7c91.tar.gz vaadin-framework-642b0adb2c4e078d766f6b5a4beac1b962dc7c91.zip |
Fix drop indicator when Grid is scrolled (#9417)
Now the DnD events are listened from tablewrapper element,
which contains also grid's header and footer, making it possible to
drop on top of them.
5 files changed, 72 insertions, 28 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 dbaaefde91..33729afe77 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 @@ -22,7 +22,6 @@ import java.util.Objects; 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.dom.client.TableSectionElement; import com.google.gwt.user.client.Window; import com.vaadin.client.ServerConnector; import com.vaadin.client.WidgetUtil; @@ -82,10 +81,11 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector { private String styleDragEmpty; /** - * The latest row that was dragged on top of, or the grid body if drop is - * not applicable for any rows. Need to store this so that can remove drop - * hint styling when the target has changed since all browsers don't seem to - * always fire the drag-enter drag-exit events in a consistent order. + * The latest row that was dragged on top of, or the tablewrapper element + * returned by {@link #getDropTargetElement()} if drop is not applicable for + * any body rows. Need to store this so that can remove drop hint styling + * when the target has changed since all browsers don't seem to always fire + * the drag-enter drag-exit events in a consistent order. */ private Element latestTargetElement; @@ -106,7 +106,7 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector { Element targetElement = getTargetElement( (Element) dropEvent.getEventTarget().cast()); - // the target element is either the body or one of the rows + // the target element is either the tablewrapper or one of the body rows if (TableRowElement.is(targetElement)) { rowKey = getRowData(targetElement.cast()) .getString(GridState.JSONKEY_ROWKEY); @@ -237,20 +237,43 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector { } private Element getTargetElement(Element source) { + final Element tableWrapper = getDropTargetElement(); final BodyRowContainer gridBody = getGridBody(); - final TableSectionElement bodyElement = gridBody.getElement(); - while (!Objects.equals(source, bodyElement)) { + final int rowCount = gridBody.getRowCount(); + + while (!Objects.equals(source, tableWrapper)) { + // the drop might happen on top of header, body or footer rows if (TableRowElement.is(source)) { - return source; + String parentTagName = source.getParentElement().getTagName(); + if ("thead".equalsIgnoreCase(parentTagName)) { + // for empty grid or ON_TOP mode, drop as last row, + // otherwise as above first visible row + if (rowCount == 0 + || getState().dropMode == DropMode.ON_TOP) { + return tableWrapper; + } else { + return gridBody.getRowElement(0); + } + } else if ("tfoot".equalsIgnoreCase(parentTagName)) { + // for empty grid or ON_TOP mode, drop as last row, + // otherwise as below last visible row + if (rowCount == 0 + || getState().dropMode == DropMode.ON_TOP) { + return tableWrapper; + } else { + return gridBody.getRowElement(rowCount - 1); + } + } else { // parent is tbody + return source; + } } source = source.getParentElement(); } - // the drag is on top of the body - final int rowCount = gridBody.getRowCount(); - // if no rows in grid, or if the drop mode is on top, then there is no + // the drag is on top of the tablewrapper + // if no rows in grid, or if the drop mode is ON_TOP, then there is no // target row for the drop if (rowCount == 0 || getState().dropMode == DropMode.ON_TOP) { - return bodyElement; + return tableWrapper; } else { // if dragged under the last row to empty space, drop target // needs to be below the last row return gridBody.getRowElement(rowCount - 1); @@ -259,7 +282,14 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector { @Override protected Element getDropTargetElement() { - return getGridBody().getElement(); + /* + * The drop target element, the <div class="v-grid-tablewrapper" />. + * This is where the event listeners are added since then we can accept + * drops on header, body and footer rows and the "empty area" outside + * rows. Also it is used since then the drop hints for "empty" area can + * be shown properly as the grid body would scroll. + */ + return getEscalator().getTableWrapper(); } private Escalator getEscalator() { diff --git a/client/src/main/java/com/vaadin/client/widgets/Escalator.java b/client/src/main/java/com/vaadin/client/widgets/Escalator.java index 5239fa9636..1a96f7b771 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Escalator.java +++ b/client/src/main/java/com/vaadin/client/widgets/Escalator.java @@ -6796,6 +6796,20 @@ public class Escalator extends Widget return null; } + /** + * Returns the {@code <div class="v-grid-tablewrapper" />} element which has + * the table inside it. + * <p> + * <em>NOTE: you should not do any modifications to the returned element. + * This API is only available for querying data from the element.</em> + * + * @return the table wrapper element + * @since 8.1 + */ + public Element getTableWrapper() { + return tableWrapper; + } + private Element getSubPartElementTableStructure(SubPartArguments args) { String type = args.getType(); diff --git a/documentation/advanced/advanced-dragndrop.asciidoc b/documentation/advanced/advanced-dragndrop.asciidoc index 10d4ce2b01..8aa18b72ac 100644 --- a/documentation/advanced/advanced-dragndrop.asciidoc +++ b/documentation/advanced/advanced-dragndrop.asciidoc @@ -348,14 +348,16 @@ dropTarget.addGridDropListener(event -> { The _drop location_ property in the [classname]#GridDropEvent# specifies the dropped location in relative to grid row the drop happened on and depends on the used [classname]#DropMode#. When the drop happened on top of a row, the possible options for the location are `ON_TOP`, `ABOVE` and `BELOW`. -If the grid is empty, or if there was empty space after the last row in grid and the [classname]#DropMode.ON_TOP# was used, then the drop location `EMPTY` will be used. If the drop modes [classname]#DropMode.BETWEEN# or [classname]#DropMode.ON_TOP_OR_BETWEEN# are used, then the location can be `EMPTY` only when the grid was empty; otherwise the drop happened ´BELOW´ the last row. When the drop location is `EMPTY`, the [methodname]#getDropTargetRow# method will also return an empty optional. +If the grid is empty or if the drop was on empty space after the last row in grid, and the [classname]#DropMode.ON_TOP# was used, then the drop location `EMPTY` will be used. If the drop modes [classname]#DropMode.BETWEEN# or [classname]#DropMode.ON_TOP_OR_BETWEEN# are used, then the location can be `EMPTY` only when the grid was empty; otherwise the drop happened `BELOW` the last visible row. When the drop location is `EMPTY`, the [methodname]#getDropTargetRow# method will also return an empty optional. + +When dropping on top of the grid's header or footer, the drop location will be `EMPTY` if there are no rows in the grid or if [classname]#DropMode.ON_TOP# was used. If there are rows in the grid, dropping on top of the header will set the drop location to `ABOVE` and the dropped row will be the first currently visible row in grid. Similarly, if dropping on top of the footer, the drop location will be `BELOW` and the dropped row will be the last visible row in the grid. ==== 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 or to the grid body to indicate the drop location. -When dragging on top of a row, `v-grid-row-drag-center` indicates ON_TOP, `v-grid-row-drag-top` indicates ABOVE and `v-grid-row-drag-bottom` indicates BELOW locations. When dragging on top of an empty grid, or when the drop location is ON_TOP and dragged below the last row in grid (and there is empty space visible), the `v-grid-body-body-drag-top` style is applied to the table body element. +When dragging on top of a row, `v-grid-row-drag-center` indicates ON_TOP, `v-grid-row-drag-top` indicates ABOVE and `v-grid-row-drag-bottom` indicates BELOW locations. When dragging on top of an empty grid, or when the drop location is ON_TOP and dragged below the last row in grid (and there is empty space visible), the `v-grid-body-body-drag-top` style is applied to the `v-grid-tablewrapper` element which surrounds the grid header, body and footer. (((range="endofrange", startref="term.advanced.dragndrop"))) 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 e6af56d66d..aa80cdc7b8 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 @@ -87,8 +87,9 @@ public class GridDropEvent<T> extends DropEvent<Grid<T>> { * Get the location of the drop within the row. * <p> * <em>NOTE: when dropped on an empty grid, or when {@link DropMode#ON_TOP} - * is used and the drop happened on empty space after last row, the location - * will be {@link DropLocation#EMPTY}.</em> + * is used and the drop happened on empty space after last row or on top of + * the header / footer, the location will be + * {@link DropLocation#EMPTY}.</em> * * @return Location of the drop within the row. * @see GridDropTarget#setDropMode(DropMode) 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 2f6be4a584..fdc4eff73f 100644 --- a/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss +++ b/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss @@ -832,7 +832,7 @@ $v-grid-drag-indicator-color: $v-focus-color; // Drag and drop .#{$primary-stylename}-row-drag-top, .#{$primary-stylename}-row-drag-bottom, .#{$primary-stylename}-row-drag-bottom { - z-index: 100; + z-index: 100; // based on what else z-indexes are in grid/escalator } .#{$primary-stylename}-row-drag-top:before, @@ -855,6 +855,10 @@ $v-grid-drag-indicator-color: $v-focus-color; top: -1px; } + .#{$primary-stylename}-row-drag-top:first-child:before { + top: 0; + } + .#{$primary-stylename}-row-drag-center:after { content: ""; position: absolute; @@ -877,28 +881,21 @@ $v-grid-drag-indicator-color: $v-focus-color; } } - // Expand Grid's body to cover the whole Grid - .#{$primary-stylename}-body-droptarget { - top: 0; - right: 0; - bottom: 0; - left: 0; - } // Show drop hint when the grid is empty or, // if the DropMode.ON_TOP is used and dragging below last row .#{$primary-stylename}-body-drag-top:after { content: ""; position: absolute; top: 0; - right: 2px; + right: 0; bottom: 0; left: 0; pointer-events: none; border: 2px solid $v-grid-drag-indicator-color; + z-index: 100; // based on what else z-indexes are in grid/escalator } } - @include keyframes(valo-grid-editor-footer-animate-in) { 0% { margin-top: -$v-grid-row-height; |