You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DropTargetExtensionConnector.java 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.extensions;
  17. import com.google.gwt.dom.client.DataTransfer;
  18. import com.google.gwt.dom.client.Element;
  19. import com.google.gwt.dom.client.NativeEvent;
  20. import com.google.gwt.user.client.ui.Widget;
  21. import com.vaadin.client.ComponentConnector;
  22. import com.vaadin.client.ServerConnector;
  23. import com.vaadin.event.dnd.DropTargetExtension;
  24. import com.vaadin.shared.ui.Connect;
  25. import com.vaadin.shared.ui.dnd.DragSourceState;
  26. import com.vaadin.shared.ui.dnd.DropTargetRpc;
  27. import com.vaadin.shared.ui.dnd.DropTargetState;
  28. import elemental.events.Event;
  29. import elemental.events.EventListener;
  30. import elemental.events.EventTarget;
  31. /**
  32. * Extension to add drop target functionality to a widget for using HTML5 drag
  33. * and drop. Client side counterpart of {@link DropTargetExtension}.
  34. *
  35. * @author Vaadin Ltd
  36. * @since 8.1
  37. */
  38. @Connect(DropTargetExtension.class)
  39. public class DropTargetExtensionConnector extends AbstractExtensionConnector {
  40. protected static final String CLASS_DRAG_OVER = "v-drag-over";
  41. // Create event listeners
  42. private final EventListener dragEnterListener = this::onDragEnter;
  43. private final EventListener dragOverListener = this::onDragOver;
  44. private final EventListener dragLeaveListener = this::onDragLeave;
  45. private final EventListener dropListener = this::onDrop;
  46. /**
  47. * Widget of the drop target component.
  48. */
  49. private Widget dropTargetWidget;
  50. @Override
  51. protected void extend(ServerConnector target) {
  52. dropTargetWidget = ((ComponentConnector) target).getWidget();
  53. addDropListeners(getDropTargetElement());
  54. }
  55. /**
  56. * Adds dragenter, dragover, dragleave and drop event listeners to the given
  57. * DOM element.
  58. *
  59. * @param element
  60. * DOM element to attach event listeners to.
  61. */
  62. protected void addDropListeners(Element element) {
  63. EventTarget target = element.cast();
  64. target.addEventListener(Event.DRAGENTER, dragEnterListener);
  65. target.addEventListener(Event.DRAGOVER, dragOverListener);
  66. target.addEventListener(Event.DRAGLEAVE, dragLeaveListener);
  67. target.addEventListener(Event.DROP, dropListener);
  68. }
  69. /**
  70. * Removes dragenter, dragover, dragleave and drop event listeners from the
  71. * given DOM element.
  72. *
  73. * @param element
  74. * DOM element to remove event listeners from.
  75. */
  76. protected void removeDropListeners(Element element) {
  77. EventTarget target = element.cast();
  78. target.removeEventListener(Event.DRAGENTER, dragEnterListener);
  79. target.removeEventListener(Event.DRAGOVER, dragOverListener);
  80. target.removeEventListener(Event.DRAGLEAVE, dragLeaveListener);
  81. target.removeEventListener(Event.DROP, dropListener);
  82. }
  83. @Override
  84. public void onUnregister() {
  85. super.onUnregister();
  86. removeDropListeners(getDropTargetElement());
  87. }
  88. /**
  89. * Finds the drop target element within the widget. By default, returns the
  90. * topmost element.
  91. *
  92. * @return the drop target element in the parent widget.
  93. */
  94. protected Element getDropTargetElement() {
  95. return dropTargetWidget.getElement();
  96. }
  97. /**
  98. * Event handler for the {@code dragenter} event.
  99. *
  100. * @param event
  101. * browser event to be handled
  102. */
  103. protected void onDragEnter(Event event) {
  104. setTargetIndicator(event);
  105. }
  106. /**
  107. * Event handler for the {@code dragover} event.
  108. *
  109. * @param event
  110. * browser event to be handled
  111. */
  112. protected void onDragOver(Event event) {
  113. NativeEvent nativeEvent = (NativeEvent) event;
  114. if (isDragOverAllowed(nativeEvent)) {
  115. // Set dropEffect parameter
  116. if (getState().dropEffect != null) {
  117. nativeEvent.getDataTransfer().setDropEffect(
  118. DataTransfer.DropEffect
  119. .valueOf(getState().dropEffect.name()));
  120. }
  121. // Add drop target indicator in case the element doesn't have one
  122. setTargetIndicator(event);
  123. // Prevent default to allow drop
  124. nativeEvent.preventDefault();
  125. nativeEvent.stopPropagation();
  126. } else {
  127. // Remove drop effect
  128. nativeEvent.getDataTransfer()
  129. .setDropEffect(DataTransfer.DropEffect.NONE);
  130. // Remove drop target indicator
  131. removeTargetIndicator(event);
  132. }
  133. }
  134. /**
  135. * Determines if dragover event is allowed on this drop target according to
  136. * the dragover criteria.
  137. *
  138. * @param event
  139. * Native dragover event.
  140. * @return {@code true} if dragover is allowed, {@code false} otherwise.
  141. * @see DropTargetExtension#setDragOverCriteria(String)
  142. */
  143. protected boolean isDragOverAllowed(NativeEvent event) {
  144. if (getState().dragOverCriteria != null) {
  145. return executeScript(event, getState().dragOverCriteria);
  146. }
  147. // Allow when criteria not set
  148. return true;
  149. }
  150. /**
  151. * Event handler for the {@code dragleave} event.
  152. *
  153. * @param event
  154. * browser event to be handled
  155. */
  156. protected void onDragLeave(Event event) {
  157. removeTargetIndicator(event);
  158. }
  159. /**
  160. * Event handler for the {@code drop} event.
  161. *
  162. * @param event
  163. * browser event to be handled
  164. */
  165. protected void onDrop(Event event) {
  166. NativeEvent nativeEvent = (NativeEvent) event;
  167. if (dropAllowed(nativeEvent)) {
  168. nativeEvent.preventDefault();
  169. nativeEvent.stopPropagation();
  170. String dataTransferText = nativeEvent.getDataTransfer().getData(
  171. DragSourceState.DATA_TYPE_TEXT);
  172. sendDropEventToServer(dataTransferText, event);
  173. }
  174. removeTargetIndicator(event);
  175. }
  176. private boolean dropAllowed(NativeEvent event) {
  177. if (getState().dropCriteria != null) {
  178. return executeScript(event, getState().dropCriteria);
  179. }
  180. // Allow when criteria not set
  181. return true;
  182. }
  183. /**
  184. * Initiates a server RPC for the drop event.
  185. *
  186. * @param dataTransferText
  187. * Client side textual data that can be set for the drag source and
  188. * is transferred to the drop target.
  189. * @param dropEvent
  190. * Client side drop event.
  191. */
  192. protected void sendDropEventToServer(String dataTransferText,
  193. Event dropEvent) {
  194. getRpcProxy(DropTargetRpc.class).drop(dataTransferText);
  195. }
  196. /**
  197. * Add class that indicates that the component is a target.
  198. *
  199. * @param event
  200. * The drag enter or dragover event that triggered the indication.
  201. */
  202. protected void setTargetIndicator(Event event) {
  203. getDropTargetElement().addClassName(CLASS_DRAG_OVER);
  204. }
  205. /**
  206. * Remove the drag target indicator class name from the target element.
  207. * <p>
  208. * This is triggered on dragleave, drop and dragover events.
  209. *
  210. * @param event
  211. * the event that triggered the removal of the indicator
  212. */
  213. protected void removeTargetIndicator(Event event) {
  214. getDropTargetElement().removeClassName(CLASS_DRAG_OVER);
  215. }
  216. private native boolean executeScript(NativeEvent event, String script)/*-{
  217. return new Function('event', script)(event);
  218. }-*/;
  219. @Override
  220. public DropTargetState getState() {
  221. return (DropTargetState) super.getState();
  222. }
  223. }