Răsfoiți Sursa

implemented elementFromPoint. Get all clickable elements under the mouse position.

Ievgen Naida 5 ani în urmă
părinte
comite
9c9795ab81

+ 1 - 0
.vscode/settings.json

@@ -13,6 +13,7 @@
         "esnext",
         "esnext",
         "khtml",
         "khtml",
         "pixelated",
         "pixelated",
+        "prio",
         "sourcemap",
         "sourcemap",
         "timechanged",
         "timechanged",
         "tslib"
         "tslib"

+ 369 - 183
lib/animation-timeline.js

@@ -91,7 +91,7 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/
 /******/
 /******/
 /******/
 /******/ 	// Load entry module and return exports
 /******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 11);
+/******/ 	return __webpack_require__(__webpack_require__.s = 10);
 /******/ })
 /******/ })
 /************************************************************************/
 /************************************************************************/
 /******/ ([
 /******/ ([
@@ -156,12 +156,6 @@ return /******/ (function(modules) { // webpackBootstrap
 
 
 /***/ }),
 /***/ }),
 /* 10 */
 /* 10 */
-/***/ (function(module, exports) {
-
-
-
-/***/ }),
-/* 11 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 
 "use strict";
 "use strict";
@@ -171,26 +165,28 @@ __webpack_require__.r(__webpack_exports__);
 // EXPORTS
 // EXPORTS
 __webpack_require__.d(__webpack_exports__, "Timeline", function() { return /* reexport */ timeline_Timeline; });
 __webpack_require__.d(__webpack_exports__, "Timeline", function() { return /* reexport */ timeline_Timeline; });
 __webpack_require__.d(__webpack_exports__, "TimelineModel", function() { return /* reexport */ TimelineModel; });
 __webpack_require__.d(__webpack_exports__, "TimelineModel", function() { return /* reexport */ TimelineModel; });
