Browse Source

Make it possible to drop things between Grid rows (#8979)

Fixes #8401
tags/8.1.0.alpha5
Adam Wagner 7 years ago
parent
commit
a773c8c7b3

+ 85
- 8
client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetExtensionConnector.java View File

@@ -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) {

+ 3
- 3
client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java View File

@@ -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);
}


+ 20
- 7
server/src/main/java/com/vaadin/event/dnd/grid/GridDropEvent.java View File

@@ -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;
}
}

+ 54
- 8
server/src/main/java/com/vaadin/ui/GridDropTargetExtension.java View File

@@ -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;
}

/**
@@ -60,6 +90,22 @@ public class GridDropTargetExtension<T> extends DropTargetExtension<Grid<T>> {
GridDropListener.DROP_METHOD);
}

@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();

+ 40
- 0
shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java View File

@@ -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
}

+ 38
- 0
shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java View File

@@ -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,
}

+ 4
- 1
shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionRpc.java View File

@@ -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);
}

+ 4
- 0
shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetExtensionState.java View File

@@ -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;
}

+ 36
- 22
uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java View File

@@ -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;
}

Loading…
Cancel
Save