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.
 
 
 
 

517 lines
16 KiB

  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. exports.__esModule = true;
  4. exports.getEventNodeFromPoint = getEventNodeFromPoint;
  5. exports.isEvent = isEvent;
  6. exports.objectsCollide = objectsCollide;
  7. exports.getBoundsForNode = getBoundsForNode;
  8. exports.default = void 0;
  9. var _contains = _interopRequireDefault(require("dom-helpers/contains"));
  10. var _closest = _interopRequireDefault(require("dom-helpers/closest"));
  11. var _listen = _interopRequireDefault(require("dom-helpers/listen"));
  12. function addEventListener(type, handler, target) {
  13. if (target === void 0) {
  14. target = document;
  15. }
  16. return (0, _listen.default)(target, type, handler, {
  17. passive: false
  18. });
  19. }
  20. function isOverContainer(container, x, y) {
  21. return !container || (0, _contains.default)(container, document.elementFromPoint(x, y));
  22. }
  23. function getEventNodeFromPoint(node, _ref) {
  24. var clientX = _ref.clientX,
  25. clientY = _ref.clientY;
  26. var target = document.elementFromPoint(clientX, clientY);
  27. return (0, _closest.default)(target, '.rbc-event', node);
  28. }
  29. function isEvent(node, bounds) {
  30. return !!getEventNodeFromPoint(node, bounds);
  31. }
  32. function getEventCoordinates(e) {
  33. var target = e;
  34. if (e.touches && e.touches.length) {
  35. target = e.touches[0];
  36. }
  37. return {
  38. clientX: target.clientX,
  39. clientY: target.clientY,
  40. pageX: target.pageX,
  41. pageY: target.pageY
  42. };
  43. }
  44. var clickTolerance = 5;
  45. var clickInterval = 250;
  46. var Selection =
  47. /*#__PURE__*/
  48. function () {
  49. function Selection(node, _temp) {
  50. var _ref2 = _temp === void 0 ? {} : _temp,
  51. _ref2$global = _ref2.global,
  52. global = _ref2$global === void 0 ? false : _ref2$global,
  53. _ref2$longPressThresh = _ref2.longPressThreshold,
  54. longPressThreshold = _ref2$longPressThresh === void 0 ? 250 : _ref2$longPressThresh;
  55. this.isDetached = false;
  56. this.container = node;
  57. this.globalMouse = !node || global;
  58. this.longPressThreshold = longPressThreshold;
  59. this._listeners = Object.create(null);
  60. this._handleInitialEvent = this._handleInitialEvent.bind(this);
  61. this._handleMoveEvent = this._handleMoveEvent.bind(this);
  62. this._handleTerminatingEvent = this._handleTerminatingEvent.bind(this);
  63. this._keyListener = this._keyListener.bind(this);
  64. this._dropFromOutsideListener = this._dropFromOutsideListener.bind(this);
  65. this._dragOverFromOutsideListener = this._dragOverFromOutsideListener.bind(this); // Fixes an iOS 10 bug where scrolling could not be prevented on the window.
  66. // https://github.com/metafizzy/flickity/issues/457#issuecomment-254501356
  67. this._removeTouchMoveWindowListener = addEventListener('touchmove', function () {}, window);
  68. this._removeKeyDownListener = addEventListener('keydown', this._keyListener);
  69. this._removeKeyUpListener = addEventListener('keyup', this._keyListener);
  70. this._removeDropFromOutsideListener = addEventListener('drop', this._dropFromOutsideListener);
  71. this._onDragOverfromOutisde = addEventListener('dragover', this._dragOverFromOutsideListener);
  72. this._addInitialEventListener();
  73. }
  74. var _proto = Selection.prototype;
  75. _proto.on = function on(type, handler) {
  76. var handlers = this._listeners[type] || (this._listeners[type] = []);
  77. handlers.push(handler);
  78. return {
  79. remove: function remove() {
  80. var idx = handlers.indexOf(handler);
  81. if (idx !== -1) handlers.splice(idx, 1);
  82. }
  83. };
  84. };
  85. _proto.emit = function emit(type) {
  86. for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  87. args[_key - 1] = arguments[_key];
  88. }
  89. var result;
  90. var handlers = this._listeners[type] || [];
  91. handlers.forEach(function (fn) {
  92. if (result === undefined) result = fn.apply(void 0, args);
  93. });
  94. return result;
  95. };
  96. _proto.teardown = function teardown() {
  97. this.isDetached = true;
  98. this.listeners = Object.create(null);
  99. this._removeTouchMoveWindowListener && this._removeTouchMoveWindowListener();
  100. this._removeInitialEventListener && this._removeInitialEventListener();
  101. this._removeEndListener && this._removeEndListener();
  102. this._onEscListener && this._onEscListener();
  103. this._removeMoveListener && this._removeMoveListener();
  104. this._removeKeyUpListener && this._removeKeyUpListener();
  105. this._removeKeyDownListener && this._removeKeyDownListener();
  106. this._removeDropFromOutsideListener && this._removeDropFromOutsideListener();
  107. };
  108. _proto.isSelected = function isSelected(node) {
  109. var box = this._selectRect;
  110. if (!box || !this.selecting) return false;
  111. return objectsCollide(box, getBoundsForNode(node));
  112. };
  113. _proto.filter = function filter(items) {
  114. var box = this._selectRect; //not selecting
  115. if (!box || !this.selecting) return [];
  116. return items.filter(this.isSelected, this);
  117. } // Adds a listener that will call the handler only after the user has pressed on the screen
  118. // without moving their finger for 250ms.
  119. ;
  120. _proto._addLongPressListener = function _addLongPressListener(handler, initialEvent) {
  121. var _this = this;
  122. var timer = null;
  123. var removeTouchMoveListener = null;
  124. var removeTouchEndListener = null;
  125. var handleTouchStart = function handleTouchStart(initialEvent) {
  126. timer = setTimeout(function () {
  127. cleanup();
  128. handler(initialEvent);
  129. }, _this.longPressThreshold);
  130. removeTouchMoveListener = addEventListener('touchmove', function () {
  131. return cleanup();
  132. });
  133. removeTouchEndListener = addEventListener('touchend', function () {
  134. return cleanup();
  135. });
  136. };
  137. var removeTouchStartListener = addEventListener('touchstart', handleTouchStart);
  138. var cleanup = function cleanup() {
  139. if (timer) {
  140. clearTimeout(timer);
  141. }
  142. if (removeTouchMoveListener) {
  143. removeTouchMoveListener();
  144. }
  145. if (removeTouchEndListener) {
  146. removeTouchEndListener();
  147. }
  148. timer = null;
  149. removeTouchMoveListener = null;
  150. removeTouchEndListener = null;
  151. };
  152. if (initialEvent) {
  153. handleTouchStart(initialEvent);
  154. }
  155. return function () {
  156. cleanup();
  157. removeTouchStartListener();
  158. };
  159. } // Listen for mousedown and touchstart events. When one is received, disable the other and setup
  160. // future event handling based on the type of event.
  161. ;
  162. _proto._addInitialEventListener = function _addInitialEventListener() {
  163. var _this2 = this;
  164. var removeMouseDownListener = addEventListener('mousedown', function (e) {
  165. _this2._removeInitialEventListener();
  166. _this2._handleInitialEvent(e);
  167. _this2._removeInitialEventListener = addEventListener('mousedown', _this2._handleInitialEvent);
  168. });
  169. var removeTouchStartListener = addEventListener('touchstart', function (e) {
  170. _this2._removeInitialEventListener();
  171. _this2._removeInitialEventListener = _this2._addLongPressListener(_this2._handleInitialEvent, e);
  172. });
  173. this._removeInitialEventListener = function () {
  174. removeMouseDownListener();
  175. removeTouchStartListener();
  176. };
  177. };
  178. _proto._dropFromOutsideListener = function _dropFromOutsideListener(e) {
  179. var _getEventCoordinates = getEventCoordinates(e),
  180. pageX = _getEventCoordinates.pageX,
  181. pageY = _getEventCoordinates.pageY,
  182. clientX = _getEventCoordinates.clientX,
  183. clientY = _getEventCoordinates.clientY;
  184. this.emit('dropFromOutside', {
  185. x: pageX,
  186. y: pageY,
  187. clientX: clientX,
  188. clientY: clientY
  189. });
  190. e.preventDefault();
  191. };
  192. _proto._dragOverFromOutsideListener = function _dragOverFromOutsideListener(e) {
  193. var _getEventCoordinates2 = getEventCoordinates(e),
  194. pageX = _getEventCoordinates2.pageX,
  195. pageY = _getEventCoordinates2.pageY,
  196. clientX = _getEventCoordinates2.clientX,
  197. clientY = _getEventCoordinates2.clientY;
  198. this.emit('dragOverFromOutside', {
  199. x: pageX,
  200. y: pageY,
  201. clientX: clientX,
  202. clientY: clientY
  203. });
  204. e.preventDefault();
  205. };
  206. _proto._handleInitialEvent = function _handleInitialEvent(e) {
  207. if (this.isDetached) {
  208. return;
  209. }
  210. var _getEventCoordinates3 = getEventCoordinates(e),
  211. clientX = _getEventCoordinates3.clientX,
  212. clientY = _getEventCoordinates3.clientY,
  213. pageX = _getEventCoordinates3.pageX,
  214. pageY = _getEventCoordinates3.pageY;
  215. var node = this.container(),
  216. collides,
  217. offsetData; // Right clicks
  218. if (e.which === 3 || e.button === 2 || !isOverContainer(node, clientX, clientY)) return;
  219. if (!this.globalMouse && node && !(0, _contains.default)(node, e.target)) {
  220. var _normalizeDistance = normalizeDistance(0),
  221. top = _normalizeDistance.top,
  222. left = _normalizeDistance.left,
  223. bottom = _normalizeDistance.bottom,
  224. right = _normalizeDistance.right;
  225. offsetData = getBoundsForNode(node);
  226. collides = objectsCollide({
  227. top: offsetData.top - top,
  228. left: offsetData.left - left,
  229. bottom: offsetData.bottom + bottom,
  230. right: offsetData.right + right
  231. }, {
  232. top: pageY,
  233. left: pageX
  234. });
  235. if (!collides) return;
  236. }
  237. var result = this.emit('beforeSelect', this._initialEventData = {
  238. isTouch: /^touch/.test(e.type),
  239. x: pageX,
  240. y: pageY,
  241. clientX: clientX,
  242. clientY: clientY
  243. });
  244. if (result === false) return;
  245. switch (e.type) {
  246. case 'mousedown':
  247. this._removeEndListener = addEventListener('mouseup', this._handleTerminatingEvent);
  248. this._onEscListener = addEventListener('keydown', this._handleTerminatingEvent);
  249. this._removeMoveListener = addEventListener('mousemove', this._handleMoveEvent);
  250. break;
  251. case 'touchstart':
  252. this._handleMoveEvent(e);
  253. this._removeEndListener = addEventListener('touchend', this._handleTerminatingEvent);
  254. this._removeMoveListener = addEventListener('touchmove', this._handleMoveEvent);
  255. break;
  256. default:
  257. break;
  258. }
  259. };
  260. _proto._handleTerminatingEvent = function _handleTerminatingEvent(e) {
  261. var _getEventCoordinates4 = getEventCoordinates(e),
  262. pageX = _getEventCoordinates4.pageX,
  263. pageY = _getEventCoordinates4.pageY;
  264. this.selecting = false;
  265. this._removeEndListener && this._removeEndListener();
  266. this._removeMoveListener && this._removeMoveListener();
  267. if (!this._initialEventData) return;
  268. var inRoot = !this.container || (0, _contains.default)(this.container(), e.target);
  269. var bounds = this._selectRect;
  270. var click = this.isClick(pageX, pageY);
  271. this._initialEventData = null;
  272. if (e.key === 'Escape') {
  273. return this.emit('reset');
  274. }
  275. if (!inRoot) {
  276. return this.emit('reset');
  277. }
  278. if (click && inRoot) {
  279. return this._handleClickEvent(e);
  280. } // User drag-clicked in the Selectable area
  281. if (!click) return this.emit('select', bounds);
  282. };
  283. _proto._handleClickEvent = function _handleClickEvent(e) {
  284. var _getEventCoordinates5 = getEventCoordinates(e),
  285. pageX = _getEventCoordinates5.pageX,
  286. pageY = _getEventCoordinates5.pageY,
  287. clientX = _getEventCoordinates5.clientX,
  288. clientY = _getEventCoordinates5.clientY;
  289. var now = new Date().getTime();
  290. if (this._lastClickData && now - this._lastClickData.timestamp < clickInterval) {
  291. // Double click event
  292. this._lastClickData = null;
  293. return this.emit('doubleClick', {
  294. x: pageX,
  295. y: pageY,
  296. clientX: clientX,
  297. clientY: clientY
  298. });
  299. } // Click event
  300. this._lastClickData = {
  301. timestamp: now
  302. };
  303. return this.emit('click', {
  304. x: pageX,
  305. y: pageY,
  306. clientX: clientX,
  307. clientY: clientY
  308. });
  309. };
  310. _proto._handleMoveEvent = function _handleMoveEvent(e) {
  311. if (this._initialEventData === null || this.isDetached) {
  312. return;
  313. }
  314. var _this$_initialEventDa = this._initialEventData,
  315. x = _this$_initialEventDa.x,
  316. y = _this$_initialEventDa.y;
  317. var _getEventCoordinates6 = getEventCoordinates(e),
  318. pageX = _getEventCoordinates6.pageX,
  319. pageY = _getEventCoordinates6.pageY;
  320. var w = Math.abs(x - pageX);
  321. var h = Math.abs(y - pageY);
  322. var left = Math.min(pageX, x),
  323. top = Math.min(pageY, y),
  324. old = this.selecting; // Prevent emitting selectStart event until mouse is moved.
  325. // in Chrome on Windows, mouseMove event may be fired just after mouseDown event.
  326. if (this.isClick(pageX, pageY) && !old && !(w || h)) {
  327. return;
  328. }
  329. this.selecting = true;
  330. this._selectRect = {
  331. top: top,
  332. left: left,
  333. x: pageX,
  334. y: pageY,
  335. right: left + w,
  336. bottom: top + h
  337. };
  338. if (!old) {
  339. this.emit('selectStart', this._initialEventData);
  340. }
  341. if (!this.isClick(pageX, pageY)) this.emit('selecting', this._selectRect);
  342. e.preventDefault();
  343. };
  344. _proto._keyListener = function _keyListener(e) {
  345. this.ctrl = e.metaKey || e.ctrlKey;
  346. };
  347. _proto.isClick = function isClick(pageX, pageY) {
  348. var _this$_initialEventDa2 = this._initialEventData,
  349. x = _this$_initialEventDa2.x,
  350. y = _this$_initialEventDa2.y,
  351. isTouch = _this$_initialEventDa2.isTouch;
  352. return !isTouch && Math.abs(pageX - x) <= clickTolerance && Math.abs(pageY - y) <= clickTolerance;
  353. };
  354. return Selection;
  355. }();
  356. /**
  357. * Resolve the disance prop from either an Int or an Object
  358. * @return {Object}
  359. */
  360. function normalizeDistance(distance) {
  361. if (distance === void 0) {
  362. distance = 0;
  363. }
  364. if (typeof distance !== 'object') distance = {
  365. top: distance,
  366. left: distance,
  367. right: distance,
  368. bottom: distance
  369. };
  370. return distance;
  371. }
  372. /**
  373. * Given two objects containing "top", "left", "offsetWidth" and "offsetHeight"
  374. * properties, determine if they collide.
  375. * @param {Object|HTMLElement} a
  376. * @param {Object|HTMLElement} b
  377. * @return {bool}
  378. */
  379. function objectsCollide(nodeA, nodeB, tolerance) {
  380. if (tolerance === void 0) {
  381. tolerance = 0;
  382. }
  383. var _getBoundsForNode = getBoundsForNode(nodeA),
  384. aTop = _getBoundsForNode.top,
  385. aLeft = _getBoundsForNode.left,
  386. _getBoundsForNode$rig = _getBoundsForNode.right,
  387. aRight = _getBoundsForNode$rig === void 0 ? aLeft : _getBoundsForNode$rig,
  388. _getBoundsForNode$bot = _getBoundsForNode.bottom,
  389. aBottom = _getBoundsForNode$bot === void 0 ? aTop : _getBoundsForNode$bot;
  390. var _getBoundsForNode2 = getBoundsForNode(nodeB),
  391. bTop = _getBoundsForNode2.top,
  392. bLeft = _getBoundsForNode2.left,
  393. _getBoundsForNode2$ri = _getBoundsForNode2.right,
  394. bRight = _getBoundsForNode2$ri === void 0 ? bLeft : _getBoundsForNode2$ri,
  395. _getBoundsForNode2$bo = _getBoundsForNode2.bottom,
  396. bBottom = _getBoundsForNode2$bo === void 0 ? bTop : _getBoundsForNode2$bo;
  397. return !( // 'a' bottom doesn't touch 'b' top
  398. aBottom - tolerance < bTop || // 'a' top doesn't touch 'b' bottom
  399. aTop + tolerance > bBottom || // 'a' right doesn't touch 'b' left
  400. aRight - tolerance < bLeft || // 'a' left doesn't touch 'b' right
  401. aLeft + tolerance > bRight);
  402. }
  403. /**
  404. * Given a node, get everything needed to calculate its boundaries
  405. * @param {HTMLElement} node
  406. * @return {Object}
  407. */
  408. function getBoundsForNode(node) {
  409. if (!node.getBoundingClientRect) return node;
  410. var rect = node.getBoundingClientRect(),
  411. left = rect.left + pageOffset('left'),
  412. top = rect.top + pageOffset('top');
  413. return {
  414. top: top,
  415. left: left,
  416. right: (node.offsetWidth || 0) + left,
  417. bottom: (node.offsetHeight || 0) + top
  418. };
  419. }
  420. function pageOffset(dir) {
  421. if (dir === 'left') return window.pageXOffset || document.body.scrollLeft || 0;
  422. if (dir === 'top') return window.pageYOffset || document.body.scrollTop || 0;
  423. }
  424. var _default = Selection;
  425. exports.default = _default;