-__webpack_require__.d(__webpack_exports__, "TimelineOptions", function() { return /* reexport */ timelineOptions_TimelineOptions; });
 __webpack_require__.d(__webpack_exports__, "TimelineRow", function() { return /* reexport */ timelineRow["TimelineRow"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineRow", function() { return /* reexport */ timelineRow["TimelineRow"]; });
-__webpack_require__.d(__webpack_exports__, "TimelineStyleUtils", function() { return /* reexport */ TimelineStyleUtils; });
 __webpack_require__.d(__webpack_exports__, "TimelineKeyframe", function() { return /* reexport */ timelineKeyframe["TimelineKeyframe"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineKeyframe", function() { return /* reexport */ timelineKeyframe["TimelineKeyframe"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineEventsEmitter", function() { return /* reexport */ TimelineEventsEmitter; });
 __webpack_require__.d(__webpack_exports__, "TimelineEventsEmitter", function() { return /* reexport */ TimelineEventsEmitter; });
+__webpack_require__.d(__webpack_exports__, "TimelineOptions", function() { return /* reexport */ timelineOptions_TimelineOptions; });
+__webpack_require__.d(__webpack_exports__, "TimelineStyleUtils", function() { return /* reexport */ TimelineStyleUtils; });
+__webpack_require__.d(__webpack_exports__, "TimelineKeyframeStyle", function() { return /* reexport */ timelineKeyframeStyle["TimelineKeyframeStyle"]; });
+__webpack_require__.d(__webpack_exports__, "TimelineRowStyle", function() { return /* reexport */ timelineRowStyle["TimelineRowStyle"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineUtils", function() { return /* reexport */ TimelineUtils; });
 __webpack_require__.d(__webpack_exports__, "TimelineUtils", function() { return /* reexport */ TimelineUtils; });
-__webpack_require__.d(__webpack_exports__, "TimelineDraggableData", function() { return /* reexport */ timelineDraggableData["TimelineDraggableData"]; });
-__webpack_require__.d(__webpack_exports__, "SelectionTuple", function() { return /* reexport */ selectionTuple["SelectionTuple"]; });
+__webpack_require__.d(__webpack_exports__, "TimelineClickableElement", function() { return /* reexport */ timelineClickableElement["TimelineClickableElement"]; });
 __webpack_require__.d(__webpack_exports__, "Selectable", function() { return /* reexport */ selectable["Selectable"]; });
 __webpack_require__.d(__webpack_exports__, "Selectable", function() { return /* reexport */ selectable["Selectable"]; });
 __webpack_require__.d(__webpack_exports__, "RowsCalculationsResults", function() { return /* reexport */ rowsCalculationsResults["RowsCalculationsResults"]; });
 __webpack_require__.d(__webpack_exports__, "RowsCalculationsResults", function() { return /* reexport */ rowsCalculationsResults["RowsCalculationsResults"]; });
+__webpack_require__.d(__webpack_exports__, "RowSize", function() { return /* reexport */ rowsCalculationsResults["RowSize"]; });
 __webpack_require__.d(__webpack_exports__, "CutBoundsRect", function() { return /* reexport */ cutBoundsRect["CutBoundsRect"]; });
 __webpack_require__.d(__webpack_exports__, "CutBoundsRect", function() { return /* reexport */ cutBoundsRect["CutBoundsRect"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineSelectedEvent", function() { return /* reexport */ timelineSelectedEvent["TimelineSelectedEvent"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineSelectedEvent", function() { return /* reexport */ timelineSelectedEvent["TimelineSelectedEvent"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineScrollEvent", function() { return /* reexport */ timelineScrollEvent["TimelineScrollEvent"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineScrollEvent", function() { return /* reexport */ timelineScrollEvent["TimelineScrollEvent"]; });
+__webpack_require__.d(__webpack_exports__, "TimelineClickEvent", function() { return /* reexport */ TimelineClickEvent; });
+__webpack_require__.d(__webpack_exports__, "TimelineDragEvent", function() { return /* reexport */ TimelineDragEvent; });
+__webpack_require__.d(__webpack_exports__, "TimelineEvents", function() { return /* reexport */ TimelineEvents; });
 __webpack_require__.d(__webpack_exports__, "TimelineConsts", function() { return /* reexport */ TimelineConsts; });
 __webpack_require__.d(__webpack_exports__, "TimelineConsts", function() { return /* reexport */ TimelineConsts; });
-__webpack_require__.d(__webpack_exports__, "TimelineKeyframeStyle", function() { return /* reexport */ timelineKeyframeStyle["TimelineKeyframeStyle"]; });
-__webpack_require__.d(__webpack_exports__, "TimelineRowStyle", function() { return /* reexport */ timelineRowStyle["TimelineRowStyle"]; });
 __webpack_require__.d(__webpack_exports__, "TimelineKeyframeShape", function() { return /* reexport */ TimelineKeyframeShape; });
 __webpack_require__.d(__webpack_exports__, "TimelineKeyframeShape", function() { return /* reexport */ TimelineKeyframeShape; });
 __webpack_require__.d(__webpack_exports__, "TimelineInteractionMode", function() { return /* reexport */ TimelineInteractionMode; });
 __webpack_require__.d(__webpack_exports__, "TimelineInteractionMode", function() { return /* reexport */ TimelineInteractionMode; });
-__webpack_require__.d(__webpack_exports__, "TimelineEvents", function() { return /* reexport */ TimelineEvents; });
-__webpack_require__.d(__webpack_exports__, "TimelineDraggableType", function() { return /* reexport */ TimelineDraggableType; });
+__webpack_require__.d(__webpack_exports__, "TimelineElementType", function() { return /* reexport */ TimelineElementType; });
 __webpack_require__.d(__webpack_exports__, "TimelineCursorType", function() { return /* reexport */ TimelineCursorType; });
 __webpack_require__.d(__webpack_exports__, "TimelineCursorType", function() { return /* reexport */ TimelineCursorType; });
 __webpack_require__.d(__webpack_exports__, "TimelineCapShape", function() { return /* reexport */ TimelineCapShape; });
 __webpack_require__.d(__webpack_exports__, "TimelineCapShape", function() { return /* reexport */ TimelineCapShape; });
 
 
@@ -285,7 +281,6 @@ var TimelineUtils = /*#__PURE__*/function () {
     key: "isOverlap",
     key: "isOverlap",
     value: function isOverlap(x, y, rectangle) {
     value: function isOverlap(x, y, rectangle) {
       if (!rectangle) {
       if (!rectangle) {
-        console.error('rectangle argument cannot be empty');
         return false;
         return false;
       }
       }
 
 
@@ -700,14 +695,14 @@ var TimelineStyleUtils = /*#__PURE__*/function () {
 
 
   return TimelineStyleUtils;
   return TimelineStyleUtils;
 }();
 }();
-// CONCATENATED MODULE: ./src/enums/timelineDraggableType.ts
-var TimelineDraggableType;
-
-(function (TimelineDraggableType) {
-  TimelineDraggableType["keyframe"] = "keyframe";
-  TimelineDraggableType["keyframes"] = "keyframes";
-  TimelineDraggableType["timeline"] = "timeline";
-})(TimelineDraggableType || (TimelineDraggableType = {}));
+// CONCATENATED MODULE: ./src/enums/timelineElementType.ts
+var TimelineElementType;
+
+(function (TimelineElementType) {
+  TimelineElementType["Timeline"] = "timeline";
+  TimelineElementType["Keyframe"] = "keyframe";
+  TimelineElementType["Stripe"] = "stripe";
+})(TimelineElementType || (TimelineElementType = {}));
 // CONCATENATED MODULE: ./src/enums/timelineEvents.ts
 // CONCATENATED MODULE: ./src/enums/timelineEvents.ts
 var TimelineEvents;
 var TimelineEvents;
 
 
@@ -728,6 +723,50 @@ var TimelineInteractionMode;
   TimelineInteractionMode["Selection"] = "selection";
   TimelineInteractionMode["Selection"] = "selection";
   TimelineInteractionMode["Pan"] = "pan";
   TimelineInteractionMode["Pan"] = "pan";
 })(TimelineInteractionMode || (TimelineInteractionMode = {}));
 })(TimelineInteractionMode || (TimelineInteractionMode = {}));
+// CONCATENATED MODULE: ./src/utils/events/timelineClickEvent.ts
+function timelineClickEvent_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function timelineClickEvent_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+
+function timelineClickEvent_createClass(Constructor, protoProps, staticProps) { if (protoProps) timelineClickEvent_defineProperties(Constructor.prototype, protoProps); if (staticProps) timelineClickEvent_defineProperties(Constructor, staticProps); return Constructor; }
+
+function timelineClickEvent_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var TimelineClickEvent = /*#__PURE__*/function () {
+  function TimelineClickEvent() {
+    timelineClickEvent_classCallCheck(this, TimelineClickEvent);
+
+    timelineClickEvent_defineProperty(this, "args", void 0);
+
+    timelineClickEvent_defineProperty(this, "pos", new DOMPoint());
+
+    timelineClickEvent_defineProperty(this, "val", void 0);
+
+    timelineClickEvent_defineProperty(this, "elements", void 0);
+
+    timelineClickEvent_defineProperty(this, "target", void 0);
+
+    timelineClickEvent_defineProperty(this, "_prevented", false);
+  }
+
+  timelineClickEvent_createClass(TimelineClickEvent, [{
+    key: "preventDefault",
+
+    /**
+     * Prevent default click logic.
+     */
+    value: function preventDefault() {
+      this._prevented = true;
+    }
+  }, {
+    key: "isPrevented",
+    value: function isPrevented() {
+      return this._prevented;
+    }
+  }]);
+
+  return TimelineClickEvent;
+}();
 // CONCATENATED MODULE: ./src/timeline.ts
 // CONCATENATED MODULE: ./src/timeline.ts
 function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
 function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
 
 
@@ -768,6 +807,7 @@ function timeline_defineProperty(obj, key, value) { if (key in obj) { Object.def
 
 
 
 
 
 
+
 var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
 var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
   _inherits(Timeline, _TimelineEventsEmitte);
   _inherits(Timeline, _TimelineEventsEmitte);
 
 
@@ -982,39 +1022,76 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
     });
     });
 
 
     timeline_defineProperty(_assertThisInitialized(_this), "handleMouseDownEvent", function (args) {
     timeline_defineProperty(_assertThisInitialized(_this), "handleMouseDownEvent", function (args) {
-      var isDoubleClick = Date.now() - _this.lastClickTime < _this.consts.doubleClickTimeoutMs;
+      var isDoubleClick = Date.now() - _this.lastClickTime < _this.consts.doubleClickTimeoutMs; // Prevent drag of the canvas if canvas is selected as text:
 
 
-      _this.lastClickTime = Date.now(); // Prevent drag of the canvas if canvas is selected as text:
 
 
       TimelineUtils.clearBrowserSelection();
       TimelineUtils.clearBrowserSelection();
       _this.startPos = _this.trackMousePos(_this.canvas, args);
       _this.startPos = _this.trackMousePos(_this.canvas, args);
+
+      if (!_this.startPos) {
+        return;
+      }
+
       _this.scrollStartPos = {
       _this.scrollStartPos = {
         x: _this.scrollContainer.scrollLeft,
         x: _this.scrollContainer.scrollLeft,
         y: _this.scrollContainer.scrollTop
         y: _this.scrollContainer.scrollTop
       };
       };
 
 
+      var elements = _this.elementFromPoint(_this.startPos, Math.max(2, _this.startPos.radius));
+
+      var target = _this.findDraggable(elements, _this.startPos.val);
+
+      var event = new TimelineClickEvent();
+      event.pos = _this.startPos;
+      event.val = _this.startPos.val;
+      event.args = args; // all elements under the click:
+
+      event.elements = elements; // target element.
+
+      event.target = target;
+
       if (isDoubleClick) {
       if (isDoubleClick) {
-        _get((_thisSuper2 = _assertThisInitialized(_this), _getPrototypeOf(Timeline.prototype)), "emit", _thisSuper2).call(_thisSuper2, TimelineEvents.DoubleClick, _this.startPos);
+        _get((_thisSuper2 = _assertThisInitialized(_this), _getPrototypeOf(Timeline.prototype)), "emit", _thisSuper2).call(_thisSuper2, TimelineEvents.DoubleClick, event);
 
 
         return;
         return;
       }
       }
 
 
-      _get((_thisSuper3 = _assertThisInitialized(_this), _getPrototypeOf(Timeline.prototype)), "emit", _thisSuper3).call(_thisSuper3, TimelineEvents.MouseDown, _this.startPos);
+      _get((_thisSuper3 = _assertThisInitialized(_this), _getPrototypeOf(Timeline.prototype)), "emit", _thisSuper3).call(_thisSuper3, TimelineEvents.MouseDown, event);
 
 
       _this.clickTimeout = Date.now();
       _this.clickTimeout = Date.now();
-      _this.currentPos = _this.startPos;
-      _this.drag = _this.getDraggable(_this.currentPos); // Select keyframes on mouse down
+      _this.lastClickTime = Date.now();
+
+      if (event.isPrevented()) {
+        _this.cleanUpSelection();
+
+        return;
+      }
 
 
-      if (_this.drag) {
-        if (_this.drag.type === TimelineDraggableType.keyframe) {
+      _this.currentPos = _this.startPos; // Select keyframes on mouse down
+
+      if (target) {
+        _this.drag = {
+          changed: false,
+          target: target,
+          val: target.val,
+          type: target.type,
+          elements: []
+        };
+
+        if (target.type === TimelineElementType.Keyframe) {
           _this.startedDragWithCtrl = _this.controlKeyPressed(args);
           _this.startedDragWithCtrl = _this.controlKeyPressed(args);
           _this.startedDragWithShiftKey = args.shiftKey; // get all related selected keyframes if we are selecting one.
           _this.startedDragWithShiftKey = args.shiftKey; // get all related selected keyframes if we are selecting one.
 
 
-          if (!_this.drag.keyframe.selected && !_this.controlKeyPressed(args) && !args.shiftKey) {
-            _this.performSelection(true, _this.drag.keyframe, 'keyframe');
+          if (!target.keyframe.selected && !_this.controlKeyPressed(args) && !args.shiftKey) {
+            _this.performSelection(true, target.keyframe);
           }
           }
 
 
-          _this.drag.keyframes = _this.getSelectedKeyframes();
+          _this.drag.elements = _this.getSelectedElements();
+        } else if (target.type === TimelineElementType.Stripe) {
+          var keyframes = _this.drag.target.row.keyframes;
+          _this.drag.elements = keyframes && Array.isArray(keyframes) ? keyframes.map(function (keyframe) {
+            return _this.convertToElement(_this.drag.target.row, keyframe);
+          }) : [];
         }
         }
       }
       }
 
 
@@ -1049,18 +1126,18 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
             var convertedVal = _this.mousePosToVal(_this.currentPos.x, true); //redraw();
             var convertedVal = _this.mousePosToVal(_this.currentPos.x, true); //redraw();
 
 
 
 
-            if (_this.drag.type === TimelineDraggableType.timeline) {
+            if (_this.drag.type === TimelineElementType.Timeline) {
               isChanged = _this.setTimeInternal(convertedVal, 'user') || isChanged;
               isChanged = _this.setTimeInternal(convertedVal, 'user') || isChanged;
-            } else if ((_this.drag.type == TimelineDraggableType.keyframe || _this.drag.type == TimelineDraggableType.keyframes) && _this.drag.keyframes) {
+            } else if ((_this.drag.type == TimelineElementType.Keyframe || _this.drag.type == TimelineElementType.Stripe) && _this.drag.elements) {
               var offset = Math.floor(convertedVal - _this.drag.val);
               var offset = Math.floor(convertedVal - _this.drag.val);
 
 
               if (Math.abs(offset) > 0) {
               if (Math.abs(offset) > 0) {
                 // don't allow to move less than zero.
                 // don't allow to move less than zero.
-                _this.drag.keyframes.forEach(function (p) {
+                _this.drag.elements.forEach(function (p) {
                   if (_this.options.snapAllKeyframesOnMove) {
                   if (_this.options.snapAllKeyframesOnMove) {
-                    var toSet = _this.snapVal(p.val);
+                    var toSet = _this.snapVal(p.keyframe.val);
 
 
-                    isChanged = _this.setKeyframePos(p, toSet) || isChanged;
+                    isChanged = _this.setKeyframePos(p.keyframe, toSet) || isChanged;
                   }
                   }
 
 
                   var newPosition = p.val + offset;
                   var newPosition = p.val + offset;
@@ -1072,25 +1149,25 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
 
 
                 if (Math.abs(offset) > 0) {
                 if (Math.abs(offset) > 0) {
                   // don't allow to move less than zero.
                   // don't allow to move less than zero.
-                  _this.drag.keyframes.forEach(function (keyframe) {
-                    var toSet = keyframe.val + offset;
-                    isChanged = _this.setKeyframePos(keyframe, toSet) || isChanged;
+                  _this.drag.elements.forEach(function (element) {
+                    var toSet = element.keyframe.val + offset;
+                    isChanged = _this.setKeyframePos(element.keyframe, toSet) || isChanged;
+
+                    if (isChanged) {
+                      element.val = element.keyframe.val;
+                    }
                   });
                   });
                 }
                 }
 
 
                 if (isChanged) {
                 if (isChanged) {
                   if (!_this.drag.changed) {
                   if (!_this.drag.changed) {
-                    _this.emit('dragStarted', {
-                      keyframes: _this.drag.keyframes
-                    });
+                    _this.emitDragStartedEvent();
                   }
                   }
 
 
                   _this.drag.changed = true;
                   _this.drag.changed = true;
                   _this.drag.val += offset;
                   _this.drag.val += offset;
 
 
-                  _this.emit('drag', {
-                    keyframes: _this.drag.keyframes
-                  });
+                  _this.emitDragEvent();
                 }
                 }
               }
               }
             }
             }
@@ -1113,18 +1190,21 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
           _this.redraw();
           _this.redraw();
         }
         }
       } else if (!isTouch) {
       } else if (!isTouch) {
-        var draggable = _this.getDraggable(_this.currentPos);
+        // TODO: mouse over event
+        var elements = _this.elementFromPoint(_this.currentPos, Math.max(2, _this.currentPos.radius));
+
+        var target = _this.findDraggable(elements, _this.currentPos.val);
 
 
         _this.setCursor('default');
         _this.setCursor('default');
 
 
-        if (draggable) {
+        if (target) {
           var cursor = null;
           var cursor = null;
 
 
-          if (draggable.type === TimelineDraggableType.keyframes) {
+          if (target.type === TimelineElementType.Stripe) {
             cursor = cursor || TimelineCursorType.EWResize;
             cursor = cursor || TimelineCursorType.EWResize;
-          } else if (draggable.type == 'keyframe') {
+          } else if (target.type == TimelineElementType.Keyframe) {
             cursor = cursor || TimelineCursorType.Pointer;
             cursor = cursor || TimelineCursorType.Pointer;
-          } else {
+          } else if (target.type == TimelineElementType.Timeline) {
             cursor = cursor || TimelineCursorType.EWResize;
             cursor = cursor || TimelineCursorType.EWResize;
           }
           }
 
 
@@ -1148,7 +1228,7 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
         if (_this.selectionRect && _this.selectionRect.height <= 2 && _this.selectionRect.width <= 2 || !_this.clickTimeoutIsOver() || _this.drag && _this.startedDragWithCtrl || _this.drag && _this.startedDragWithShiftKey) {
         if (_this.selectionRect && _this.selectionRect.height <= 2 && _this.selectionRect.width <= 2 || !_this.clickTimeoutIsOver() || _this.drag && _this.startedDragWithCtrl || _this.drag && _this.startedDragWithShiftKey) {
           _this.performClick(pos, args, _this.drag);
           _this.performClick(pos, args, _this.drag);
         } else if (!_this.drag && _this.selectionRect && _this.selectionRectEnabled) {
         } else if (!_this.drag && _this.selectionRect && _this.selectionRectEnabled) {
-          _this.performSelection(true, _this.selectionRect, 'rectangle', args.shiftKey);
+          _this.performSelection(true, _this.selectionRect, args.shiftKey);
         }
         }
 
 
         _this.cleanUpSelection();
         _this.cleanUpSelection();
@@ -1162,7 +1242,7 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
       if (_this.valToPx(_this.val, true) > _this.scrollContainer.scrollWidth) {
       if (_this.valToPx(_this.val, true) > _this.scrollContainer.scrollWidth) {
         _this.rescale();
         _this.rescale();
 
 
-        if (!_this.isPanStarted && _this.drag && _this.drag.type !== TimelineDraggableType.timeline) {
+        if (!_this.isPanStarted && _this.drag && _this.drag.type !== TimelineElementType.Timeline) {
           _this.scrollLeft();
           _this.scrollLeft();
         }
         }
       }
       }
@@ -1274,7 +1354,8 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
   }, {
   }, {
     key: "dispose",
     key: "dispose",
     value: function dispose() {
     value: function dispose() {
-      this._subscriptions = null;
+      // Unsubscribe all events
+      this.offAll();
       this.container = null;
       this.container = null;
       this.canvas = null;
       this.canvas = null;
       this.scrollContainer = null;
       this.scrollContainer = null;
@@ -1295,7 +1376,8 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
       window.removeEventListener('mousemove', this.handleMouseMoveEvent);
       window.removeEventListener('mousemove', this.handleMouseMoveEvent);
       window.removeEventListener('touchmove', this.handleMouseMoveEvent);
       window.removeEventListener('touchmove', this.handleMouseMoveEvent);
       window.removeEventListener('mouseup', this.handleMouseUpEvent);
       window.removeEventListener('mouseup', this.handleMouseUpEvent);
-      window.removeEventListener('touchend', this.handleMouseUpEvent);
+      window.removeEventListener('touchend', this.handleMouseUpEvent); // Stop times
+
       this.stopAutoPan();
       this.stopAutoPan();
       this.clearScrollFinishedTimer();
       this.clearScrollFinishedTimer();
     }
     }
@@ -1312,17 +1394,17 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
     value: function performClick(pos, args, drag) {
     value: function performClick(pos, args, drag) {
       var isChanged = false;
       var isChanged = false;
 
 
-      if (drag && drag.type === TimelineDraggableType.keyframe) {
+      if (drag && drag.type === TimelineElementType.Keyframe) {
         var isSelected = true;
         var isSelected = true;
 
 
         if (this.startedDragWithCtrl && this.controlKeyPressed(args) || this.startedDragWithShiftKey && args.shiftKey) {
         if (this.startedDragWithCtrl && this.controlKeyPressed(args) || this.startedDragWithShiftKey && args.shiftKey) {
           if (this.controlKeyPressed(args)) {
           if (this.controlKeyPressed(args)) {
-            isSelected = !drag.keyframe.selected;
+            isSelected = !drag.target.keyframe.selected;
           }
           }
         } // Reverse selected keyframe selection by a click:
         } // Reverse selected keyframe selection by a click:
 
 
 
 
-        isChanged = this.performSelection(isSelected, this.drag.keyframe, 'keyframe', this.controlKeyPressed(args) || args.shiftKey) || isChanged;
+        isChanged = this.performSelection(isSelected, this.drag.target.keyframe, this.controlKeyPressed(args) || args.shiftKey) || isChanged;
 
 
         if (args.shiftKey) {
         if (args.shiftKey) {
           // change timeline pos:
           // change timeline pos:
@@ -1384,27 +1466,35 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
       }
       }
     }
     }
   }, {
   }, {
-    key: "getSelectedKeyframes",
-    value: function getSelectedKeyframes() {
+    key: "convertToElement",
+    value: function convertToElement(row, keyframe) {
+      var data = {
+        type: TimelineElementType.Keyframe,
+        val: keyframe.val,
+        keyframe: keyframe,
+        row: row
+      };
+      return data;
+    }
+  }, {
+    key: "getSelectedElements",
+    value: function getSelectedElements() {
       var _this2 = this;
       var _this2 = this;
 
 
-      if (!this.selectedKeyframes) {
-        this.selectedKeyframes = [];
-      }
-
-      this.selectedKeyframes.length = 0;
-      this.forEachKeyframe(function (keyframe) {
+      var selected = [];
+      this.forEachKeyframe(function (keyframe, index, rowModel) {
         if (keyframe && keyframe.selected) {
         if (keyframe && keyframe.selected) {
-          _this2.selectedKeyframes.push(keyframe);
+          selected.push(_this2.convertToElement(rowModel.row, keyframe));
         }
         }
+
+        return false;
       });
       });
-      return this.selectedKeyframes;
+      return selected;
     }
     }
     /**
     /**
      * Do the selection.
      * Do the selection.
      * @param {boolean} isSelected
      * @param {boolean} isSelected
      * @param {object} selector can be a rectangle or a keyframe object.
      * @param {object} selector can be a rectangle or a keyframe object.
-     * @param {string} mode selector mode. keyframe | rectangle | all
      * @param {boolean} ignoreOthers value indicating whether all other object should be reversed.
      * @param {boolean} ignoreOthers value indicating whether all other object should be reversed.
      * @return {boolean} isChanged
      * @return {boolean} isChanged
      */
      */
@@ -1416,11 +1506,10 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
 
 
       var isSelected = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
       var isSelected = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
       var selector = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
       var selector = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
-      var mode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'all';
-      var ignoreOthers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
-      var deselectionMode = false;
+      var ignoreOthers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
+      var deselectionMode = false; // TODO: simplify
 
 
-      if (mode == 'all') {
+      if (!selector) {
         if (!isSelected) {
         if (!isSelected) {
           isSelected = false;
           isSelected = false;
         }
         }
@@ -1434,7 +1523,7 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
         var keyframePos = _this3.getKeyframePosition(keyframe, rowSize);
         var keyframePos = _this3.getKeyframePosition(keyframe, rowSize);
 
 
         if (keyframePos) {
         if (keyframePos) {
-          if (mode == 'keyframe' && selector === keyframe || mode == 'rectangle' && selector && TimelineUtils.isOverlap(keyframePos.x, keyframePos.y, selector)) {
+          if (selector && selector === keyframe || TimelineUtils.isOverlap(keyframePos.x, keyframePos.y, selector)) {
             if (keyframe.selected != isSelected) {
             if (keyframe.selected != isSelected) {
               keyframe.selected = isSelected;
               keyframe.selected = isSelected;
               isChanged = true;
               isChanged = true;
@@ -1451,6 +1540,8 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
             }
             }
           }
           }
         }
         }
+
+        return false;
       });
       });
 
 
       if (isChanged) {
       if (isChanged) {
@@ -1466,11 +1557,13 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
   }, {
   }, {
     key: "forEachKeyframe",
     key: "forEachKeyframe",
     value: function forEachKeyframe(callback) {
     value: function forEachKeyframe(callback) {
+      var calculateStripesBounds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+
       if (!this.model) {
       if (!this.model) {
         return;
         return;
       }
       }
 
 
-      var model = this.calculateRowsBounds(false);
+      var model = this.calculateRowsBounds(calculateStripesBounds);
 
 
       if (!model) {
       if (!model) {
         return;
         return;
@@ -1490,9 +1583,13 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
         var nextRow = true;
         var nextRow = true;
         row.keyframes.filter(function (p) {
         row.keyframes.filter(function (p) {
           return p && !p.hidden;
           return p && !p.hidden;
-        }).forEach(function (keyframe, keyframeIndex) {
+        }).find(function (keyframe, keyframeIndex) {
           if (callback && keyframe) {
           if (callback && keyframe) {
-            callback(keyframe, keyframeIndex, rowSize, index, nextRow);
+            var isBreak = callback(keyframe, keyframeIndex, rowSize, index, nextRow);
+
+            if (isBreak) {
+              return true;
+            }
           }
           }
 
 
           nextRow = false;
           nextRow = false;
@@ -1525,12 +1622,7 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
   }, {
   }, {
     key: "cleanUpSelection",
     key: "cleanUpSelection",
     value: function cleanUpSelection() {
     value: function cleanUpSelection() {
-      if (this.drag && this.drag.changed) {
-        this.emit(TimelineEvents.DragFinished, {
-          keyframes: this.drag.keyframes
-        });
-      }
-
+      this.emitDragFinishedEvent();
       this.startPos = null;
       this.startPos = null;
       this.drag = null;
       this.drag = null;
       this.startedDragWithCtrl = false;
       this.startedDragWithCtrl = false;
@@ -2256,6 +2348,8 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
 
 
           _this7.ctx.restore();
           _this7.ctx.restore();
         }
         }
+
+        return false;
       });
       });
     }
     }
   }, {
   }, {
@@ -2371,11 +2465,29 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
      */
      */
 
 
   }, {
   }, {
-    key: "getSharp",
+    key: "getRowByY",
 
 
+    /**
+     * Get row by y coordinate.
+     * @param posY y screen coordinate.
+     */
+    value: function getRowByY(posY) {
+      var model = this.calculateRowsBounds();
+
+      if (model && model.rows) {
+        return model.rows.find(function (rowData) {
+          return rowData.y >= posY && posY <= rowData.y + rowData.height;
+        });
+      }
+
+      return null;
+    }
     /**
     /**
      * Find sharp pixel position
      * Find sharp pixel position
      */
      */
+
+  }, {
+    key: "getSharp",
     value: function getSharp(pos) {
     value: function getSharp(pos) {
       var thickness = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
       var thickness = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
 
 
@@ -2418,7 +2530,7 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
     key: "setTime",
     key: "setTime",
     value: function setTime(val) {
     value: function setTime(val) {
       // don't allow to change time during drag:
       // don't allow to change time during drag:
-      if (this.drag && this.drag.type === TimelineDraggableType.timeline) {
+      if (this.drag && this.drag.type === TimelineElementType.Timeline) {
         return false;
         return false;
       }
       }
 
 
@@ -2448,6 +2560,31 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
         keyframes: selectedKeyframes
         keyframes: selectedKeyframes
       });
       });
     }
     }
+  }, {
+    key: "emitDragStartedEvent",
+    value: function emitDragStartedEvent() {
+      this.emit(TimelineEvents.DragStarted, {
+        keyframes: this.drag.elements
+      });
+    }
+  }, {
+    key: "emitDragFinishedEvent",
+    value: function emitDragFinishedEvent() {
+      if (this.drag && this.drag.changed) {
+        this.emit(TimelineEvents.DragFinished, {
+          keyframes: this.drag.elements
+        });
+      }
+    }
+  }, {
+    key: "emitDragEvent",
+    value: function emitDragEvent() {
+      if (this.drag) {
+        this.emit(TimelineEvents.Drag, {
+          keyframes: this.drag.elements
+        });
+      }
+    }
   }, {
   }, {
     key: "setScrollLeft",
     key: "setScrollLeft",
     value: function setScrollLeft(value) {
     value: function setScrollLeft(value) {
@@ -2613,116 +2750,133 @@ var timeline_Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
       }
       }
     }
     }
     /**
     /**
-     * Find clickable elements under the mouse position.
+     * get draggable element.
+     * Filter elements and get first element by a priority.
+     * @param Array
+     * @param val current mouse value
      */
      */
 
 
   }, {
   }, {
-    key: "getDraggable",
-    value: function getDraggable(pos) {
+    key: "findDraggable",
+    value: function findDraggable(elements, val) {
       var _this8 = this;
       var _this8 = this;
 
 
-      // few extra pixels to select items:
-      var helperSelector = Math.max(2, pos.radius);
-      var draggable = null;
-      var lastLength = Number.MAX_SAFE_INTEGER;
+      // filter and sort: Timeline, individual keyframes, stripes (distance).
+      var getPriority = function getPriority(type) {
+        if (type === TimelineElementType.Keyframe) {
+          return 1;
+        } else if (type === TimelineElementType.Stripe) {
+          return 2;
+        } else if (type === TimelineElementType.Timeline) {
+          return 3;
+        }
 
 
-      if (pos.y >= this.options.headerHeight && this.options.keyframesDraggable) {
-        this.forEachKeyframe(function (keyframe, keyframeIndex, rowSize) {
-          if (keyframe.draggable !== undefined) {
-            if (!keyframe.draggable) {
-              return;
-            }
+        return -1;
+      };
+
+      var filteredElements = elements.filter(function (element) {
+        if (element.type === TimelineElementType.Keyframe) {
+          var draggable = (_this8.options.keyframesDraggable === undefined ? true : !!_this8.options.keyframesDraggable) && (element.keyframe.draggable === undefined ? true : !!element.keyframe.draggable);
+
+          if (!draggable) {
+            return false;
           }
           }
-          /*  const row = rowSize.row; if (row.keyframesDraggable !== undefined) {
-              if (!row.keyframesDraggable) {
-                return;
-              }
-            }
-          */
+        } else if (element.type === TimelineElementType.Stripe) {
+          var _draggable = (_this8.options.stripesDraggable === undefined ? true : !!_this8.options.stripesDraggable) && (element.row.stripeDraggable === undefined ? true : !!element.row.stripeDraggable);
 
 
+          if (!_draggable) {
+            return false;
+          }
+        }
 
 
-          var keyframePos = _this8.getKeyframePosition(keyframe, rowSize);
+        return true;
+      });
+      var sorted = filteredElements.sort(function (a, b) {
+        var prioA = getPriority(a.type);
+        var prioB = getPriority(b.type);
 
 
-          if (keyframePos) {
-            var dist = TimelineUtils.getDistance(keyframePos.x, keyframePos.y, pos.x, pos.y);
+        if (prioA == prioB) {
+          return TimelineUtils.getDistance(a.val, val) > TimelineUtils.getDistance(b.val, val) ? 1 : 0;
+        }
 
 
-            if (dist <= keyframePos.height + helperSelector) {
-              if (!draggable) {
-                lastLength = dist;
-                draggable = {
-                  keyframe: keyframe,
-                  val: keyframe.val,
-                  type: TimelineDraggableType.keyframe
-                };
-              } else if (dist <= lastLength) {
-                draggable.keyframe = keyframe;
-                draggable.val = keyframe.val;
-              }
-            }
-          }
-        });
+        return prioA > prioB ? 1 : 0;
+      });
 
 
-        if (draggable) {
-          return draggable;
-        } // TODO:
-        // Return keyframes lanes:
+      if (sorted.length > 0) {
+        return sorted[sorted.length - 1];
+      }
 
 
+      return null;
+    }
+    /**
+     * get all clickable elements by a screen point.
+     */
 
 
-        var data = this.calculateRowsBounds();
+  }, {
+    key: "elementFromPoint",
+    value: function elementFromPoint(pos) {
+      var _this9 = this;
 
 
-        if (this.options.stripesDraggable && data) {
-          var overlapped = null;
+      var clickRadius = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
+      clickRadius = Math.max(clickRadius, 1);
+      var toReturn = [];
 
 
-          if (data.rows) {
-            for (var i = 0; i < data.rows.length; i++) {
-              var rowSizeData = data.rows[i];
+      if (!pos) {
+        return toReturn;
+      } // Check whether we can drag timeline.
 
 
-              if (!rowSizeData) {
-                break;
-              }
 
 
-              var _draggable = TimelineStyleUtils.getRowStyle(rowSizeData.row, this.options.rowsStyle, 'stripeDraggable', true);
+      var timeLinePos = this.valToPx(this.val);
+      var width = Math.max((this.options.timelineThicknessPx || 1) * this.pixelRatio, this.options.timelineCapWidthPx * this.pixelRatio || 1) + clickRadius;
+
+      if (pos.y <= this.options.headerHeight || pos.x >= timeLinePos - width / 2 && pos.x <= timeLinePos + width / 2) {
+        toReturn.push({
+          val: this.val,
+          type: TimelineElementType.Timeline
+        });
+      }
 
 
-              if (!_draggable) {
-                break;
-              }
+      if (pos.y >= this.options.headerHeight && this.options.keyframesDraggable) {
+        this.forEachKeyframe(function (keyframe, keyframeIndex, rowModel, rowIndex, isNextRow) {
+          // Check keyframes stripe overlap
+          if (isNextRow && rowModel.stripeRect) {
+            var keyframesStripeOverlapped = TimelineUtils.isOverlap(pos.x, pos.y, rowModel.stripeRect);
 
 
-              var laneOverlapped = TimelineUtils.isOverlap(pos.x, pos.y, rowSizeData.stripeRect);
+            if (keyframesStripeOverlapped) {
+              var stripe = {
+                val: _this9.mousePosToVal(pos.x, true),
+                type: TimelineElementType.Stripe,
+                row: rowModel.row
+              };
 
 
-              if (laneOverlapped) {
-                overlapped = rowSizeData;
-              }
+              var snapped = _this9.snapVal(rowModel.minValue); // get snapped mouse pos based on a min value.
+
+
+              stripe.val += rowModel.minValue - snapped;
+              toReturn.push(stripe);
             }
             }
           }
           }
 
 
-          if (overlapped) {
-            draggable = {
-              type: TimelineDraggableType.keyframes
-            };
-            draggable.val = this.mousePosToVal(pos.x, true);
+          var keyframePos = _this9.getKeyframePosition(keyframe, rowModel);
 
 
-            if (overlapped.row && overlapped.row.keyframes) {
-              draggable.keyframes = overlapped.row.keyframes;
-              var snapped = this.snapVal(overlapped.minValue); // get snapped mouse pos based on a min value.
+          if (keyframePos) {
+            var dist = TimelineUtils.getDistance(keyframePos.x, keyframePos.y, pos.x, pos.y);
 
 
-              draggable.val += overlapped.minValue - snapped;
+            if (dist <= keyframePos.height + clickRadius) {
+              toReturn.push({
+                keyframe: keyframe,
+                val: keyframe.val,
+                row: rowModel.row,
+                type: TimelineElementType.Keyframe
+              });
             }
             }
-
-            return draggable;
           }
           }
-        }
-      } // Check whether we can drag timeline.
-
-
-      var timeLinePos = this.valToPx(this.val);
-      var width = Math.max((this.options.timelineThicknessPx || 1) * this.pixelRatio, this.options.timelineCapWidthPx * this.pixelRatio || 1) + helperSelector;
 
 
-      if (pos.y <= this.options.headerHeight || pos.x >= timeLinePos - width / 2 && pos.x <= timeLinePos + width / 2) {
-        return {
-          val: this.val,
-          type: TimelineDraggableType.timeline
-        };
+          return false;
+        }, true);
       }
       }
+
+      return toReturn;
     }
     }
     /**
     /**
      * Merge options with the defaults.
      * Merge options with the defaults.
@@ -2771,38 +2925,68 @@ var TimelineModel = function TimelineModel() {
   timelineModel_defineProperty(this, "rows", []);
   timelineModel_defineProperty(this, "rows", []);
 };
 };
 // EXTERNAL MODULE: ./src/timelineRow.ts
 // EXTERNAL MODULE: ./src/timelineRow.ts
-var timelineRow = __webpack_require__(0);
+var timelineRow = __webpack_require__(1);
 
 
 // EXTERNAL MODULE: ./src/timelineKeyframe.ts
 // EXTERNAL MODULE: ./src/timelineKeyframe.ts
-var timelineKeyframe = __webpack_require__(1);
+var timelineKeyframe = __webpack_require__(2);
+
+// EXTERNAL MODULE: ./src/settings/styles/timelineKeyframeStyle.ts
+var timelineKeyframeStyle = __webpack_require__(3);
 
 
-// EXTERNAL MODULE: ./src/utils/timelineDraggableData.ts
-var timelineDraggableData = __webpack_require__(2);
+// EXTERNAL MODULE: ./src/settings/styles/timelineRowStyle.ts
+var timelineRowStyle = __webpack_require__(4);
 
 
-// EXTERNAL MODULE: ./src/utils/selectionTuple.ts
-var selectionTuple = __webpack_require__(3);
+// EXTERNAL MODULE: ./src/utils/timelineClickableElement.ts
+var timelineClickableElement = __webpack_require__(5);
 
 
 // EXTERNAL MODULE: ./src/utils/selectable.ts
 // EXTERNAL MODULE: ./src/utils/selectable.ts
-var selectable = __webpack_require__(4);
+var selectable = __webpack_require__(6);
 
 
 // EXTERNAL MODULE: ./src/utils/rowsCalculationsResults.ts
 // EXTERNAL MODULE: ./src/utils/rowsCalculationsResults.ts
-var rowsCalculationsResults = __webpack_require__(5);
+var rowsCalculationsResults = __webpack_require__(0);
 
 
 // EXTERNAL MODULE: ./src/utils/cutBoundsRect.ts
 // EXTERNAL MODULE: ./src/utils/cutBoundsRect.ts
-var cutBoundsRect = __webpack_require__(6);
+var cutBoundsRect = __webpack_require__(7);
 
 
 // EXTERNAL MODULE: ./src/utils/events/timelineSelectedEvent.ts
 // EXTERNAL MODULE: ./src/utils/events/timelineSelectedEvent.ts
-var timelineSelectedEvent = __webpack_require__(7);
+var timelineSelectedEvent = __webpack_require__(8);
 
 
 // EXTERNAL MODULE: ./src/utils/events/timelineScrollEvent.ts
 // EXTERNAL MODULE: ./src/utils/events/timelineScrollEvent.ts
-var timelineScrollEvent = __webpack_require__(8);
+var timelineScrollEvent = __webpack_require__(9);
 
 
-// EXTERNAL MODULE: ./src/settings/styles/timelineKeyframeStyle.ts
-var timelineKeyframeStyle = __webpack_require__(9);
+// CONCATENATED MODULE: ./src/utils/events/timelineDragEvent.ts
+function timelineDragEvent_typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { timelineDragEvent_typeof = function _typeof(obj) { return typeof obj; }; } else { timelineDragEvent_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return timelineDragEvent_typeof(obj); }
 
 
-// EXTERNAL MODULE: ./src/settings/styles/timelineRowStyle.ts
-var timelineRowStyle = __webpack_require__(10);
+function timelineDragEvent_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function timelineDragEvent_inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) timelineDragEvent_setPrototypeOf(subClass, superClass); }
+
+function timelineDragEvent_setPrototypeOf(o, p) { timelineDragEvent_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return timelineDragEvent_setPrototypeOf(o, p); }
+
+function timelineDragEvent_createSuper(Derived) { var hasNativeReflectConstruct = timelineDragEvent_isNativeReflectConstruct(); return function () { var Super = timelineDragEvent_getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = timelineDragEvent_getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return timelineDragEvent_possibleConstructorReturn(this, result); }; }
+
+function timelineDragEvent_possibleConstructorReturn(self, call) { if (call && (timelineDragEvent_typeof(call) === "object" || typeof call === "function")) { return call; } return timelineDragEvent_assertThisInitialized(self); }
 
 
+function timelineDragEvent_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
+
+function timelineDragEvent_isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
+
+function timelineDragEvent_getPrototypeOf(o) { timelineDragEvent_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return timelineDragEvent_getPrototypeOf(o); }
+
+
+var TimelineDragEvent = /*#__PURE__*/function (_TimelineClickEvent) {
+  timelineDragEvent_inherits(TimelineDragEvent, _TimelineClickEvent);
+
+  var _super = timelineDragEvent_createSuper(TimelineDragEvent);
+
+  function TimelineDragEvent() {
+    timelineDragEvent_classCallCheck(this, TimelineDragEvent);
+
+    return _super.apply(this, arguments);
+  }
+
+  return TimelineDragEvent;
+}(TimelineClickEvent);
 // CONCATENATED MODULE: ./src/index.ts
 // CONCATENATED MODULE: ./src/index.ts
 // bundle entry point
 // bundle entry point
 
 
@@ -2827,6 +3011,8 @@ var timelineRowStyle = __webpack_require__(10);
 
 
 
 
 
 
+
+
 
 
 
 
 
 

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
lib/animation-timeline.min.js


+ 0 - 14
src/enums/timelineDraggableType.ts

@@ -1,14 +0,0 @@
-export enum TimelineDraggableType {
-  /**
-   * Drag keyframe
-   */
-  keyframe = 'keyframe',
-  /**
-   * Drag keyframes stripe.
-   */
-  keyframes = 'keyframes',
-  /**
-   * Drag time indicator
-   */
-  timeline = 'timeline',
-}

+ 14 - 0
src/enums/timelineElementType.ts

@@ -0,0 +1,14 @@
+export enum TimelineElementType {
+  /**
+   * Timeline
+   */
+  Timeline = 'timeline',
+  /**
+   * Keyframes
+   */
+  Keyframe = 'keyframe',
+  /**
+   * Keyframes stripe
+   */
+  Stripe = 'stripe',
+}

+ 15 - 8
src/index.ts

@@ -2,25 +2,32 @@
 
 
 export { Timeline } from './timeline';
 export { Timeline } from './timeline';
 export { TimelineModel } from './timelineModel';
 export { TimelineModel } from './timelineModel';
-export { TimelineOptions } from './settings/timelineOptions';
 export { TimelineRow } from './timelineRow';
 export { TimelineRow } from './timelineRow';
-export { TimelineStyleUtils } from './settings/timelineStyleUtils';
 export { TimelineKeyframe } from './timelineKeyframe';
 export { TimelineKeyframe } from './timelineKeyframe';
 export { TimelineEventsEmitter } from './timelineEventsEmitter';
 export { TimelineEventsEmitter } from './timelineEventsEmitter';
+
+export { TimelineOptions } from './settings/timelineOptions';
+export { TimelineStyleUtils } from './settings/timelineStyleUtils';
+export { TimelineKeyframeStyle } from './settings/styles/timelineKeyframeStyle';
+export { TimelineRowStyle } from './settings/styles/timelineRowStyle';
+
 export { TimelineUtils } from './utils/timelineUtils';
 export { TimelineUtils } from './utils/timelineUtils';
-export { TimelineDraggableData } from './utils/timelineDraggableData';
-export { SelectionTuple } from './utils/selectionTuple';
+export { TimelineClickableElement } from './utils/timelineClickableElement';
 export { Selectable } from './utils/selectable';
 export { Selectable } from './utils/selectable';
 export { RowsCalculationsResults } from './utils/rowsCalculationsResults';
 export { RowsCalculationsResults } from './utils/rowsCalculationsResults';
+export { RowSize } from './utils/rowsCalculationsResults';
 export { CutBoundsRect } from './utils/cutBoundsRect';
 export { CutBoundsRect } from './utils/cutBoundsRect';
+
 export { TimelineSelectedEvent } from './utils/events/timelineSelectedEvent';
 export { TimelineSelectedEvent } from './utils/events/timelineSelectedEvent';
 export { TimelineScrollEvent } from './utils/events/timelineScrollEvent';
 export { TimelineScrollEvent } from './utils/events/timelineScrollEvent';
+export { TimelineClickEvent } from './utils/events/timelineClickEvent';
+export { TimelineDragEvent } from './utils/events/timelineDragEvent';
+
+export { TimelineEvents } from './enums/timelineEvents';
 export { TimelineConsts } from './settings/timelineConsts';
 export { TimelineConsts } from './settings/timelineConsts';
-export { TimelineKeyframeStyle } from './settings/styles/timelineKeyframeStyle';
-export { TimelineRowStyle } from './settings/styles/timelineRowStyle';
+
 export { TimelineKeyframeShape } from './enums/timelineKeyframeShape';
 export { TimelineKeyframeShape } from './enums/timelineKeyframeShape';
 export { TimelineInteractionMode } from './enums/timelineInteractionMode';
 export { TimelineInteractionMode } from './enums/timelineInteractionMode';
-export { TimelineEvents } from './enums/timelineEvents';
-export { TimelineDraggableType } from './enums/timelineDraggableType';
+export { TimelineElementType } from './enums/timelineElementType';
 export { TimelineCursorType } from './enums/timelineCursorType';
 export { TimelineCursorType } from './enums/timelineCursorType';
 export { TimelineCapShape } from './enums/timelineCapShape';
 export { TimelineCapShape } from './enums/timelineCapShape';

+ 221 - 149
src/timeline.ts

@@ -4,12 +4,12 @@ import { TimelineOptions } from './settings/timelineOptions';
 import { TimelineConsts } from './settings/timelineConsts';
 import { TimelineConsts } from './settings/timelineConsts';
 import { TimelineKeyframe } from './timelineKeyframe';
 import { TimelineKeyframe } from './timelineKeyframe';
 import { TimelineModel } from './timelineModel';
 import { TimelineModel } from './timelineModel';
-import { TimelineDraggableData } from './utils/timelineDraggableData';
+import { TimelineClickableElement } from './utils/timelineClickableElement';
 import { TimelineRow } from './timelineRow';
 import { TimelineRow } from './timelineRow';
 import { TimelineCursorType } from './enums/timelineCursorType';
 import { TimelineCursorType } from './enums/timelineCursorType';
 import { TimelineKeyframeShape } from './enums/timelineKeyframeShape';
 import { TimelineKeyframeShape } from './enums/timelineKeyframeShape';
 import { TimelineStyleUtils } from './settings/timelineStyleUtils';
 import { TimelineStyleUtils } from './settings/timelineStyleUtils';
-import { TimelineDraggableType } from './enums/timelineDraggableType';
+import { TimelineElementType } from './enums/timelineElementType';
 import { TimelineEvents } from './enums/timelineEvents';
 import { TimelineEvents } from './enums/timelineEvents';
 import { CutBoundsRect } from './utils/cutBoundsRect';
 import { CutBoundsRect } from './utils/cutBoundsRect';
 import { TimelineCapShape } from './enums/timelineCapShape';
 import { TimelineCapShape } from './enums/timelineCapShape';
@@ -17,6 +17,8 @@ import { RowSize, RowsCalculationsResults } from './utils/rowsCalculationsResult
 import { TimelineInteractionMode } from './enums/timelineInteractionMode';
 import { TimelineInteractionMode } from './enums/timelineInteractionMode';
 import { TimelineScrollEvent } from './utils/events/timelineScrollEvent';
 import { TimelineScrollEvent } from './utils/events/timelineScrollEvent';
 import { TimelineSelectedEvent } from './utils/events/timelineSelectedEvent';
 import { TimelineSelectedEvent } from './utils/events/timelineSelectedEvent';
+import { TimelineDraggableData } from './utils/timelineDraggableData';
+import { TimelineClickEvent } from './utils/events/timelineClickEvent';
 
 
 interface MousePoint extends DOMPoint {
 interface MousePoint extends DOMPoint {
   radius: number;
   radius: number;
@@ -63,7 +65,7 @@ export class Timeline extends TimelineEventsEmitter {
 
 
   private selectionRect: DOMRect = null;
   private selectionRect: DOMRect = null;
   private selectionRectEnabled = false;
   private selectionRectEnabled = false;
-  private drag: TimelineDraggableData = null;
+  private drag: TimelineDraggableData | null = null;
   private startedDragWithCtrl = false;
   private startedDragWithCtrl = false;
   private startedDragWithShiftKey = false;
   private startedDragWithShiftKey = false;
 
 
@@ -311,34 +313,68 @@ export class Timeline extends TimelineEventsEmitter {
     // Prevent drag of the canvas if canvas is selected as text:
     // Prevent drag of the canvas if canvas is selected as text:
     TimelineUtils.clearBrowserSelection();
     TimelineUtils.clearBrowserSelection();
     this.startPos = this.trackMousePos(this.canvas, args);
     this.startPos = this.trackMousePos(this.canvas, args);
+    if (!this.startPos) {
+      return;
+    }
     this.scrollStartPos = {
     this.scrollStartPos = {
       x: this.scrollContainer.scrollLeft,
       x: this.scrollContainer.scrollLeft,
       y: this.scrollContainer.scrollTop,
       y: this.scrollContainer.scrollTop,
     } as DOMPoint;
     } as DOMPoint;
 
 
+    const elements = this.elementFromPoint(this.startPos, Math.max(2, this.startPos.radius));
+    const target = this.findDraggable(elements, this.startPos.val);
+    const event = new TimelineClickEvent();
+    event.pos = this.startPos;
+    event.val = this.startPos.val;
+    event.args = args;
+    // all elements under the click:
+    event.elements = elements;
+    // target element.
+    event.target = target;
+
     if (isDoubleClick) {
     if (isDoubleClick) {
-      super.emit(TimelineEvents.DoubleClick, this.startPos);
+      super.emit(TimelineEvents.DoubleClick, event);
       return;
       return;
     }
     }
 
 
-    this.lastClickTime = Date.now();
-
-    super.emit(TimelineEvents.MouseDown, this.startPos);
+    super.emit(TimelineEvents.MouseDown, event);
 
 
     this.clickTimeout = Date.now();
     this.clickTimeout = Date.now();
+    this.lastClickTime = Date.now();
+    if (event.isPrevented()) {
+      this.cleanUpSelection();
+      return;
+    }
+
     this.currentPos = this.startPos;
     this.currentPos = this.startPos;
-    this.drag = this.getDraggable(this.currentPos);
+
     // Select keyframes on mouse down
     // Select keyframes on mouse down
-    if (this.drag) {
-      if (this.drag.type === TimelineDraggableType.keyframe) {
+    if (target) {
+      this.drag = {
+        changed: false,
+        target: target,
+        val: target.val,
+        type: target.type,
+        elements: [],
+      } as TimelineDraggableData;
+
+      if (target.type === TimelineElementType.Keyframe) {
         this.startedDragWithCtrl = this.controlKeyPressed(args);
         this.startedDragWithCtrl = this.controlKeyPressed(args);
         this.startedDragWithShiftKey = args.shiftKey;
         this.startedDragWithShiftKey = args.shiftKey;
         // get all related selected keyframes if we are selecting one.
         // get all related selected keyframes if we are selecting one.
-        if (!this.drag.keyframe.selected && !this.controlKeyPressed(args) && !args.shiftKey) {
-          this.performSelection(true, this.drag.keyframe, 'keyframe');
+        if (!target.keyframe.selected && !this.controlKeyPressed(args) && !args.shiftKey) {
+          this.performSelection(true, target.keyframe);
         }
         }
 
 
-        this.drag.keyframes = this.getSelectedKeyframes();
+        this.drag.elements = this.getSelectedElements();
+      } else if (target.type === TimelineElementType.Stripe) {
+        const keyframes = this.drag.target.row.keyframes;
+        this.drag.elements =
+          keyframes && Array.isArray(keyframes)
+            ? keyframes.map((keyframe) => {
+                return this.convertToElement(this.drag.target.row, keyframe) as TimelineClickableElement;
+              })
+            : [];
       }
       }
     }
     }
 
 
@@ -367,16 +403,16 @@ export class Timeline extends TimelineEventsEmitter {
         if (this.drag && !this.startedDragWithCtrl) {
         if (this.drag && !this.startedDragWithCtrl) {
           const convertedVal = this.mousePosToVal(this.currentPos.x, true);
           const convertedVal = this.mousePosToVal(this.currentPos.x, true);
           //redraw();
           //redraw();
-          if (this.drag.type === TimelineDraggableType.timeline) {
+          if (this.drag.type === TimelineElementType.Timeline) {
             isChanged = this.setTimeInternal(convertedVal, 'user') || isChanged;
             isChanged = this.setTimeInternal(convertedVal, 'user') || isChanged;
-          } else if ((this.drag.type == TimelineDraggableType.keyframe || this.drag.type == TimelineDraggableType.keyframes) && this.drag.keyframes) {
+          } else if ((this.drag.type == TimelineElementType.Keyframe || this.drag.type == TimelineElementType.Stripe) && this.drag.elements) {
             let offset = Math.floor(convertedVal - this.drag.val);
             let offset = Math.floor(convertedVal - this.drag.val);
             if (Math.abs(offset) > 0) {
             if (Math.abs(offset) > 0) {
               // don't allow to move less than zero.
               // don't allow to move less than zero.
-              this.drag.keyframes.forEach((p) => {
+              this.drag.elements.forEach((p) => {
                 if (this.options.snapAllKeyframesOnMove) {
                 if (this.options.snapAllKeyframesOnMove) {
-                  const toSet = this.snapVal(p.val);
-                  isChanged = this.setKeyframePos(p, toSet) || isChanged;
+                  const toSet = this.snapVal(p.keyframe.val);
+                  isChanged = this.setKeyframePos(p.keyframe, toSet) || isChanged;
                 }
                 }
 
 
                 const newPosition = p.val + offset;
                 const newPosition = p.val + offset;
@@ -387,25 +423,24 @@ export class Timeline extends TimelineEventsEmitter {
 
 
               if (Math.abs(offset) > 0) {
               if (Math.abs(offset) > 0) {
                 // don't allow to move less than zero.
                 // don't allow to move less than zero.
-                this.drag.keyframes.forEach((keyframe) => {
-                  const toSet = keyframe.val + offset;
-                  isChanged = this.setKeyframePos(keyframe, toSet) || isChanged;
+                this.drag.elements.forEach((element) => {
+                  const toSet = element.keyframe.val + offset;
+                  isChanged = this.setKeyframePos(element.keyframe, toSet) || isChanged;
+                  if (isChanged) {
+                    element.val = element.keyframe.val;
+                  }
                 });
                 });
               }
               }
 
 
               if (isChanged) {
               if (isChanged) {
                 if (!this.drag.changed) {
                 if (!this.drag.changed) {
-                  this.emit('dragStarted', {
-                    keyframes: this.drag.keyframes,
-                  });
+                  this.emitDragStartedEvent();
                 }
                 }
 
 
                 this.drag.changed = true;
                 this.drag.changed = true;
 
 
                 this.drag.val += offset;
                 this.drag.val += offset;
-                this.emit('drag', {
-                  keyframes: this.drag.keyframes,
-                });
+                this.emitDragEvent();
               }
               }
             }
             }
           }
           }
@@ -426,15 +461,17 @@ export class Timeline extends TimelineEventsEmitter {
         this.redraw();
         this.redraw();
       }
       }
     } else if (!isTouch) {
     } else if (!isTouch) {
-      const draggable = this.getDraggable(this.currentPos);
+      // TODO: mouse over event
+      const elements = this.elementFromPoint(this.currentPos, Math.max(2, this.currentPos.radius));
+      const target = this.findDraggable(elements, this.currentPos.val);
       this.setCursor('default');
       this.setCursor('default');
-      if (draggable) {
-        let cursor = null;
-        if (draggable.type === TimelineDraggableType.keyframes) {
+      if (target) {
+        let cursor: TimelineCursorType | null = null;
+        if (target.type === TimelineElementType.Stripe) {
           cursor = cursor || TimelineCursorType.EWResize;
           cursor = cursor || TimelineCursorType.EWResize;
-        } else if (draggable.type == 'keyframe') {
+        } else if (target.type == TimelineElementType.Keyframe) {
           cursor = cursor || TimelineCursorType.Pointer;
           cursor = cursor || TimelineCursorType.Pointer;
-        } else {
+        } else if (target.type == TimelineElementType.Timeline) {
           cursor = cursor || TimelineCursorType.EWResize;
           cursor = cursor || TimelineCursorType.EWResize;
         }
         }
 
 
@@ -448,7 +485,6 @@ export class Timeline extends TimelineEventsEmitter {
       args.preventDefault();
       args.preventDefault();
     }
     }
   };
   };
-
   private handleMouseUpEvent = (args: MouseEvent): void => {
   private handleMouseUpEvent = (args: MouseEvent): void => {
     if (this.startPos) {
     if (this.startPos) {
       //window.releaseCapture();
       //window.releaseCapture();
@@ -463,7 +499,7 @@ export class Timeline extends TimelineEventsEmitter {
       ) {
       ) {
         this.performClick(pos, args, this.drag);
         this.performClick(pos, args, this.drag);
       } else if (!this.drag && this.selectionRect && this.selectionRectEnabled) {
       } else if (!this.drag && this.selectionRect && this.selectionRectEnabled) {
-        this.performSelection(true, this.selectionRect, 'rectangle', args.shiftKey);
+        this.performSelection(true, this.selectionRect, args.shiftKey);
       }
       }
 
 
       this.cleanUpSelection();
       this.cleanUpSelection();
@@ -473,15 +509,15 @@ export class Timeline extends TimelineEventsEmitter {
 
 
   private performClick(pos: MouseData, args: MouseEvent, drag: TimelineDraggableData): boolean {
   private performClick(pos: MouseData, args: MouseEvent, drag: TimelineDraggableData): boolean {
     let isChanged = false;
     let isChanged = false;
-    if (drag && drag.type === TimelineDraggableType.keyframe) {
+    if (drag && drag.type === TimelineElementType.Keyframe) {
       let isSelected = true;
       let isSelected = true;
       if ((this.startedDragWithCtrl && this.controlKeyPressed(args)) || (this.startedDragWithShiftKey && args.shiftKey)) {
       if ((this.startedDragWithCtrl && this.controlKeyPressed(args)) || (this.startedDragWithShiftKey && args.shiftKey)) {
         if (this.controlKeyPressed(args)) {
         if (this.controlKeyPressed(args)) {
-          isSelected = !drag.keyframe.selected;
+          isSelected = !drag.target.keyframe.selected;
         }
         }
       }
       }
       // Reverse selected keyframe selection by a click:
       // Reverse selected keyframe selection by a click:
-      isChanged = this.performSelection(isSelected, this.drag.keyframe, 'keyframe', this.controlKeyPressed(args) || args.shiftKey) || isChanged;
+      isChanged = this.performSelection(isSelected, this.drag.target.keyframe, this.controlKeyPressed(args) || args.shiftKey) || isChanged;
 
 
       if (args.shiftKey) {
       if (args.shiftKey) {
         // change timeline pos:
         // change timeline pos:
@@ -536,31 +572,39 @@ export class Timeline extends TimelineEventsEmitter {
     }
     }
   }
   }
 
 
-  public getSelectedKeyframes(): Array<TimelineKeyframe> {
-    if (!this.selectedKeyframes) {
-      this.selectedKeyframes = [];
-    }
-    this.selectedKeyframes.length = 0;
-    this.forEachKeyframe((keyframe) => {
+  private convertToElement(row: TimelineRow, keyframe: TimelineKeyframe): TimelineClickableElement {
+    const data = {
+      type: TimelineElementType.Keyframe,
+      val: keyframe.val,
+      keyframe: keyframe,
+      row: row,
+    } as TimelineClickableElement;
+    return data;
+  }
+
+  public getSelectedElements(): Array<TimelineClickableElement> {
+    const selected: Array<TimelineClickableElement> = [];
+    this.forEachKeyframe((keyframe, index, rowModel): boolean => {
       if (keyframe && keyframe.selected) {
       if (keyframe && keyframe.selected) {
-        this.selectedKeyframes.push(keyframe);
+        selected.push(this.convertToElement(rowModel.row, keyframe));
       }
       }
+      return false;
     });
     });
 
 
-    return this.selectedKeyframes;
+    return selected;
   }
   }
 
 
   /**
   /**
    * Do the selection.
    * Do the selection.
    * @param {boolean} isSelected
    * @param {boolean} isSelected
    * @param {object} selector can be a rectangle or a keyframe object.
    * @param {object} selector can be a rectangle or a keyframe object.
-   * @param {string} mode selector mode. keyframe | rectangle | all
    * @param {boolean} ignoreOthers value indicating whether all other object should be reversed.
    * @param {boolean} ignoreOthers value indicating whether all other object should be reversed.
    * @return {boolean} isChanged
    * @return {boolean} isChanged
    */
    */
-  private performSelection(isSelected = true, selector: DOMRect | TimelineKeyframe = null, mode = 'all', ignoreOthers = false): boolean {
+  private performSelection(isSelected = true, selector: DOMRect | TimelineKeyframe | null = null, ignoreOthers = false): boolean {
     let deselectionMode = false;
     let deselectionMode = false;
-    if (mode == 'all') {
+    // TODO: simplify
+    if (!selector) {
       if (!isSelected) {
       if (!isSelected) {
         isSelected = false;
         isSelected = false;
       }
       }
@@ -570,11 +614,11 @@ export class Timeline extends TimelineEventsEmitter {
 
 
     this.selectedKeyframes.length = 0;
     this.selectedKeyframes.length = 0;
     let isChanged = true;
     let isChanged = true;
-    this.forEachKeyframe((keyframe, keyframeIndex, rowSize) => {
+    this.forEachKeyframe((keyframe, keyframeIndex, rowSize): boolean => {
       const keyframePos = this.getKeyframePosition(keyframe, rowSize);
       const keyframePos = this.getKeyframePosition(keyframe, rowSize);
 
 
       if (keyframePos) {
       if (keyframePos) {
-        if ((mode == 'keyframe' && selector === keyframe) || (mode == 'rectangle' && selector && TimelineUtils.isOverlap(keyframePos.x, keyframePos.y, selector as DOMRect))) {
+        if ((selector && selector === keyframe) || TimelineUtils.isOverlap(keyframePos.x, keyframePos.y, selector as DOMRect)) {
           if (keyframe.selected != isSelected) {
           if (keyframe.selected != isSelected) {
             keyframe.selected = isSelected;
             keyframe.selected = isSelected;
             isChanged = true;
             isChanged = true;
@@ -591,6 +635,8 @@ export class Timeline extends TimelineEventsEmitter {
           }
           }
         }
         }
       }
       }
+
+      return false;
     });
     });
 
 
     if (isChanged) {
     if (isChanged) {
@@ -603,12 +649,12 @@ export class Timeline extends TimelineEventsEmitter {
   /**
   /**
    * foreach visible keyframe.
    * foreach visible keyframe.
    */
    */
-  private forEachKeyframe(callback: (keyframe: TimelineKeyframe, keyframeIndex?: number, row?: RowSize, index?: number, newRow?: boolean) => void): void {
+  private forEachKeyframe(callback: (keyframe: TimelineKeyframe, keyframeIndex?: number, row?: RowSize, index?: number, newRow?: boolean) => boolean, calculateStripesBounds = false): void {
     if (!this.model) {
     if (!this.model) {
       return;
       return;
     }
     }
 
 
-    const model = this.calculateRowsBounds(false);
+    const model = this.calculateRowsBounds(calculateStripesBounds);
     if (!model) {
     if (!model) {
       return;
       return;
     }
     }
@@ -625,9 +671,12 @@ export class Timeline extends TimelineEventsEmitter {
       let nextRow = true;
       let nextRow = true;
       row.keyframes
       row.keyframes
         .filter((p) => p && !p.hidden)
         .filter((p) => p && !p.hidden)
-        .forEach((keyframe: TimelineKeyframe, keyframeIndex) => {
+        .find((keyframe: TimelineKeyframe, keyframeIndex) => {
           if (callback && keyframe) {
           if (callback && keyframe) {
-            callback(keyframe, keyframeIndex, rowSize, index, nextRow);
+            const isBreak = callback(keyframe, keyframeIndex, rowSize, index, nextRow);
+            if (isBreak) {
+              return true;
+            }
           }
           }
 
 
           nextRow = false;
           nextRow = false;
@@ -657,12 +706,7 @@ export class Timeline extends TimelineEventsEmitter {
   }
   }
 
 
   private cleanUpSelection(): void {
   private cleanUpSelection(): void {
-    if (this.drag && this.drag.changed) {
-      this.emit(TimelineEvents.DragFinished, {
-        keyframes: this.drag.keyframes,
-      });
-    }
-
+    this.emitDragFinishedEvent();
     this.startPos = null;
     this.startPos = null;
     this.drag = null;
     this.drag = null;
     this.startedDragWithCtrl = false;
     this.startedDragWithCtrl = false;
@@ -807,7 +851,7 @@ export class Timeline extends TimelineEventsEmitter {
   /**
   /**
    * Convert screen pixel to value.
    * Convert screen pixel to value.
    */
    */
-  private pxToVal(coords: number, absolute = false): number {
+  public pxToVal(coords: number, absolute = false): number {
     if (!absolute) {
     if (!absolute) {
       coords -= this.options.leftMarginPx;
       coords -= this.options.leftMarginPx;
     }
     }
@@ -818,7 +862,7 @@ export class Timeline extends TimelineEventsEmitter {
   /**
   /**
    * Convert area value to screen pixel coordinates.
    * Convert area value to screen pixel coordinates.
    */
    */
-  private valToPx(ms: number, absolute = false): number {
+  public valToPx(ms: number, absolute = false): number {
     // Respect current scroll container offset. (virtualization)
     // Respect current scroll container offset. (virtualization)
     if (!absolute) {
     if (!absolute) {
       const x = this.scrollContainer.scrollLeft;
       const x = this.scrollContainer.scrollLeft;
@@ -831,7 +875,7 @@ export class Timeline extends TimelineEventsEmitter {
   /**
   /**
    * Snap a value to a nearest beautiful point.
    * Snap a value to a nearest beautiful point.
    */
    */
-  private snapVal(ms: number): number {
+  public snapVal(ms: number): number {
     // Apply snap to steps if enabled.
     // Apply snap to steps if enabled.
     if (this.options.snapsPerSeconds && this.options.snapEnabled) {
     if (this.options.snapsPerSeconds && this.options.snapEnabled) {
       const stopsPerPixel = 1000 / this.options.snapsPerSeconds;
       const stopsPerPixel = 1000 / this.options.snapsPerSeconds;
@@ -1224,7 +1268,7 @@ export class Timeline extends TimelineEventsEmitter {
   }
   }
 
 
   private renderKeyframes(): void {
   private renderKeyframes(): void {
-    this.forEachKeyframe((keyframe, keyframeIndex, rowSize) => {
+    this.forEachKeyframe((keyframe, keyframeIndex, rowSize): boolean => {
       const row = rowSize.row;
       const row = rowSize.row;
       const pos = this.getKeyframePosition(keyframe, rowSize);
       const pos = this.getKeyframePosition(keyframe, rowSize);
       if (pos) {
       if (pos) {
@@ -1307,6 +1351,7 @@ export class Timeline extends TimelineEventsEmitter {
 
 
         this.ctx.restore();
         this.ctx.restore();
       }
       }
+      return false;
     });
     });
   }
   }
 
 
@@ -1413,7 +1458,7 @@ export class Timeline extends TimelineEventsEmitter {
     // Rescale when animation is played out of the bounds.
     // Rescale when animation is played out of the bounds.
     if (this.valToPx(this.val, true) > this.scrollContainer.scrollWidth) {
     if (this.valToPx(this.val, true) > this.scrollContainer.scrollWidth) {
       this.rescale();
       this.rescale();
-      if (!this.isPanStarted && this.drag && this.drag.type !== TimelineDraggableType.timeline) {
+      if (!this.isPanStarted && this.drag && this.drag.type !== TimelineElementType.Timeline) {
         this.scrollLeft();
         this.scrollLeft();
       }
       }
     }
     }
@@ -1478,7 +1523,7 @@ export class Timeline extends TimelineEventsEmitter {
 
 
   public setTime(val: number): boolean {
   public setTime(val: number): boolean {
     // don't allow to change time during drag:
     // don't allow to change time during drag:
-    if (this.drag && this.drag.type === TimelineDraggableType.timeline) {
+    if (this.drag && this.drag.type === TimelineElementType.Timeline) {
       return false;
       return false;
     }
     }
 
 
@@ -1503,7 +1548,25 @@ export class Timeline extends TimelineEventsEmitter {
       keyframes: selectedKeyframes,
       keyframes: selectedKeyframes,
     } as TimelineSelectedEvent);
     } as TimelineSelectedEvent);
   }
   }
-
+  private emitDragStartedEvent(): void {
+    this.emit(TimelineEvents.DragStarted, {
+      keyframes: this.drag.elements,
+    });
+  }
+  private emitDragFinishedEvent(): void {
+    if (this.drag && this.drag.changed) {
+      this.emit(TimelineEvents.DragFinished, {
+        keyframes: this.drag.elements,
+      });
+    }
+  }
+  private emitDragEvent(): void {
+    if (this.drag) {
+      this.emit(TimelineEvents.Drag, {
+        keyframes: this.drag.elements,
+      });
+    }
+  }
   public setScrollLeft(value: number): void {
   public setScrollLeft(value: number): void {
     if (this.scrollContainer) {
     if (this.scrollContainer) {
       this.scrollContainer.scrollLeft = value;
       this.scrollContainer.scrollLeft = value;
@@ -1650,103 +1713,112 @@ export class Timeline extends TimelineEventsEmitter {
   }
   }
 
 
   /**
   /**
-   * Find clickable elements under the mouse position.
+   * get draggable element.
+   * Filter elements and get first element by a priority.
+   * @param Array
+   * @param val current mouse value
    */
    */
-  private getDraggable(pos: MousePoint): TimelineDraggableData {
-    // few extra pixels to select items:
-    const helperSelector = Math.max(2, pos.radius);
-    let draggable: TimelineDraggableData = null;
-    let lastLength = Number.MAX_SAFE_INTEGER;
-    if (pos.y >= this.options.headerHeight && this.options.keyframesDraggable) {
-      this.forEachKeyframe((keyframe, keyframeIndex, rowSize) => {
-        if (keyframe.draggable !== undefined) {
-          if (!keyframe.draggable) {
-            return;
-          }
-        }
-
-        /*  const row = rowSize.row; if (row.keyframesDraggable !== undefined) {
-            if (!row.keyframesDraggable) {
-              return;
-            }
-          }
-*/
-        const keyframePos = this.getKeyframePosition(keyframe, rowSize);
-        if (keyframePos) {
-          const dist = TimelineUtils.getDistance(keyframePos.x, keyframePos.y, pos.x, pos.y);
-          if (dist <= keyframePos.height + helperSelector) {
-            if (!draggable) {
-              lastLength = dist;
-              draggable = {
-                keyframe: keyframe,
-                val: keyframe.val,
-                type: TimelineDraggableType.keyframe,
-              } as TimelineDraggableData;
-            } else if (dist <= lastLength) {
-              draggable.keyframe = keyframe;
-              draggable.val = keyframe.val;
-            }
-          }
-        }
-      });
-
-      if (draggable) {
-        return draggable;
+  private findDraggable(elements: Array<TimelineClickableElement>, val: number): TimelineClickableElement {
+    // filter and sort: Timeline, individual keyframes, stripes (distance).
+    const getPriority = (type: TimelineElementType): number => {
+      if (type === TimelineElementType.Timeline) {
+        return 1;
+      } else if (type === TimelineElementType.Keyframe) {
+        return 2;
+      } else if (type === TimelineElementType.Stripe) {
+        return 3;
       }
       }
+      return -1;
+    };
+    const filteredElements = elements.filter((element) => {
+      if (element.type === TimelineElementType.Keyframe) {
+        const draggable =
+          (this.options.keyframesDraggable === undefined ? true : !!this.options.keyframesDraggable) && (element.keyframe.draggable === undefined ? true : !!element.keyframe.draggable);
 
 
-      // TODO:
-      // Return keyframes lanes:
-      const data = this.calculateRowsBounds();
-      if (this.options.stripesDraggable && data) {
-        let overlapped: RowSize = null;
-        if (data.rows) {
-          for (let i = 0; i < data.rows.length; i++) {
-            const rowSizeData = data.rows[i];
-            if (!rowSizeData) {
-              break;
-            }
-
-            const draggable = TimelineStyleUtils.getRowStyle<boolean>(rowSizeData.row, this.options.rowsStyle, 'stripeDraggable', true);
-            if (!draggable) {
-              break;
-            }
-
-            const laneOverlapped = TimelineUtils.isOverlap(pos.x, pos.y, rowSizeData.stripeRect);
-            if (laneOverlapped) {
-              overlapped = rowSizeData;
-            }
-          }
+        if (!draggable) {
+          return false;
+        }
+      } else if (element.type === TimelineElementType.Stripe) {
+        const draggable = (this.options.stripesDraggable === undefined ? true : !!this.options.stripesDraggable) && (element.row.stripeDraggable === undefined ? true : !!element.row.stripeDraggable);
+        if (!draggable) {
+          return false;
         }
         }
+      }
+      return true;
+    });
 
 
-        if (overlapped) {
-          draggable = {
-            type: TimelineDraggableType.keyframes,
-          } as TimelineDraggableData;
+    const sorted = filteredElements.sort((a, b): number => {
+      const prioA = getPriority(a.type);
+      const prioB = getPriority(b.type);
+      if (prioA == prioB) {
+        return TimelineUtils.getDistance(a.val, val) > TimelineUtils.getDistance(b.val, val) ? 1 : 0;
+      }
 
 
-          draggable.val = this.mousePosToVal(pos.x, true);
+      return prioA > prioB ? 1 : 0;
+    });
+    if (sorted.length > 0) {
+      return sorted[sorted.length - 1];
+    }
 
 
-          if (overlapped.row && overlapped.row.keyframes) {
-            draggable.keyframes = overlapped.row.keyframes;
+    return null;
+  }
 
 
-            const snapped = this.snapVal(overlapped.minValue);
-            // get snapped mouse pos based on a min value.
-            draggable.val += overlapped.minValue - snapped;
-          }
+  /**
+   * get all clickable elements by a screen point.
+   */
+  public elementFromPoint(pos: DOMPoint, clickRadius = 2): Array<TimelineClickableElement> {
+    clickRadius = Math.max(clickRadius, 1);
+    const toReturn: Array<TimelineClickableElement> = [];
 
 
-          return draggable;
-        }
-      }
+    if (!pos) {
+      return toReturn;
     }
     }
 
 
     // Check whether we can drag timeline.
     // Check whether we can drag timeline.
     const timeLinePos = this.valToPx(this.val);
     const timeLinePos = this.valToPx(this.val);
-    const width = Math.max((this.options.timelineThicknessPx || 1) * this.pixelRatio, this.options.timelineCapWidthPx * this.pixelRatio || 1) + helperSelector;
+    const width = Math.max((this.options.timelineThicknessPx || 1) * this.pixelRatio, this.options.timelineCapWidthPx * this.pixelRatio || 1) + clickRadius;
     if (pos.y <= this.options.headerHeight || (pos.x >= timeLinePos - width / 2 && pos.x <= timeLinePos + width / 2)) {
     if (pos.y <= this.options.headerHeight || (pos.x >= timeLinePos - width / 2 && pos.x <= timeLinePos + width / 2)) {
-      return {
+      toReturn.push({
         val: this.val,
         val: this.val,
-        type: TimelineDraggableType.timeline,
-      } as TimelineDraggableData;
+        type: TimelineElementType.Timeline,
+      } as TimelineClickableElement);
+    }
+
+    if (pos.y >= this.options.headerHeight && this.options.keyframesDraggable) {
+      this.forEachKeyframe((keyframe, keyframeIndex, rowModel, rowIndex, isNextRow): boolean => {
+        // Check keyframes stripe overlap
+        if (isNextRow && rowModel.stripeRect) {
+          const keyframesStripeOverlapped = TimelineUtils.isOverlap(pos.x, pos.y, rowModel.stripeRect);
+          if (keyframesStripeOverlapped) {
+            const stripe = {
+              val: this.mousePosToVal(pos.x, true),
+              type: TimelineElementType.Stripe,
+              row: rowModel.row,
+            } as TimelineClickableElement;
+
+            const snapped = this.snapVal(rowModel.minValue);
+            // get snapped mouse pos based on a min value.
+            stripe.val += rowModel.minValue - snapped;
+            toReturn.push(stripe);
+          }
+        }
+
+        const keyframePos = this.getKeyframePosition(keyframe, rowModel);
+        if (keyframePos) {
+          const dist = TimelineUtils.getDistance(keyframePos.x, keyframePos.y, pos.x, pos.y);
+          if (dist <= keyframePos.height + clickRadius) {
+            toReturn.push({
+              keyframe: keyframe,
+              val: keyframe.val,
+              row: rowModel.row,
+              type: TimelineElementType.Keyframe,
+            } as TimelineClickableElement);
+          }
+        }
+        return false;
+      }, true);
     }
     }
+    return toReturn;
   }
   }
 
 
   /**
   /**

+ 33 - 0
src/utils/events/timelineClickEvent.ts

@@ -0,0 +1,33 @@
+import { TimelineClickableElement } from '../timelineClickableElement';
+
+export class TimelineClickEvent {
+  args: MouseEvent;
+  /**
+   * Clicked screen position.
+   */
+  pos: DOMPoint = new DOMPoint();
+  /**
+   * Click time value.
+   */
+  val: number;
+
+  /**
+   * Elements under the click
+   */
+  elements: Array<TimelineClickableElement>;
+  /**
+   * Target element
+   */
+  target: TimelineClickableElement;
+  private _prevented = false;
+  /**
+   * Prevent default click logic.
+   */
+  preventDefault(): void {
+    this._prevented = true;
+  }
+
+  isPrevented(): boolean {
+    return this._prevented;
+  }
+}

+ 3 - 0
src/utils/events/timelineDragEvent.ts

@@ -0,0 +1,3 @@
+import { TimelineClickEvent } from './timelineClickEvent';
+
+export class TimelineDragEvent extends TimelineClickEvent {}

+ 0 - 7
src/utils/selectionTuple.ts

@@ -1,7 +0,0 @@
-import { TimelineKeyframe } from '../timelineKeyframe';
-import { TimelineRow } from '../timelineRow';
-
-export interface SelectionTuple {
-  keyframe: TimelineKeyframe;
-  row: TimelineRow;
-}

+ 23 - 0
src/utils/timelineClickableElement.ts

@@ -0,0 +1,23 @@
+import { TimelineKeyframe } from '../timelineKeyframe';
+import { TimelineElementType } from '../enums/timelineElementType';
+import { TimelineRow } from '../timelineRow';
+/**
+ * Timeline clickable element.
+ */
+export interface TimelineClickableElement {
+  type: TimelineElementType;
+  /**
+   * Timeline value,
+   */
+  val: number;
+
+  /**
+   * Related keyframe model. In a case of stripe this value will be empty.
+   */
+  keyframe: TimelineKeyframe;
+
+  /**
+   * Related row model.
+   */
+  row: TimelineRow;
+}

+ 9 - 8
src/utils/timelineDraggableData.ts

@@ -1,15 +1,16 @@
-import { TimelineKeyframe } from '../timelineKeyframe';
-import { TimelineDraggableType } from '../enums/timelineDraggableType';
+import { TimelineClickableElement } from './timelineClickableElement';
+import { TimelineElementType } from '..';
+
 export interface TimelineDraggableData {
 export interface TimelineDraggableData {
-  type: TimelineDraggableType;
+  changed: boolean;
   /**
   /**
-   * First selected keyframe (clicked one)
+   * Drag click target
    */
    */
-  keyframe?: TimelineKeyframe;
+  target: TimelineClickableElement;
+  elements: Array<TimelineClickableElement>;
+  type: TimelineElementType;
   /**
   /**
-   * Related draggable keyframes.
+   * Current drag data.
    */
    */
-  keyframes?: Array<TimelineKeyframe>;
-  changed: boolean;
   val: number;
   val: number;
 }
 }

+ 0 - 1
src/utils/timelineUtils.ts

@@ -9,7 +9,6 @@ export class TimelineUtils {
    */
    */
   static isOverlap(x: number, y: number, rectangle: DOMRect): boolean {
   static isOverlap(x: number, y: number, rectangle: DOMRect): boolean {
     if (!rectangle) {
     if (!rectangle) {
-      console.error('rectangle argument cannot be empty');
       return false;
       return false;
     }
     }
 
 

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff