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.tags/8.1.0.beta1
import com.google.gwt.dom.client.Element; | import com.google.gwt.dom.client.Element; | ||||
import com.google.gwt.dom.client.NativeEvent; | import com.google.gwt.dom.client.NativeEvent; | ||||
import com.google.gwt.dom.client.TableRowElement; | import com.google.gwt.dom.client.TableRowElement; | ||||
import com.google.gwt.dom.client.TableSectionElement; | |||||
import com.google.gwt.user.client.Window; | import com.google.gwt.user.client.Window; | ||||
import com.vaadin.client.ServerConnector; | import com.vaadin.client.ServerConnector; | ||||
import com.vaadin.client.WidgetUtil; | import com.vaadin.client.WidgetUtil; | ||||
private String styleDragEmpty; | 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; | private Element latestTargetElement; | ||||
Element targetElement = getTargetElement( | Element targetElement = getTargetElement( | ||||
(Element) dropEvent.getEventTarget().cast()); | (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)) { | if (TableRowElement.is(targetElement)) { | ||||
rowKey = getRowData(targetElement.cast()) | rowKey = getRowData(targetElement.cast()) | ||||
.getString(GridState.JSONKEY_ROWKEY); | .getString(GridState.JSONKEY_ROWKEY); | ||||
} | } | ||||
private Element getTargetElement(Element source) { | private Element getTargetElement(Element source) { | ||||
final Element tableWrapper = getDropTargetElement(); | |||||
final BodyRowContainer gridBody = getGridBody(); | 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)) { | 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(); | 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 | // target row for the drop | ||||
if (rowCount == 0 || getState().dropMode == DropMode.ON_TOP) { | if (rowCount == 0 || getState().dropMode == DropMode.ON_TOP) { | ||||
return bodyElement; | |||||
return tableWrapper; | |||||
} else { // if dragged under the last row to empty space, drop target | } else { // if dragged under the last row to empty space, drop target | ||||
// needs to be below the last row | // needs to be below the last row | ||||
return gridBody.getRowElement(rowCount - 1); | return gridBody.getRowElement(rowCount - 1); | ||||
@Override | @Override | ||||
protected Element getDropTargetElement() { | 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() { | private Escalator getEscalator() { |
return null; | 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) { | private Element getSubPartElementTableStructure(SubPartArguments args) { | ||||
String type = args.getType(); | String type = args.getType(); |
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`. | 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 | ==== 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. | 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 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"))) | (((range="endofrange", startref="term.advanced.dragndrop"))) | ||||
* Get the location of the drop within the row. | * Get the location of the drop within the row. | ||||
* <p> | * <p> | ||||
* <em>NOTE: when dropped on an empty grid, or when {@link DropMode#ON_TOP} | * <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. | * @return Location of the drop within the row. | ||||
* @see GridDropTarget#setDropMode(DropMode) | * @see GridDropTarget#setDropMode(DropMode) |
// Drag and drop | // Drag and drop | ||||
.#{$primary-stylename}-row-drag-top, .#{$primary-stylename}-row-drag-bottom, .#{$primary-stylename}-row-drag-bottom { | .#{$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, | .#{$primary-stylename}-row-drag-top:before, | ||||
top: -1px; | top: -1px; | ||||
} | } | ||||
.#{$primary-stylename}-row-drag-top:first-child:before { | |||||
top: 0; | |||||
} | |||||
.#{$primary-stylename}-row-drag-center:after { | .#{$primary-stylename}-row-drag-center:after { | ||||
content: ""; | content: ""; | ||||
position: absolute; | position: absolute; | ||||
} | } | ||||
} | } | ||||
// 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, | // Show drop hint when the grid is empty or, | ||||
// if the DropMode.ON_TOP is used and dragging below last row | // if the DropMode.ON_TOP is used and dragging below last row | ||||
.#{$primary-stylename}-body-drag-top:after { | .#{$primary-stylename}-body-drag-top:after { | ||||
content: ""; | content: ""; | ||||
position: absolute; | position: absolute; | ||||
top: 0; | top: 0; | ||||
right: 2px; | |||||
right: 0; | |||||
bottom: 0; | bottom: 0; | ||||
left: 0; | left: 0; | ||||
pointer-events: none; | pointer-events: none; | ||||
border: 2px solid $v-grid-drag-indicator-color; | 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) { | @include keyframes(valo-grid-editor-footer-animate-in) { | ||||
0% { | 0% { | ||||
margin-top: -$v-grid-row-height; | margin-top: -$v-grid-row-height; |