Browse Source

Add API to store any type of data in the dataTransfer object (#9319)

tags/8.1.0.alpha8
Adam Wagner 7 years ago
parent
commit
eb743d9652

+ 6
- 3
client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java View File

@@ -15,6 +15,8 @@
*/
package com.vaadin.client.connectors.grid;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

@@ -81,8 +83,9 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
}

@Override
protected void sendDropEventToServer(String dataTransferText,
String dropEffect, NativeEvent dropEvent) {
protected void sendDropEventToServer(List<String> types,
Map<String, String> data, String dropEffect,
NativeEvent dropEvent) {

String rowKey = null;
DropLocation dropLocation = null;
@@ -95,7 +98,7 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
dropLocation = getDropLocation(targetRow.get(), dropEvent);
}

getRpcProxy(GridDropTargetRpc.class).drop(dataTransferText, dropEffect,
getRpcProxy(GridDropTargetRpc.class).drop(types, data, dropEffect,
rowKey, dropLocation);
}


+ 16
- 5
client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java View File

@@ -15,6 +15,9 @@
*/
package com.vaadin.client.extensions;

import java.util.List;
import java.util.Map;

import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.dom.client.DataTransfer;
import com.google.gwt.dom.client.Element;
@@ -182,14 +185,22 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector {
// Set drag image
setDragImage(nativeEvent);

// Set text data parameter
String dataTransferText = createDataTransferText(nativeEvent);
// Set data parameters
List<String> types = getState().types;
Map<String, String> data = getState().data;
for (String type : types) {
nativeEvent.getDataTransfer().setData(type, data.get(type));
}

// Always set something as the text data, or DnD won't work in FF !
String dataTransferText = createDataTransferText(nativeEvent);
if (dataTransferText == null) {
dataTransferText = "";
}
nativeEvent.getDataTransfer().setData(DragSourceState.DATA_TYPE_TEXT,
dataTransferText);

// Override data type "text" when storing special data is needed
nativeEvent.getDataTransfer()
.setData(DragSourceState.DATA_TYPE_TEXT, dataTransferText);

// Initiate firing server side dragstart event when there is a
// DragStartListener attached on the server side
@@ -252,7 +263,7 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector {
* @return Textual data to be set for the event or {@literal null}.
*/
protected String createDataTransferText(NativeEvent dragStartEvent) {
return getState().dataTransferText;
return getState().data.get(DragSourceState.DATA_TYPE_TEXT);
}

/**

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

@@ -15,6 +15,12 @@
*/
package com.vaadin.client.extensions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.dom.client.DataTransfer;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
@@ -24,7 +30,6 @@ import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.dnd.DragSourceState;
import com.vaadin.shared.ui.dnd.DropEffect;
import com.vaadin.shared.ui.dnd.DropTargetRpc;
import com.vaadin.shared.ui.dnd.DropTargetState;
@@ -257,13 +262,18 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
nativeEvent.preventDefault();
nativeEvent.stopPropagation();

String dataTransferText = nativeEvent.getDataTransfer()
.getData(DragSourceState.DATA_TYPE_TEXT);

String dropEffect = DragSourceExtensionConnector
.getDropEffect(nativeEvent.getDataTransfer());

sendDropEventToServer(dataTransferText, dropEffect, nativeEvent);
JsArrayString typesJsArray = getTypes(
nativeEvent.getDataTransfer());
List<String> types = new ArrayList<>();
Map<String, String> data = new HashMap<>();
for (int i = 0; i < typesJsArray.length(); i++) {
String type = typesJsArray.get(i);
types.add(type);
data.put(type, nativeEvent.getDataTransfer().getData(type));
}

sendDropEventToServer(types, data, DragSourceExtensionConnector
.getDropEffect(nativeEvent.getDataTransfer()), nativeEvent);
}

removeTargetClassIndicator(nativeEvent);
@@ -291,17 +301,18 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
/**
* Initiates a server RPC for the drop event.
*
* @param dataTransferText
* Client side textual data that can be set for the drag source
* and is transferred to the drop target.
* @param types
* List of data types from {@code DataTransfer.types} object.
* @param data
* Map containing all types and corresponding data from the {@code
* DataTransfer} object.
* @param dropEffect
* the desired drop effect
* @param dropEvent
* Client side drop event.
* The desired drop effect.
*/
protected void sendDropEventToServer(String dataTransferText,
String dropEffect, NativeEvent dropEvent) {
getRpcProxy(DropTargetRpc.class).drop(dataTransferText, dropEffect);
protected void sendDropEventToServer(List<String> types,
Map<String, String> data, String dropEffect,
NativeEvent dropEvent) {
getRpcProxy(DropTargetRpc.class).drop(types, data, dropEffect);
}

/**
@@ -337,6 +348,11 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
getDropTargetElement().removeClassName(styleDragCenter);
}

private native JsArrayString getTypes(DataTransfer dataTransfer)
/*-{
return dataTransfer.types;
}-*/;

private native boolean executeScript(NativeEvent event, String script)
/*-{
return new Function('event', script)(event);

+ 18
- 3
documentation/advanced/advanced-dragndrop.asciidoc View File

@@ -33,12 +33,21 @@ DragSourceExtension<Label> dragSource = new DragSourceExtension<>(draggableLabel
dragSource.setEffectAllowed(EffectAllowed.MOVE);
// set the text to transfer
dragSource.setDataTransferText("hello receiver");
// set other data to transfer (in this case HTML)
dragSource.setDataTransferData("text/html", "<label>hello receiver</label>");
----

The __effect allowed__ specifies the allowed effects that must match the __drop effect__ of the drop target. If these don't match, the drop event is never fired on the target. If multiple effects are allowed, the user can use the modifier keys to switch between the desired effects. The default effect and the modifier keys are system and browser dependent.

The __data transfer text__ is textual data, that the drop target will receive in the __drop event__.

The __data transfer data__ is any data of the given type, that the drop target will also receive in the __drop event__. The order, in which the data is set for the drag source, is preserved, which helps the drop target finding the most preferred and supported data.

[NOTE]
====
Note that `"text"` is the only cross browser supported data type. If your application supports IE11, pleas only use the `setDataTransferText()` method.
====

The [classname]#DragStartEvent# is fired when the drag has started, and the [classname]#DragEndEvent# event when the drag has ended, either in a drop or a cancel.

[source, java]
@@ -113,10 +122,16 @@ dropTarget.addDropListener(event -> {
if (dragSource.isPresent() && dragSource.get() instanceof Label) {
// move the label to the layout
dropTargetLayout.addComponent(dragSource.get());
// get possible transfer data
String message = event.getDataTransferText();
Notification.show("DropEvent with data transfer: "+ message);
String message = event.getDataTransferData("text/html");
if (message != null) {
Notification.show("DropEvent with data transfer html: " + message);
} else {
// get transfer text
message = event.getDataTransferText();
Notification.show("DropEvent with data transfer text: " + message);
}

// handle possible server side drag data, if the drag source was in the same UI
event.getDragData().ifPresent(data -> handleMyDragData((MyObject) data));

+ 13
- 11
server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java View File

@@ -15,6 +15,8 @@
*/
package com.vaadin.ui.components.grid;

import java.util.Map;

import com.vaadin.shared.ui.dnd.DropEffect;
import com.vaadin.shared.ui.grid.DropLocation;
import com.vaadin.ui.AbstractComponent;
@@ -40,25 +42,25 @@ public class GridDropEvent<T> extends DropEvent<Grid<T>> {
* Creates a Grid row drop event.
*
* @param target
* Grid that received the drop.
* @param dataTransferText
* Data of type {@code "text"} from the {@code DataTransfer}
* object.
* Grid that received the drop.
* @param data
* Map containing all types and corresponding data from the {@code
* DataTransfer} object.
* @param dropEffect
* the desired drop effect
* the desired drop effect
* @param dragSourceExtension
* Drag source extension of the component that initiated the drop
* event.
* Drag source extension of the component that initiated the drop
* event.
* @param dropTargetRow
* Target row that received the drop.
* Target row that received the drop.
* @param dropLocation
* Location of the drop within the target row.
* Location of the drop within the target row.
*/
public GridDropEvent(Grid<T> target, String dataTransferText,
public GridDropEvent(Grid<T> target, Map<String, String> data,
DropEffect dropEffect,
DragSourceExtension<? extends AbstractComponent> dragSourceExtension,
T dropTargetRow, DropLocation dropLocation) {
super(target, dataTransferText, dropEffect, dragSourceExtension);
super(target, data, dropEffect, dragSourceExtension);

this.dropTargetRow = dropTargetRow;
this.dropLocation = dropLocation;

+ 9
- 2
server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java View File

@@ -15,6 +15,9 @@
*/
package com.vaadin.ui.components.grid;

import java.util.LinkedHashMap;
import java.util.Map;

import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.dnd.DropEffect;
import com.vaadin.shared.ui.grid.DropMode;
@@ -130,14 +133,18 @@ public class GridDropTarget<T> extends DropTargetExtension<Grid<T>> {

@Override
protected void registerDropTargetRpc() {
registerRpc((GridDropTargetRpc) (dataTransferText, dropEffect, rowKey,
registerRpc((GridDropTargetRpc) (types, data, dropEffect, rowKey,
dropLocation) -> {

// Create a linked map that preserves the order of types
Map<String, String> dataPreserveOrder = new LinkedHashMap<>();
types.forEach(type -> dataPreserveOrder.put(type, data.get(type)));

T dropTargetRow = getParent().getDataCommunicator().getKeyMapper()
.get(rowKey);

GridDropEvent<T> event = new GridDropEvent<>(getParent(),
dataTransferText,
dataPreserveOrder,
DropEffect.valueOf(dropEffect.toUpperCase()),
getUI().getActiveDragSource(), dropTargetRow, dropLocation);


+ 107
- 6
server/src/main/java/com/vaadin/ui/dnd/DragSourceExtension.java View File

@@ -15,6 +15,9 @@
*/
package com.vaadin.ui.dnd;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

import com.vaadin.server.AbstractExtension;
@@ -163,6 +166,14 @@ public class DragSourceExtension<T extends AbstractComponent>
* Returns the allowed effects for the current drag source element. Used to
* set client side {@code DataTransfer.effectAllowed} parameter for the drag
* event.
* <p>
* You can use different types of data to support dragging to different
* targets. Accepted types depend on the drop target and those can be
* platform specific. See https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types
* for examples on different types.
* <p>
* <em>NOTE: IE11 only supports type ' text', which can be set using {@link
* #setDataTransferText(String data)}</em>
*
* @return Effects that are allowed for this draggable element.
*/
@@ -171,15 +182,81 @@ public class DragSourceExtension<T extends AbstractComponent>
}

/**
* Sets data for this drag source element. The data is set for the client
* side draggable element using the {@code DataTransfer.setData("text",
* data)} method.
* Sets data for this drag source element with the given type. The data is
* set for the client side draggable element using {@code
* DataTransfer.setData(type, data)} method.
* <p>
* Note that {@code "text"} is the only cross browser supported data type.
* Use {@link #setDataTransferText(String)} method instead if your
* application supports IE11.
*
* @param type
* Type of the data to be set for the client side draggable element,
* e.g. {@code text/plain}. Cannot be {@code null}.
* @param data
* Data to be set for the client side draggable element.
* Data to be set for the client side draggable element. Cannot be
* {@code null}.
*/
public void setDataTransferData(String type, String data) {
if (type == null) {
throw new IllegalArgumentException("Data type cannot be null");
}

if (data == null) {
throw new IllegalArgumentException("Data cannot be null");
}

if (!getState(false).types.contains(type)) {
getState().types.add(type);
}
getState().data.put(type, data);
}

/**
* Returns the data stored with type {@code type} in this drag source
* element.
*
* @param type
* Type of the requested data, e.g. {@code text/plain}.
* @return Data of type {@code type} stored in this drag source element.
*/
public String getDataTransferData(String type) {
return getState(false).data.get(type);
}

/**
* Returns the map of data stored in this drag source element. The returned
* map preserves the order of storage and is unmodifiable.
*
* @return Unmodifiable copy of the map of data in the order the data was
* stored.
*/
public Map<String, String> getDataTransferData() {
Map<String, String> data = getState(false).data;

// Create a map of data that preserves the order of types
LinkedHashMap<String, String> orderedData = new LinkedHashMap<>(
data.size());
getState(false).types
.forEach(type -> orderedData.put(type, data.get(type)));

return Collections.unmodifiableMap(orderedData);
}

/**
* Sets data of type {@code "text"} for this drag source element. The data
* is set for the client side draggable element using the {@code
* DataTransfer.setData("text", data)} method.
* <p>
* Note that {@code "text"} is the only cross browser supported data type.
* Use this method if your application supports IE11.
*
* @param data
* Data to be set for the client side draggable element.
* @see #setDataTransferData(String, String)
*/
public void setDataTransferText(String data) {
getState().dataTransferText = data;
setDataTransferData(DragSourceState.DATA_TYPE_TEXT, data);
}

/**
@@ -189,7 +266,31 @@ public class DragSourceExtension<T extends AbstractComponent>
* @return Data of type {@code "text"} stored in this drag source element.
*/
public String getDataTransferText() {
return getState(false).dataTransferText;
return getDataTransferData(DragSourceState.DATA_TYPE_TEXT);
}

/**
* Clears data with the given type for this drag source element when
* present.
*
* @param type
* Type of data to be cleared. Cannot be {@code null}.
*/
public void clearDataTransferData(String type) {
if (type == null) {
throw new IllegalArgumentException("Data type cannot be null");
}

getState().types.remove(type);
getState().data.remove(type);
}

/**
* Clears all data for this drag source element.
*/
public void clearDataTransferData() {
getState().types.clear();
getState().data.clear();
}

/**

+ 20
- 9
server/src/main/java/com/vaadin/ui/dnd/DropTargetExtension.java View File

@@ -15,6 +15,9 @@
*/
package com.vaadin.ui.dnd;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.vaadin.server.AbstractExtension;
@@ -62,12 +65,11 @@ public class DropTargetExtension<T extends AbstractComponent>
* Override this method if you need to have a custom RPC interface for
* transmitting the drop event with more data. If just need to do additional
* things before firing the drop event, then you should override
* {@link #onDrop(String, DropEffect)} instead.
* {@link #onDrop(List, Map, DropEffect)} instead.
*/
protected void registerDropTargetRpc() {
registerRpc((DropTargetRpc) (dataTransferText, dropEffect) -> {
onDrop(dataTransferText,
DropEffect.valueOf(dropEffect.toUpperCase()));
registerRpc((DropTargetRpc) (types, data, dropEffect) -> {
onDrop(types, data, DropEffect.valueOf(dropEffect.toUpperCase()));
});
}

@@ -75,13 +77,22 @@ public class DropTargetExtension<T extends AbstractComponent>
* Invoked when a <code>drop</code> has been received from client side.
* Fires the {@link DropEvent}.
*
* @param dataTransferText
* the data transfer of type 'text' for the drop
* @param types
* List of data types from {@code DataTransfer.types} object.
* @param data
* Map containing all types and corresponding data from the {@code
* DataTransfer} object.
* @param dropEffect
* the drop effect
* the drop effect
*/
protected void onDrop(String dataTransferText, DropEffect dropEffect) {
DropEvent<T> event = new DropEvent<>(getParent(), dataTransferText,
protected void onDrop(List<String> types, Map<String, String> data,
DropEffect dropEffect) {

// Create a linked map that preserves the order of types
Map<String, String> dataPreserveOrder = new LinkedHashMap<>();
types.forEach(type -> dataPreserveOrder.put(type, data.get(type)));

DropEvent<T> event = new DropEvent<>(getParent(), dataPreserveOrder,
dropEffect, getUI().getActiveDragSource());

fireEvent(event);

+ 0
- 1
server/src/main/java/com/vaadin/ui/dnd/event/DragEndEvent.java View File

@@ -64,7 +64,6 @@ public class DragEndEvent<T extends AbstractComponent> extends Component.Event {
* dragend event.
* @see DragSourceExtension#setEffectAllowed(EffectAllowed)
* @see DropTargetExtension#setDropEffect(DropEffect)
* @see DropTargetExtension#setDragOverCriteria(String)
* @see DropTargetExtension#setDropCriteria(String)
*/
public DropEffect getDropEffect() {

+ 40
- 16
server/src/main/java/com/vaadin/ui/dnd/event/DropEvent.java View File

@@ -15,8 +15,10 @@
*/
package com.vaadin.ui.dnd.event;

import java.util.Map;
import java.util.Optional;

import com.vaadin.shared.ui.dnd.DragSourceState;
import com.vaadin.shared.ui.dnd.DropEffect;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Component;
@@ -33,7 +35,7 @@ import com.vaadin.ui.dnd.DropTargetExtension;
* @since 8.1
*/
public class DropEvent<T extends AbstractComponent> extends Component.Event {
private final String dataTransferText;
private final Map<String, String> data;
private final DragSourceExtension<? extends AbstractComponent> dragSourceExtension;
private final AbstractComponent dragSource;
private final DropEffect dropEffect;
@@ -42,37 +44,59 @@ public class DropEvent<T extends AbstractComponent> extends Component.Event {
* Creates a server side drop event.
*
* @param target
* Component that received the drop.
* @param dataTransferText
* Data of type {@code "text"} from the {@code DataTransfer}
* object.
* Component that received the drop.
* @param data
* Map containing all types and corresponding data from the {@code
* DataTransfer} object.
* @param dropEffect
* the desired drop effect
* the desired drop effect
* @param dragSourceExtension
* Drag source extension of the component that initiated the drop
* event.
* Drag source extension of the component that initiated the drop
* event.
*/
public DropEvent(T target, String dataTransferText, DropEffect dropEffect,
public DropEvent(T target, Map<String, String> data, DropEffect dropEffect,
DragSourceExtension<? extends AbstractComponent> dragSourceExtension) {
super(target);

this.dataTransferText = dataTransferText;
this.data = data;
this.dropEffect = dropEffect;

this.dragSourceExtension = dragSourceExtension;
this.dragSource = Optional.ofNullable(dragSourceExtension)
.map(DragSourceExtension::getParent).orElse(null);
}

/**
* Get data of type {@code "text"} from the client side {@code DataTransfer}
* object.
* Get data from the {@code DataTransfer} object.
*
* @param type
* Data format, e.g. {@code text/plain} or {@code text/uri-list}.
* @return Optional data for the given format if exists in the {@code
* DataTransfer}, otherwise {@code Optional.empty()}.
*/
public Optional<String> getDataTransferData(String type) {
return Optional.ofNullable(data.get(type));
}

/**
* Get data of type {@code "text"} from the {@code DataTransfer} object.
*
* @return Data of type {@code "text"} if exists in the client side {@code
* DataTransfer} object, otherwise {@literal null}.
* @return Data of type {@code "text"} if exists in the {@code DataTransfer}
* object, otherwise {@literal null}.
*/
public String getDataTransferText() {
return dataTransferText;
return data.get(DragSourceState.DATA_TYPE_TEXT);
}

/**
* Get all of the transfer data from the {@code DataTransfer} object. The
* data can be iterated to find the most relevant data as it preserves the
* order in which the data was set to the drag source element.
*
* @return Map of type/data pairs, containing all the data from the {@code
* DataTransfer} object.
*/
public Map<String, String> getDataTransferData() {
return data;
}

/**

+ 13
- 3
shared/src/main/java/com/vaadin/shared/ui/dnd/DragSourceState.java View File

@@ -15,6 +15,11 @@
*/
package com.vaadin.shared.ui.dnd;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.vaadin.shared.communication.SharedState;

/**
@@ -48,8 +53,13 @@ public class DragSourceState extends SharedState {
public EffectAllowed effectAllowed = EffectAllowed.UNINITIALIZED;

/**
* Used to store text data in {@code DataTransfer} object for the drag
* event.
* {@code DataTransfer.types} parameter. Used to keep track of data formats
* set for the drag event.
*/
public List<String> types = new ArrayList<>();

/**
* Used to store data in the {@code DataTransfer} object for the drag event.
*/
public String dataTransferText;
public Map<String, String> data = new HashMap<>();
}

+ 11
- 5
shared/src/main/java/com/vaadin/shared/ui/dnd/DropTargetRpc.java View File

@@ -15,6 +15,9 @@
*/
package com.vaadin.shared.ui.dnd;

import java.util.List;
import java.util.Map;

import com.vaadin.shared.communication.ServerRpc;

/**
@@ -29,11 +32,14 @@ public interface DropTargetRpc extends ServerRpc {
/**
* Called when drop event happens on client side.
*
* @param dataTransferText
* Data of type {@code "text"} from the {@code DataTransfer}
* object.
* @param types
* List of data types from {@code DataTransfer.types} object.
* @param data
* Map containing all types and corresponding data from the {@code
* DataTransfer} object.
* @param dropEffect
* the desired drop effect
* The desired drop effect.
*/
public void drop(String dataTransferText, String dropEffect);
public void drop(List<String> types, Map<String, String> data,
String dropEffect);
}

+ 13
- 7
shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetRpc.java View File

@@ -15,6 +15,9 @@
*/
package com.vaadin.shared.ui.grid;

import java.util.List;
import java.util.Map;

import com.vaadin.shared.communication.ServerRpc;

/**
@@ -29,16 +32,19 @@ public interface GridDropTargetRpc extends ServerRpc {
/**
* Called when drop event happens on client side.
*
* @param dataTransferText
* Data of type {@code "text"} from the {@code DataTransfer}
* object.
* @param types
* List of data types from {@code DataTransfer.types} object.
* @param data
* Map containing all types and corresponding data from the {@code
* DataTransfer} object.
* @param dropEffect
* the desired drop effect
* the desired drop effect
* @param rowKey
* Key of the row on which the drop event occured.
* Key of the row on which the drop event occured.
* @param dropLocation
* Location of the drop within the row.
* Location of the drop within the row.
*/
public void drop(String dataTransferText, String dropEffect, String rowKey,
public void drop(List<String> types, Map<String, String> data,
String dropEffect, String rowKey,
DropLocation dropLocation);
}

Loading…
Cancel
Save