UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

357 lines (276 loc) • 12.2 kB
"use strict"; var $ = require("../../core/renderer"), each = require("../../core/utils/iterator").each, eventsEngine = require("../../events/core/events_engine"), translator = require("../../animation/translator"), fx = require("../../animation/fx"), dragEvents = require("../../events/drag"), mathUtils = require("../../core/utils/math"), Animator = require("../scroll_view/animator"), eventUtils = require("../../events/utils"), registerDecorator = require("./ui.list.edit.decorator_registry").register, EditDecorator = require("./ui.list.edit.decorator"); var ReorderScrollAnimator = Animator.inherit({ ctor: function ctor(strategy) { this.callBase(); this._strategy = strategy; }, _isFinished: function _isFinished() { return this._strategy.scrollFinished(); }, _step: function _step() { this._strategy.scrollByStep(); } }); var LIST_EDIT_DECORATOR = "dxListEditDecorator", DRAG_START_EVENT_NAME = eventUtils.addNamespace(dragEvents.start, LIST_EDIT_DECORATOR), DRAG_UPDATE_EVENT_NAME = eventUtils.addNamespace(dragEvents.move, LIST_EDIT_DECORATOR), DRAG_END_EVENT_NAME = eventUtils.addNamespace(dragEvents.end, LIST_EDIT_DECORATOR), REORDER_HANDLE_CONTAINER_CLASS = "dx-list-reorder-handle-container", REORDER_HANDLE_CLASS = "dx-list-reorder-handle", REOREDERING_ITEM_CLASS = "dx-list-item-reordering", REOREDERING_ITEM_GHOST_CLASS = "dx-list-item-ghost-reordering"; registerDecorator("reorder", "default", EditDecorator.inherit({ _init: function _init() { this._groupedEnabled = this._list.option("grouped"); this._initAnimator(); }, _initAnimator: function _initAnimator() { this._scrollAnimator = new ReorderScrollAnimator(this); }, _startAnimator: function _startAnimator() { if (!this._scrollAnimator.inProgress()) { this._scrollAnimator.start(); } }, _stopAnimator: function _stopAnimator() { this._scrollAnimator.stop(); }, afterBag: function afterBag(config) { var $itemElement = config.$itemElement, $container = config.$container; var $handle = $("<div>").addClass(REORDER_HANDLE_CLASS); var lockedDrag = false; eventsEngine.on($handle, "dxpointerdown", function (e) { lockedDrag = !eventUtils.isMouseEvent(e); }); eventsEngine.on($handle, "dxhold", { timeout: 30 }, function (e) { e.cancel = true; lockedDrag = false; }); eventsEngine.on($handle, DRAG_START_EVENT_NAME, { direction: "vertical", immediate: true }, function (e) { if (lockedDrag) { e.cancel = true; return; } this._dragStartHandler($itemElement, e); }.bind(this)); eventsEngine.on($handle, DRAG_UPDATE_EVENT_NAME, this._dragHandler.bind(this, $itemElement)); eventsEngine.on($handle, DRAG_END_EVENT_NAME, this._dragEndHandler.bind(this, $itemElement)); $container.addClass(REORDER_HANDLE_CONTAINER_CLASS); $container.append($handle); }, _dragStartHandler: function _dragStartHandler($itemElement, e) { if ($itemElement.is(".dx-state-disabled, .dx-state-disabled *")) { e.cancel = true; return; } this._stopPreviousAnimation(); e.targetElements = []; this._cacheItemsPositions(); this._startPointerOffset = e.pageY - $itemElement.offset().top; this._elementHeight = $itemElement.outerHeight(); var itemIndex = this._list.getFlatIndexByItemElement($itemElement); this._startIndex = itemIndex; this._lastIndex = itemIndex; this._cacheScrollData(); var that = this; this._createGhostTimeout = setTimeout(function () { that._createGhost($itemElement); that._updateGhostPosition(); $itemElement.addClass(REOREDERING_ITEM_CLASS); }); }, _stopPreviousAnimation: function _stopPreviousAnimation() { fx.stop(this._$ghostItem, true); }, _cacheItemsPositions: function _cacheItemsPositions() { var itemPositions = this._itemPositions = []; each(this._list.itemElements(), function (index, item) { var cachedPosition = null; itemPositions.push(function () { cachedPosition = cachedPosition === null ? $(item).position().top : cachedPosition; return cachedPosition; }); }); }, _getDraggingElementPosition: function _getDraggingElementPosition() { return this._itemPositions[this._startIndex](); }, _getLastElementPosition: function _getLastElementPosition() { return this._itemPositions[this._lastIndex](); }, _cacheScrollData: function _cacheScrollData() { this._list.updateDimensions(); this._startScrollTop = this._list.scrollTop(); this._scrollOffset = 0; this._scrollHeight = this._list.scrollHeight(); this._clientHeight = this._list.clientHeight(); }, _scrollTop: function _scrollTop() { return this._startScrollTop + this._scrollOffset; }, _createGhost: function _createGhost($itemElement) { this._$ghostItem = $itemElement.clone(); this._$ghostItem.addClass(REOREDERING_ITEM_GHOST_CLASS).appendTo(this._list.itemsContainer()); this._startGhostPosition = this._getDraggingElementPosition() - this._$ghostItem.position().top; translator.move(this._$ghostItem, { top: this._startGhostPosition }); }, _dragHandler: function _dragHandler($itemElement, e) { this._topOffset = e.offset.y; this._updateItemPositions(); var pointerPosition = this._getPointerPosition(); this._toggleScroll(pointerPosition); }, _getPointerPosition: function _getPointerPosition() { return this._getDraggingElementPosition() + this._startPointerOffset + this._scrollOffset + this._topOffset; }, _toggleScroll: function _toggleScroll(pointerPosition) { if (this._scrollHeight <= this._clientHeight) { return; } var minOffset = this._elementHeight * 0.7, topOffset = this._clientHeight - (pointerPosition - this._scrollTop()), topOffsetRatio = topOffset / minOffset, bottomOffset = pointerPosition - this._scrollTop(), bottomOffsetRatio = bottomOffset / minOffset; if (topOffsetRatio < 1) { this._stepSize = this._adjustRationIntoRange(topOffsetRatio); this._startAnimator(); } else if (bottomOffsetRatio < 1) { this._stepSize = -this._adjustRationIntoRange(bottomOffsetRatio); this._startAnimator(); } else { this._stopAnimator(); } }, _adjustRationIntoRange: function _adjustRationIntoRange(ratio) { return mathUtils.fitIntoRange(Math.round(Math.abs(ratio - 1) * 7), 1, 7); }, _updateItemPositions: function _updateItemPositions() { this._updateGhostPosition(); this._updateOthersPositions(); }, _updateGhostPosition: function _updateGhostPosition() { if (!this._$ghostItem) { return; } translator.move(this._$ghostItem, { top: this._startGhostPosition + this._scrollOffset + this._topOffset }); }, _updateOthersPositions: function _updateOthersPositions() { var currentIndex = this._findItemIndexByPosition(this._getPointerPosition()); if (this._lastIndex === currentIndex || this._groupedEnabled && !this._sameParent(currentIndex)) { return; } var currentIndexOffset = currentIndex - this._startIndex, currentDirection = mathUtils.sign(currentIndexOffset), minIndex = Math.min(currentIndex, this._lastIndex), maxIndex = Math.max(currentIndex, this._lastIndex); for (var itemIndex = minIndex; itemIndex <= maxIndex; itemIndex++) { if (itemIndex === this._startIndex) { continue; } var $item = this._list.getItemElementByFlatIndex(itemIndex), itemIndexOffset = itemIndex - this._startIndex, itemDirection = mathUtils.sign(itemIndexOffset), offsetsDifference = Math.abs(itemIndexOffset) <= Math.abs(currentIndexOffset), sameDirections = currentDirection === itemDirection, setupPosition = offsetsDifference && sameDirections, resetPosition = !offsetsDifference || !sameDirections; fx.stop($item); if (setupPosition) { fx.animate($item, { type: "slide", to: { top: this._elementHeight * -currentDirection }, duration: 300 }); } if (resetPosition) { fx.animate($item, { type: "slide", to: { top: 0 }, duration: 300 }); } } this._lastIndex = currentIndex; }, _sameParent: function _sameParent(index) { var $dragging = this._list.getItemElementByFlatIndex(this._startIndex), $over = this._list.getItemElementByFlatIndex(index); return $over.parent().get(0) === $dragging.parent().get(0); }, scrollByStep: function scrollByStep() { this._scrollOffset += this._stepSize; this._list.scrollBy(this._stepSize); this._updateItemPositions(); }, scrollFinished: function scrollFinished() { var scrollTop = this._scrollTop(), rejectScrollTop = scrollTop <= 0 && this._stepSize < 0, rejectScrollBottom = scrollTop >= this._scrollHeight - this._clientHeight && this._stepSize > 0; return rejectScrollTop || rejectScrollBottom; }, _dragEndHandler: function _dragEndHandler($itemElement) { this._scrollAnimator.stop(); fx.animate(this._$ghostItem, { type: "slide", to: { top: this._startGhostPosition + this._getLastElementPosition() - this._getDraggingElementPosition() }, duration: 300 }).done(function () { $itemElement.removeClass(REOREDERING_ITEM_CLASS); this._resetPositions(); this._list.reorderItem($itemElement, this._list.getItemElementByFlatIndex(this._lastIndex)); this._deleteGhost(); }.bind(this)); }, _deleteGhost: function _deleteGhost() { if (!this._$ghostItem) { return; } this._$ghostItem.remove(); }, _resetPositions: function _resetPositions() { var minIndex = Math.min(this._startIndex, this._lastIndex), maxIndex = Math.max(this._startIndex, this._lastIndex); for (var itemIndex = minIndex; itemIndex <= maxIndex; itemIndex++) { var $item = this._list.getItemElementByFlatIndex(itemIndex); translator.resetPosition($item); } }, _findItemIndexByPosition: function _findItemIndexByPosition(position) { var minIndex = 0; var maxIndex = this._itemPositions.length - 1; var currentIndex; var currentPosition; while (minIndex <= maxIndex) { currentIndex = (minIndex + maxIndex) / 2 | 0; currentPosition = this._itemPositions[currentIndex](); if (currentPosition < position) { minIndex = currentIndex + 1; } else if (currentPosition > position) { maxIndex = currentIndex - 1; } else { return currentIndex; } } return mathUtils.fitIntoRange(minIndex, 0, Math.max(maxIndex, 0)); }, getExcludedSelectors: function getExcludedSelectors(selectors) { selectors.push("." + REOREDERING_ITEM_GHOST_CLASS); }, dispose: function dispose() { clearTimeout(this._createGhostTimeout); this.callBase.apply(this, arguments); } }));