1b2bca73e1ce6f0541a3cb37459399eb25c70d866e395da00d5f88d75ff4fe4ecf0a51f85544b5008b97aa2d12a9993ac413921d643fb8feb1c8f75c75faa5 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import Hooks from './../../pluginHooks';
  2. import {getWindowScrollTop, hasClass, getWindowScrollLeft} from './../../helpers/dom/element';
  3. import {isMobileBrowser} from './../../helpers/browser';
  4. import BasePlugin from './../_base';
  5. import EventManager from './../../eventManager';
  6. import {registerPlugin} from './../../plugins';
  7. import {CellCoords} from './../../3rdparty/walkontable/src';
  8. /**
  9. * @private
  10. * @plugin MultipleSelectionHandles
  11. */
  12. class MultipleSelectionHandles extends BasePlugin {
  13. /**
  14. * @param {Object} hotInstance
  15. */
  16. constructor(hotInstance) {
  17. super(hotInstance);
  18. /**
  19. * @type {Array}
  20. */
  21. this.dragged = [];
  22. /**
  23. * Instance of EventManager.
  24. *
  25. * @type {EventManager}
  26. */
  27. this.eventManager = null;
  28. /**
  29. * @type {null}
  30. */
  31. this.lastSetCell = null;
  32. }
  33. /**
  34. * Check if the plugin is enabled in the handsontable settings.
  35. *
  36. * @returns {Boolean}
  37. */
  38. isEnabled() {
  39. return isMobileBrowser();
  40. }
  41. /**
  42. * Enable plugin for this Handsontable instance.
  43. */
  44. enablePlugin() {
  45. if (this.enabled) {
  46. return;
  47. }
  48. if (!this.eventManager) {
  49. this.eventManager = new EventManager(this);
  50. }
  51. this.registerListeners();
  52. super.enablePlugin();
  53. }
  54. /**
  55. * Bind the touch events
  56. * @private
  57. */
  58. registerListeners() {
  59. var _this = this;
  60. function removeFromDragged(query) {
  61. if (_this.dragged.length === 1) {
  62. // clear array
  63. _this.dragged.splice(0, _this.dragged.length);
  64. return true;
  65. }
  66. var entryPosition = _this.dragged.indexOf(query);
  67. if (entryPosition == -1) {
  68. return false;
  69. } else if (entryPosition === 0) {
  70. _this.dragged = _this.dragged.slice(0, 1);
  71. } else if (entryPosition == 1) {
  72. _this.dragged = _this.dragged.slice(-1);
  73. }
  74. }
  75. this.eventManager.addEventListener(this.hot.rootElement, 'touchstart', (event) => {
  76. let selectedRange;
  77. if (hasClass(event.target, 'topLeftSelectionHandle-HitArea')) {
  78. selectedRange = _this.hot.getSelectedRange();
  79. _this.dragged.push('topLeft');
  80. _this.touchStartRange = {
  81. width: selectedRange.getWidth(),
  82. height: selectedRange.getHeight(),
  83. direction: selectedRange.getDirection()
  84. };
  85. event.preventDefault();
  86. return false;
  87. } else if (hasClass(event.target, 'bottomRightSelectionHandle-HitArea')) {
  88. selectedRange = _this.hot.getSelectedRange();
  89. _this.dragged.push('bottomRight');
  90. _this.touchStartRange = {
  91. width: selectedRange.getWidth(),
  92. height: selectedRange.getHeight(),
  93. direction: selectedRange.getDirection()
  94. };
  95. event.preventDefault();
  96. return false;
  97. }
  98. });
  99. this.eventManager.addEventListener(this.hot.rootElement, 'touchend', (event) => {
  100. if (hasClass(event.target, 'topLeftSelectionHandle-HitArea')) {
  101. removeFromDragged.call(_this, 'topLeft');
  102. _this.touchStartRange = void 0;
  103. event.preventDefault();
  104. return false;
  105. } else if (hasClass(event.target, 'bottomRightSelectionHandle-HitArea')) {
  106. removeFromDragged.call(_this, 'bottomRight');
  107. _this.touchStartRange = void 0;
  108. event.preventDefault();
  109. return false;
  110. }
  111. });
  112. this.eventManager.addEventListener(this.hot.rootElement, 'touchmove', (event) => {
  113. let scrollTop = getWindowScrollTop(),
  114. scrollLeft = getWindowScrollLeft(),
  115. endTarget,
  116. targetCoords,
  117. selectedRange,
  118. rangeWidth,
  119. rangeHeight,
  120. rangeDirection,
  121. newRangeCoords;
  122. if (_this.dragged.length === 0) {
  123. return;
  124. }
  125. endTarget = document.elementFromPoint(
  126. event.touches[0].screenX - scrollLeft,
  127. event.touches[0].screenY - scrollTop);
  128. if (!endTarget || endTarget === _this.lastSetCell) {
  129. return;
  130. }
  131. if (endTarget.nodeName == 'TD' || endTarget.nodeName == 'TH') {
  132. targetCoords = _this.hot.getCoords(endTarget);
  133. if (targetCoords.col == -1) {
  134. targetCoords.col = 0;
  135. }
  136. selectedRange = _this.hot.getSelectedRange();
  137. rangeWidth = selectedRange.getWidth();
  138. rangeHeight = selectedRange.getHeight();
  139. rangeDirection = selectedRange.getDirection();
  140. if (rangeWidth == 1 && rangeHeight == 1) {
  141. _this.hot.selection.setRangeEnd(targetCoords);
  142. }
  143. newRangeCoords = _this.getCurrentRangeCoords(selectedRange, targetCoords, _this.touchStartRange.direction, rangeDirection, _this.dragged[0]);
  144. if (newRangeCoords.start !== null) {
  145. _this.hot.selection.setRangeStart(newRangeCoords.start);
  146. }
  147. _this.hot.selection.setRangeEnd(newRangeCoords.end);
  148. _this.lastSetCell = endTarget;
  149. }
  150. event.preventDefault();
  151. });
  152. }
  153. getCurrentRangeCoords(selectedRange, currentTouch, touchStartDirection, currentDirection, draggedHandle) {
  154. var topLeftCorner = selectedRange.getTopLeftCorner(),
  155. bottomRightCorner = selectedRange.getBottomRightCorner(),
  156. bottomLeftCorner = selectedRange.getBottomLeftCorner(),
  157. topRightCorner = selectedRange.getTopRightCorner();
  158. var newCoords = {
  159. start: null,
  160. end: null
  161. };
  162. switch (touchStartDirection) {
  163. case 'NE-SW':
  164. switch (currentDirection) {
  165. case 'NE-SW':
  166. case 'NW-SE':
  167. if (draggedHandle == 'topLeft') {
  168. newCoords = {
  169. start: new CellCoords(currentTouch.row, selectedRange.highlight.col),
  170. end: new CellCoords(bottomLeftCorner.row, currentTouch.col)
  171. };
  172. } else {
  173. newCoords = {
  174. start: new CellCoords(selectedRange.highlight.row, currentTouch.col),
  175. end: new CellCoords(currentTouch.row, topLeftCorner.col)
  176. };
  177. }
  178. break;
  179. case 'SE-NW':
  180. if (draggedHandle == 'bottomRight') {
  181. newCoords = {
  182. start: new CellCoords(bottomRightCorner.row, currentTouch.col),
  183. end: new CellCoords(currentTouch.row, topLeftCorner.col)
  184. };
  185. }
  186. break;
  187. default:
  188. break;
  189. }
  190. break;
  191. case 'NW-SE':
  192. switch (currentDirection) {
  193. case 'NE-SW':
  194. if (draggedHandle == 'topLeft') {
  195. newCoords = {
  196. start: currentTouch,
  197. end: bottomLeftCorner
  198. };
  199. } else {
  200. newCoords.end = currentTouch;
  201. }
  202. break;
  203. case 'NW-SE':
  204. if (draggedHandle == 'topLeft') {
  205. newCoords = {
  206. start: currentTouch,
  207. end: bottomRightCorner
  208. };
  209. } else {
  210. newCoords.end = currentTouch;
  211. }
  212. break;
  213. case 'SE-NW':
  214. if (draggedHandle == 'topLeft') {
  215. newCoords = {
  216. start: currentTouch,
  217. end: topLeftCorner
  218. };
  219. } else {
  220. newCoords.end = currentTouch;
  221. }
  222. break;
  223. case 'SW-NE':
  224. if (draggedHandle == 'topLeft') {
  225. newCoords = {
  226. start: currentTouch,
  227. end: topRightCorner
  228. };
  229. } else {
  230. newCoords.end = currentTouch;
  231. }
  232. break;
  233. default:
  234. break;
  235. }
  236. break;
  237. case 'SW-NE':
  238. switch (currentDirection) {
  239. case 'NW-SE':
  240. if (draggedHandle == 'bottomRight') {
  241. newCoords = {
  242. start: new CellCoords(currentTouch.row, topLeftCorner.col),
  243. end: new CellCoords(bottomLeftCorner.row, currentTouch.col)
  244. };
  245. } else {
  246. newCoords = {
  247. start: new CellCoords(topLeftCorner.row, currentTouch.col),
  248. end: new CellCoords(currentTouch.row, bottomRightCorner.col)
  249. };
  250. }
  251. break;
  252. // case 'NE-SW':
  253. //
  254. // break;
  255. case 'SW-NE':
  256. if (draggedHandle == 'topLeft') {
  257. newCoords = {
  258. start: new CellCoords(selectedRange.highlight.row, currentTouch.col),
  259. end: new CellCoords(currentTouch.row, bottomRightCorner.col)
  260. };
  261. } else {
  262. newCoords = {
  263. start: new CellCoords(currentTouch.row, topLeftCorner.col),
  264. end: new CellCoords(topLeftCorner.row, currentTouch.col)
  265. };
  266. }
  267. break;
  268. case 'SE-NW':
  269. if (draggedHandle == 'bottomRight') {
  270. newCoords = {
  271. start: new CellCoords(currentTouch.row, topRightCorner.col),
  272. end: new CellCoords(topLeftCorner.row, currentTouch.col)
  273. };
  274. } else if (draggedHandle == 'topLeft') {
  275. newCoords = {
  276. start: bottomLeftCorner,
  277. end: currentTouch
  278. };
  279. }
  280. break;
  281. default:
  282. break;
  283. }
  284. break;
  285. case 'SE-NW':
  286. switch (currentDirection) {
  287. case 'NW-SE':
  288. case 'NE-SW':
  289. case 'SW-NE':
  290. if (draggedHandle == 'topLeft') {
  291. newCoords.end = currentTouch;
  292. }
  293. break;
  294. case 'SE-NW':
  295. if (draggedHandle == 'topLeft') {
  296. newCoords.end = currentTouch;
  297. } else {
  298. newCoords = {
  299. start: currentTouch,
  300. end: topLeftCorner
  301. };
  302. }
  303. break;
  304. default:
  305. break;
  306. }
  307. break;
  308. default:
  309. break;
  310. }
  311. return newCoords;
  312. }
  313. /**
  314. * Check if user is currently dragging the handle.
  315. *
  316. * @returns {boolean} Dragging state
  317. */
  318. isDragged() {
  319. return this.dragged.length > 0;
  320. }
  321. }
  322. registerPlugin('multipleSelectionHandles', MultipleSelectionHandles);
  323. export default MultipleSelectionHandles;