Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

GridDropTargetConnector.java 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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.connectors.grid;
  17. import java.util.List;
  18. import java.util.Map;
  19. import java.util.Objects;
  20. import com.google.gwt.dom.client.Element;
  21. import com.google.gwt.dom.client.NativeEvent;
  22. import com.google.gwt.dom.client.TableRowElement;
  23. import com.google.gwt.dom.client.TableSectionElement;
  24. import com.google.gwt.user.client.Window;
  25. import com.vaadin.client.ServerConnector;
  26. import com.vaadin.client.WidgetUtil;
  27. import com.vaadin.client.extensions.DropTargetExtensionConnector;
  28. import com.vaadin.client.widget.escalator.RowContainer;
  29. import com.vaadin.client.widget.escalator.RowContainer.BodyRowContainer;
  30. import com.vaadin.client.widgets.Escalator;
  31. import com.vaadin.shared.ui.Connect;
  32. import com.vaadin.shared.ui.grid.DropLocation;
  33. import com.vaadin.shared.ui.grid.DropMode;
  34. import com.vaadin.shared.ui.grid.GridDropTargetRpc;
  35. import com.vaadin.shared.ui.grid.GridDropTargetState;
  36. import com.vaadin.shared.ui.grid.GridState;
  37. import com.vaadin.ui.components.grid.GridDropTarget;
  38. import elemental.events.Event;
  39. import elemental.json.JsonObject;
  40. /**
  41. * Makes Grid an HTML5 drop target. This is the client side counterpart of
  42. * {@link GridDropTarget}.
  43. *
  44. * @author Vaadin Ltd
  45. * @since 8.1
  46. */
  47. @Connect(GridDropTarget.class)
  48. public class GridDropTargetConnector extends DropTargetExtensionConnector {
  49. /**
  50. * Current style name
  51. */
  52. private String currentStyleName;
  53. private GridConnector gridConnector;
  54. /**
  55. * Class name to apply when an element is dragged over a row and the
  56. * location is {@link DropLocation#ON_TOP}.
  57. */
  58. private String styleDragCenter;
  59. /**
  60. * Class name to apply when an element is dragged over a row and the
  61. * location is {@link DropLocation#ABOVE}.
  62. */
  63. private String styleDragTop;
  64. /**
  65. * Class name to apply when an element is dragged over a row and the
  66. * location is {@link DropLocation#BELOW}.
  67. */
  68. private String styleDragBottom;
  69. /**
  70. * Class name to apply when dragged over an empty grid.
  71. */
  72. private String styleDragEmpty;
  73. /**
  74. * The latest row that was dragged on top of, or the grid body if drop is
  75. * not applicable for any rows. Need to store this so that can remove drop
  76. * hint styling when the target has changed since all browsers don't seem to
  77. * always fire the drag-enter drag-exit events in a consistent order.
  78. */
  79. private Element latestTargetElement;
  80. @Override
  81. protected void extend(ServerConnector target) {
  82. gridConnector = (GridConnector) target;
  83. super.extend(target);
  84. }
  85. @Override
  86. protected void sendDropEventToServer(List<String> types,
  87. Map<String, String> data, String dropEffect,
  88. NativeEvent dropEvent) {
  89. String rowKey = null;
  90. DropLocation dropLocation = null;
  91. Element targetElement = getTargetElement(
  92. (Element) dropEvent.getEventTarget().cast());
  93. // the target element is either the body or one of the rows
  94. if (TableRowElement.is(targetElement)) {
  95. rowKey = getRowData(targetElement.cast())
  96. .getString(GridState.JSONKEY_ROWKEY);
  97. dropLocation = getDropLocation(targetElement, dropEvent);
  98. } else {
  99. dropLocation = DropLocation.EMPTY;
  100. }
  101. getRpcProxy(GridDropTargetRpc.class).drop(types, data, dropEffect,
  102. rowKey, dropLocation);
  103. }
  104. private JsonObject getRowData(TableRowElement row) {
  105. int rowIndex = ((Escalator.AbstractRowContainer) getGridBody())
  106. .getLogicalRowIndex(row);
  107. return gridConnector.getDataSource().getRow(rowIndex);
  108. }
  109. /**
  110. * Returns the location of the event within the row.
  111. */
  112. private DropLocation getDropLocation(Element target, NativeEvent event) {
  113. if (TableRowElement.is(target)) {
  114. if (getState().dropMode == DropMode.BETWEEN) {
  115. if (getRelativeY(target,
  116. event) < (target.getOffsetHeight() / 2)) {
  117. return DropLocation.ABOVE;
  118. } else {
  119. return DropLocation.BELOW;
  120. }
  121. } else if (getState().dropMode == DropMode.ON_TOP_OR_BETWEEN) {
  122. if (getRelativeY(target, event) < getState().dropThreshold) {
  123. return DropLocation.ABOVE;
  124. } else if (target.getOffsetHeight() - getRelativeY(target,
  125. event) < getState().dropThreshold) {
  126. return DropLocation.BELOW;
  127. } else {
  128. return DropLocation.ON_TOP;
  129. }
  130. } else {
  131. return DropLocation.ON_TOP;
  132. }
  133. }
  134. return DropLocation.EMPTY;
  135. }
  136. private int getRelativeY(Element element, NativeEvent event) {
  137. int relativeTop = element.getAbsoluteTop() - Window.getScrollTop();
  138. return WidgetUtil.getTouchOrMouseClientY(event) - relativeTop;
  139. }
  140. @Override
  141. protected void onDragEnter(Event event) {
  142. // Generate style names for the drop target
  143. String styleRow = gridConnector.getWidget().getStylePrimaryName()
  144. + "-row";
  145. styleDragCenter = styleRow + STYLE_SUFFIX_DRAG_CENTER;
  146. styleDragTop = styleRow + STYLE_SUFFIX_DRAG_TOP;
  147. styleDragBottom = styleRow + STYLE_SUFFIX_DRAG_BOTTOM;
  148. styleDragEmpty = gridConnector.getWidget().getStylePrimaryName()
  149. + "-body" + STYLE_SUFFIX_DRAG_TOP;
  150. super.onDragEnter(event);
  151. }
  152. @Override
  153. protected void addDragOverStyle(NativeEvent event) {
  154. Element targetElement = getTargetElement(
  155. ((Element) event.getEventTarget().cast()));
  156. // Get required class name
  157. String className = getTargetClassName(targetElement, event);
  158. // it seems that sometimes the events are not fired in a consistent
  159. // order, and this could cause that the correct styles are not removed
  160. // from the previous target element in removeDragOverStyle(event)
  161. if (latestTargetElement != null
  162. && targetElement != latestTargetElement) {
  163. removeStyles(latestTargetElement);
  164. }
  165. latestTargetElement = targetElement;
  166. // Add or replace class name if changed
  167. if (!targetElement.hasClassName(className)) {
  168. if (currentStyleName != null) {
  169. targetElement.removeClassName(currentStyleName);
  170. }
  171. targetElement.addClassName(className);
  172. currentStyleName = className;
  173. }
  174. }
  175. private String getTargetClassName(Element target, NativeEvent event) {
  176. String className;
  177. switch (getDropLocation(target, event)) {
  178. case ABOVE:
  179. className = styleDragTop;
  180. break;
  181. case BELOW:
  182. className = styleDragBottom;
  183. break;
  184. case EMPTY:
  185. className = styleDragEmpty;
  186. break;
  187. case ON_TOP:
  188. default:
  189. className = styleDragCenter;
  190. break;
  191. }
  192. return className;
  193. }
  194. @Override
  195. protected void removeDragOverStyle(NativeEvent event) {
  196. // Remove all possible style names
  197. Element targetElement = getTargetElement(
  198. (Element) event.getEventTarget().cast());
  199. removeStyles(targetElement);
  200. }
  201. private void removeStyles(Element element) {
  202. element.removeClassName(styleDragCenter);
  203. element.removeClassName(styleDragTop);
  204. element.removeClassName(styleDragBottom);
  205. element.removeClassName(styleDragEmpty);
  206. }
  207. private Element getTargetElement(Element source) {
  208. final BodyRowContainer gridBody = getGridBody();
  209. final TableSectionElement bodyElement = gridBody.getElement();
  210. while (!Objects.equals(source, bodyElement)) {
  211. if (TableRowElement.is(source)) {
  212. return source;
  213. }
  214. source = source.getParentElement();
  215. }
  216. // the drag is on top of the body
  217. final int rowCount = gridBody.getRowCount();
  218. // if no rows in grid, or if the drop mode is on top, then there is no
  219. // target row for the drop
  220. if (rowCount == 0 || getState().dropMode == DropMode.ON_TOP) {
  221. return bodyElement;
  222. } else { // if dragged under the last row to empty space, drop target
  223. // needs to be below the last row
  224. return gridBody.getRowElement(rowCount - 1);
  225. }
  226. }
  227. @Override
  228. protected Element getDropTargetElement() {
  229. return getGridBody().getElement();
  230. }
  231. private Escalator getEscalator() {
  232. return gridConnector.getWidget().getEscalator();
  233. }
  234. private RowContainer.BodyRowContainer getGridBody() {
  235. return getEscalator().getBody();
  236. }
  237. @Override
  238. public GridDropTargetState getState() {
  239. return (GridDropTargetState) super.getState();
  240. }
  241. }