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.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. /**
  41. * Style name suffix for dragging data over the center of the drop target.
  42. */
  43. protected static final String STYLE_SUFFIX_DRAG_CENTER = "-drag-center";
  44. /**
  45. * Style name suffix for dragging data over the top part of the drop target.
  46. */
  47. protected static final String STYLE_SUFFIX_DRAG_TOP = "-drag-top";
  48. /**
  49. * Style name suffix for dragging data over the bottom part of the drop
  50. * target.
  51. */
  52. protected static final String STYLE_SUFFIX_DRAG_BOTTOM = "-drag-bottom";
  53. // Create event listeners
  54. private final EventListener dragEnterListener = this::onDragEnter;
  55. private final EventListener dragOverListener = this::onDragOver;
  56. private final EventListener dragLeaveListener = this::onDragLeave;
  57. private final EventListener dropListener = this::onDrop;
  58. /**
  59. * Widget of the drop target component.
  60. */
  61. private Widget dropTargetWidget;
  62. /**
  63. * Class name to apply when an element is dragged over the center of the
  64. * target.
  65. */
  66. private String styleDragCenter;
  67. @Override
  68. protected void extend(ServerConnector target) {
  69. dropTargetWidget = ((ComponentConnector) target).getWidget();
  70. addDropListeners(getDropTargetElement());
  71. }
  72. /**
  73. * Adds dragenter, dragover, dragleave and drop event listeners to the given
  74. * DOM element.
  75. *
  76. * @param element
  77. * DOM element to attach event listeners to.
  78. */
  79. protected void addDropListeners(Element element) {
  80. EventTarget target = element.cast();
  81. target.addEventListener(Event.DRAGENTER, dragEnterListener);
  82. target.addEventListener(Event.DRAGOVER, dragOverListener);
  83. target.addEventListener(Event.DRAGLEAVE, dragLeaveListener);
  84. target.addEventListener(Event.DROP, dropListener);
  85. }
  86. /**
  87. * Removes dragenter, dragover, dragleave and drop event listeners from the
  88. * given DOM element.
  89. *
  90. * @param element
  91. * DOM element to remove event listeners from.
  92. */
  93. protected void removeDropListeners(Element element) {
  94. EventTarget target = element.cast();
  95. target.removeEventListener(Event.DRAGENTER, dragEnterListener);
  96. target.removeEventListener(Event.DRAGOVER, dragOverListener);
  97. target.removeEventListener(Event.DRAGLEAVE, dragLeaveListener);
  98. target.removeEventListener(Event.DROP, dropListener);
  99. }
  100. @Override
  101. public void onUnregister() {
  102. super.onUnregister();
  103. removeDropListeners(getDropTargetElement());
  104. }
  105. /**
  106. * Finds the drop target element within the widget. By default, returns the
  107. * topmost element.
  108. *
  109. * @return the drop target element in the parent widget.
  110. */
  111. protected Element getDropTargetElement() {
  112. return dropTargetWidget.getElement();
  113. }
  114. /**
  115. * Event handler for the {@code dragenter} event.
  116. *
  117. * @param event
  118. * browser event to be handled
  119. */
  120. protected void onDragEnter(Event event) {
  121. // Generate style name for drop target
  122. styleDragCenter = dropTargetWidget.getStylePrimaryName()
  123. + STYLE_SUFFIX_DRAG_CENTER;
  124. setTargetIndicator(event);
  125. }
  126. /**
  127. * Event handler for the {@code dragover} event.
  128. *
  129. * @param event
  130. * browser event to be handled
  131. */
  132. protected void onDragOver(Event event) {
  133. NativeEvent nativeEvent = (NativeEvent) event;
  134. if (isDragOverAllowed(nativeEvent)) {
  135. // Set dropEffect parameter
  136. if (getState().dropEffect != null) {
  137. nativeEvent.getDataTransfer().setDropEffect(
  138. DataTransfer.DropEffect
  139. .valueOf(getState().dropEffect.name()));
  140. }
  141. // Add drop target indicator in case the element doesn't have one
  142. setTargetIndicator(event);
  143. // Prevent default to allow drop
  144. nativeEvent.preventDefault();
  145. nativeEvent.stopPropagation();
  146. } else {
  147. // Remove drop effect
  148. nativeEvent.getDataTransfer()
  149. .setDropEffect(DataTransfer.DropEffect.NONE);
  150. // Remove drop target indicator
  151. removeTargetIndicator(event);
  152. }
  153. }
  154. /**
  155. * Determines if dragover event is allowed on this drop target according to
  156. * the dragover criteria.
  157. *
  158. * @param event
  159. * Native dragover event.
  160. * @return {@code true} if dragover is allowed, {@code false} otherwise.
  161. * @see DropTargetExtension#setDragOverCriteria(String)
  162. */
  163. protected boolean isDragOverAllowed(NativeEvent event) {
  164. if (getState().dragOverCriteria != null) {
  165. return executeScript(event, getState().dragOverCriteria);
  166. }
  167. // Allow when criteria not set
  168. return true;
  169. }
  170. /**
  171. * Event handler for the {@code dragleave} event.
  172. *
  173. * @param event
  174. * browser event to be handled
  175. */
  176. protected void onDragLeave(Event event) {
  177. removeTargetIndicator(event);
  178. }
  179. /**
  180. * Event handler for the {@code drop} event.
  181. *
  182. * @param event
  183. * browser event to be handled
  184. */
  185. protected void onDrop(Event event) {
  186. NativeEvent nativeEvent = (NativeEvent) event;
  187. if (dropAllowed(nativeEvent)) {
  188. nativeEvent.preventDefault();
  189. nativeEvent.stopPropagation();
  190. String dataTransferText = nativeEvent.getDataTransfer().getData(
  191. DragSourceState.DATA_TYPE_TEXT);
  192. sendDropEventToServer(dataTransferText, event);
  193. }
  194. removeTargetIndicator(event);
  195. }
  196. private boolean dropAllowed(NativeEvent event) {
  197. if (getState().dropCriteria != null) {
  198. return executeScript(event, getState().dropCriteria);
  199. }
  200. // Allow when criteria not set
  201. return true;
  202. }
  203. /**
  204. * Initiates a server RPC for the drop event.
  205. *
  206. * @param dataTransferText
  207. * Client side textual data that can be set for the drag source and
  208. * is transferred to the drop target.
  209. * @param dropEvent
  210. * Client side drop event.
  211. */
  212. protected void sendDropEventToServer(String dataTransferText,
  213. Event dropEvent) {
  214. getRpcProxy(DropTargetRpc.class).drop(dataTransferText);
  215. }
  216. /**
  217. * Add class that indicates that the component is a target.
  218. *
  219. * @param event
  220. * The drag enter or dragover event that triggered the indication.
  221. */
  222. protected void setTargetIndicator(Event event) {
  223. getDropTargetElement().addClassName(styleDragCenter);
  224. }
  225. /**
  226. * Remove the drag target indicator class name from the target element.
  227. * <p>
  228. * This is triggered on dragleave, drop and dragover events.
  229. *
  230. * @param event
  231. * the event that triggered the removal of the indicator
  232. */
  233. protected void removeTargetIndicator(Event event) {
  234. getDropTargetElement().removeClassName(styleDragCenter);
  235. }
  236. private native boolean executeScript(NativeEvent event, String script)/*-{
  237. return new Function('event', script)(event);
  238. }-*/;
  239. @Override
  240. public DropTargetState getState() {
  241. return (DropTargetState) super.getState();
  242. }
  243. }