aboutsummaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorAdam Wagner <wbadam@users.noreply.github.com>2017-02-09 07:52:15 +0100
committerPekka Hyvönen <pekka@vaadin.com>2017-02-10 15:59:44 +0200
commita9c8e66d14899796e786e643d434daf6f16cf062 (patch)
tree59253d3b06d3223bc2e19b2f8b1b4fd5fb8e3b77 /client
parent4445eae397818196ba1818470c73ccf34e2033ce (diff)
downloadvaadin-framework-a9c8e66d14899796e786e643d434daf6f16cf062.tar.gz
vaadin-framework-a9c8e66d14899796e786e643d434daf6f16cf062.zip
HTML5 Drag and Drop Support (#8264)
* Add DragSource Extension (#8169) * Add DropTarget Extension (#8170) * Add DragStart Event to DragSource Extension (#8171) * Make DataTransfer.dropEffect configurable (#8174) * Make DragSource.dataTransfer data configurable (#8172) * Add server-side Event for drop (#8177) * Added license headers * Extract handler methods, move DropEvent and DropListener to new file, move enums to top * Replaced LinkedHashMap with Map and added List to preserve order of data * Add API for adding a JS acceptance criteria for dragover and drop (#8178, #8179) * Make DragSource Extension extendable (#8175) * Make DropTarget Extension extendable (#8176) * Added javadoc to protected methods * Moved EffectAllowed to shared so that it could be used in shared state directly * Moved DropEffect to separate file, some review fixes and javadoc * Added list to DropTargetRpc to preserve order of data * Remove event listeners on unregister * Changed method names set/getData() to more descriptive set/getTransferData() * Add server side dragStart event (#8171) * Add style to prevent text selection to allow drag * Remove target indicator style on drop * Add client side dragend event listener for drag source * Add server side dragend listener. Attach client side listener only when server side listener added. * Add drag source information to server side dragstart and dragend events. * Fixed some issues addressed in review * Trigger server side dragstart only when there is a listener attached * Criteria script can be set as null to clear * Use Js Interop instead of JSNI for handling event listeners * Use elemental package instead of Js Interop for handling event listeners * Add missing javadoc for public methods * Add default value "uninitialized" to effectAllowed parameter * Simple test UI for HTML5 DnD functionality (#8395) * Add javadoc and other minor changes
Diffstat (limited to 'client')
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java139
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java223
2 files changed, 362 insertions, 0 deletions
diff --git a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java
new file mode 100644
index 0000000000..25e2912b7a
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java
@@ -0,0 +1,139 @@
+/*
+ * 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.client.extensions;
+
+import java.util.List;
+import java.util.Map;
+
+import com.google.gwt.dom.client.DataTransfer;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.event.dnd.DragSourceExtension;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.dnd.DragSourceRpc;
+import com.vaadin.shared.ui.dnd.DragSourceState;
+
+import elemental.events.Event;
+import elemental.events.EventListener;
+import elemental.events.EventTarget;
+
+/**
+ * Extension to add drag source functionality to a widget for using HTML5 drag
+ * and drop. Client side counterpart of {@link DragSourceExtension}.
+ */
+@Connect(DragSourceExtension.class)
+public class DragSourceExtensionConnector extends AbstractExtensionConnector {
+
+ private static final String CLASS_DRAGGABLE = "v-draggable";
+
+ // Create event listeners
+ private final EventListener dragStartListener = this::onDragStart;
+ private final EventListener dragEndListener = this::onDragEnd;
+
+ @Override
+ protected void extend(ServerConnector target) {
+ Element dragSourceElement = getDraggableElement();
+
+ dragSourceElement.setDraggable(Element.DRAGGABLE_TRUE);
+ dragSourceElement.addClassName(CLASS_DRAGGABLE);
+
+ EventTarget dragSource = dragSourceElement.cast();
+
+ // dragstart
+ dragSource.addEventListener(Event.DRAGSTART, dragStartListener);
+
+ // dragend
+ dragSource.addEventListener(Event.DRAGEND, dragEndListener);
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+
+ EventTarget dragSource = (EventTarget) getDraggableElement();
+
+ // Remove listeners
+ dragSource.removeEventListener(Event.DRAGSTART, dragStartListener);
+ dragSource.removeEventListener(Event.DRAGEND, dragEndListener);
+ }
+
+ /**
+ * Event handler for the {@code dragstart} event. Called when {@code
+ * dragstart} event occurs.
+ *
+ * @param event
+ * browser event to be handled
+ */
+ protected void onDragStart(Event event) {
+ // Convert elemental event to have access to dataTransfer
+ NativeEvent nativeEvent = (NativeEvent) event;
+
+ // Set effectAllowed parameter
+ if (getState().effectAllowed != null) {
+ setEffectAllowed(nativeEvent.getDataTransfer(),
+ getState().effectAllowed.getValue());
+ }
+
+ // Set data parameter
+ List<String> types = getState().types;
+ Map<String, String> data = getState().data;
+ for (String format : types) {
+ nativeEvent.getDataTransfer().setData(format, data.get(format));
+ }
+
+ // Initiate firing server side dragstart event when there is a
+ // DragStartListener attached on the server side
+ if (hasEventListener(DragSourceState.EVENT_DRAGSTART)) {
+ getRpcProxy(DragSourceRpc.class).dragStart();
+ }
+ }
+
+ /**
+ * Event handler for the {@code dragend} event. Called when {@code dragend}
+ * event occurs.
+ *
+ * @param event
+ */
+ protected void onDragEnd(Event event) {
+ // Initiate server start dragend event when there is a DragEndListener
+ // attached on the server side
+ if (hasEventListener(DragSourceState.EVENT_DRAGEND)) {
+ getRpcProxy(DragSourceRpc.class).dragEnd();
+ }
+ }
+
+ /**
+ * Finds the draggable element within the widget. By default, returns the
+ * topmost element.
+ *
+ * @return the draggable element in the parent widget.
+ */
+ protected Element getDraggableElement() {
+ return ((ComponentConnector) getParent()).getWidget().getElement();
+ }
+
+ private native void setEffectAllowed(DataTransfer dataTransfer,
+ String effectAllowed)/*-{
+ dataTransfer.effectAllowed = effectAllowed;
+ }-*/;
+
+ @Override
+ public DragSourceState getState() {
+ return (DragSourceState) super.getState();
+ }
+}
diff --git a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java
new file mode 100644
index 0000000000..5d72a76696
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java
@@ -0,0 +1,223 @@
+/*
+ * 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.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.BrowserEvents;
+import com.google.gwt.dom.client.DataTransfer;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.event.dnd.DropTargetExtension;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.dnd.DropTargetRpc;
+import com.vaadin.shared.ui.dnd.DropTargetState;
+
+import elemental.events.Event;
+import elemental.events.EventListener;
+import elemental.events.EventTarget;
+
+/**
+ * Extension to add drop target functionality to a widget for using HTML5 drag
+ * and drop. Client side counterpart of {@link DropTargetExtension}.
+ */
+@Connect(DropTargetExtension.class)
+public class DropTargetExtensionConnector extends AbstractExtensionConnector {
+
+ private static final String CLASS_DRAG_OVER = "v-drag-over";
+
+ // Create event listeners
+ private final EventListener dragEnterListener = this::onDragEnter;
+ private final EventListener dragOverListener = this::onDragOver;
+ private final EventListener dragLeaveListener = this::onDragLeave;
+ private final EventListener dropListener = this::onDrop;
+
+ @Override
+ protected void extend(ServerConnector target) {
+ EventTarget dropTarget = getDropTargetElement().cast();
+
+ // dragenter event
+ dropTarget.addEventListener(BrowserEvents.DRAGENTER, dragEnterListener);
+
+ // dragover event
+ dropTarget.addEventListener(BrowserEvents.DRAGOVER, dragOverListener);
+
+ // dragleave event
+ dropTarget.addEventListener(BrowserEvents.DRAGLEAVE, dragLeaveListener);
+
+ // drop event
+ dropTarget.addEventListener(BrowserEvents.DROP, dropListener);
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+
+ EventTarget dropTarget = getDropTargetElement().cast();
+
+ // Remove listeners
+ dropTarget.removeEventListener(BrowserEvents.DRAGENTER,
+ dragEnterListener);
+ dropTarget.removeEventListener(BrowserEvents.DRAGOVER,
+ dragOverListener);
+ dropTarget.removeEventListener(BrowserEvents.DRAGLEAVE,
+ dragLeaveListener);
+ dropTarget.removeEventListener(BrowserEvents.DROP, dropListener);
+ }
+
+ /**
+ * Finds the drop target element within the widget. By default, returns the
+ * topmost element.
+ *
+ * @return the drop target element in the parent widget.
+ */
+ protected Element getDropTargetElement() {
+ return ((ComponentConnector) getParent()).getWidget().getElement();
+ }
+
+ /**
+ * Event handler for the {@code dragenter} event.
+ *
+ * @param event
+ * browser event to be handled
+ */
+ protected void onDragEnter(Event event) {
+ addTargetIndicator(getDropTargetElement());
+ }
+
+ /**
+ * Event handler for the {@code dragover} event.
+ *
+ * @param event
+ * browser event to be handled
+ */
+ protected void onDragOver(Event event) {
+ NativeEvent nativeEvent = (NativeEvent) event;
+ if (isDragOverAllowed(nativeEvent)) {
+ // Set dropEffect parameter
+ if (getState().dropEffect != null) {
+ nativeEvent.getDataTransfer().setDropEffect(
+ DataTransfer.DropEffect
+ .valueOf(getState().dropEffect.name()));
+ }
+
+ // Prevent default to allow drop
+ nativeEvent.preventDefault();
+ nativeEvent.stopPropagation();
+ } else {
+ // Remove drop effect
+ nativeEvent.getDataTransfer()
+ .setDropEffect(DataTransfer.DropEffect.NONE);
+
+ // Remove drop target indicator
+ removeTargetIndicator(getDropTargetElement());
+ }
+ }
+
+ /**
+ * Determines if dragover event is allowed on this drop target according to
+ * the dragover criteria.
+ *
+ * @param event
+ * Native dragover event.
+ * @return {@code true} if dragover is allowed, {@code false} otherwise.
+ * @see DropTargetExtension#setDragOverCriteria(String)
+ */
+ protected boolean isDragOverAllowed(NativeEvent event) {
+ if (getState().dragOverCriteria != null) {
+ return executeScript(event, getState().dragOverCriteria);
+ }
+
+ // Allow when criteria not set
+ return true;
+ }
+
+ /**
+ * Event handler for the {@code dragleave} event.
+ *
+ * @param event
+ * browser event to be handled
+ */
+ protected void onDragLeave(Event event) {
+ removeTargetIndicator(getDropTargetElement());
+ }
+
+ /**
+ * Event handler for the {@code drop} event.
+ *
+ * @param event
+ * browser event to be handled
+ */
+ protected void onDrop(Event event) {
+ NativeEvent nativeEvent = (NativeEvent) event;
+ if (dropAllowed(nativeEvent)) {
+ nativeEvent.preventDefault();
+ nativeEvent.stopPropagation();
+
+ // Initiate firing server side drop event
+ JsArrayString typesJsArray = getTypes(
+ nativeEvent.getDataTransfer());
+ List<String> types = new ArrayList<>();
+ Map<String, String> data = new HashMap<>();
+ for (int i = 0; i < typesJsArray.length(); i++) {
+ types.add(typesJsArray.get(i));
+ data.put(typesJsArray.get(i), nativeEvent.getDataTransfer()
+ .getData(typesJsArray.get(i)));
+ }
+
+ getRpcProxy(DropTargetRpc.class)
+ .drop(types, data, getState().dropEffect);
+ }
+
+ removeTargetIndicator(getDropTargetElement());
+ }
+
+ private boolean dropAllowed(NativeEvent event) {
+ if (getState().dropCriteria != null) {
+ return executeScript(event, getState().dropCriteria);
+ }
+
+ // Allow when criteria not set
+ return true;
+ }
+
+ private void addTargetIndicator(Element element) {
+ element.addClassName(CLASS_DRAG_OVER);
+ }
+
+ private void removeTargetIndicator(Element element) {
+ element.removeClassName(CLASS_DRAG_OVER);
+ }
+
+ private native boolean executeScript(NativeEvent event, String script)/*-{
+ return new Function('event', script)(event);
+ }-*/;
+
+ private native JsArrayString getTypes(DataTransfer dataTransfer)/*-{
+ return dataTransfer.types;
+ }-*/;
+
+ @Override
+ public DropTargetState getState() {
+ return (DropTargetState) super.getState();
+ }
+}