devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
672 lines (669 loc) • 28.7 kB
JavaScript
/**
* DevExtreme (ui/sortable.js)
* Version: 20.1.7
* Build date: Tue Aug 25 2020
*
* Copyright (c) 2012 - 2020 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
var _renderer = require("../core/renderer");
var _renderer2 = _interopRequireDefault(_renderer);
var _component_registrator = require("../core/component_registrator");
var _component_registrator2 = _interopRequireDefault(_component_registrator);
var _extend = require("../core/utils/extend");
var _draggable = require("./draggable");
var _draggable2 = _interopRequireDefault(_draggable);
var _dom = require("../core/utils/dom");
var _window = require("../core/utils/window");
var _position = require("../core/utils/position");
var _translator = require("../animation/translator");
var _translator2 = _interopRequireDefault(_translator);
var _fx = require("../animation/fx");
var _fx2 = _interopRequireDefault(_fx);
var _deferred = require("../core/utils/deferred");
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
"default": obj
}
}
function _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 SORTABLE = "dxSortable";
var PLACEHOLDER_CLASS = "placeholder";
var CLONE_CLASS = "clone";
var isElementVisible = function(itemElement) {
return (0, _renderer2.default)(itemElement).is(":visible")
};
var animate = function(element, config) {
var _config$to, _config$to2;
if (!element) {
return
}
var left = (null === (_config$to = config.to) || void 0 === _config$to ? void 0 : _config$to.left) || 0;
var top = (null === (_config$to2 = config.to) || void 0 === _config$to2 ? void 0 : _config$to2.top) || 0;
element.style.transform = "translate(".concat(left, "px,").concat(top, "px)");
element.style.transition = _fx2.default.off ? "" : "all ".concat(config.duration, "ms ").concat(config.easing)
};
var stopAnimation = function(element) {
if (!element) {
return
}
element.style.transform = "";
element.style.transition = ""
};
var Sortable = _draggable2.default.inherit({
_getDefaultOptions: function() {
return (0, _extend.extend)(this.callBase(), {
clone: true,
filter: "> *",
itemOrientation: "vertical",
dropFeedbackMode: "push",
allowDropInsideItem: false,
allowReordering: true,
moveItemOnDrop: false,
onDragChange: null,
onAdd: null,
onRemove: null,
onReorder: null,
onPlaceholderPrepared: null,
animation: {
type: "slide",
duration: 300,
easing: "ease"
},
fromIndex: null,
toIndex: null,
dropInsideItem: false,
itemPoints: null
})
},
reset: function() {
this.option({
dropInsideItem: false,
toIndex: null,
fromIndex: null
});
if (this._$placeholderElement) {
this._$placeholderElement.remove()
}
this._$placeholderElement = null;
if (!this._isIndicateMode() && this._$modifiedItem) {
this._$modifiedItem.css("marginBottom", this._modifiedItemMargin);
this._$modifiedItem = null
}
},
_getPrevVisibleItem: function(items, index) {
return items.slice(0, index).reverse().filter(isElementVisible)[0]
},
_dragStartHandler: function(e) {
this.callBase.apply(this, arguments);
if (true === e.cancel) {
return
}
var $sourceElement = this._getSourceElement();
this._updateItemPoints();
this.option("fromIndex", this._getElementIndex($sourceElement))
},
_dragEnterHandler: function() {
this.callBase.apply(this, arguments);
if (this === this._getSourceDraggable()) {
return
}
this._updateItemPoints();
this.option("fromIndex", -1);
if (!this._isIndicateMode()) {
var itemPoints = this.option("itemPoints");
var lastItemPoint = itemPoints[itemPoints.length - 1];
if (lastItemPoint) {
var $element = this.$element();
var $sourceElement = this._getSourceElement();
var isVertical = this._isVerticalOrientation();
var sourceElementSize = isVertical ? $sourceElement.outerHeight(true) : $sourceElement.outerWidth(true);
var scrollSize = $element.get(0)[isVertical ? "scrollHeight" : "scrollWidth"];
var scrollPosition = $element.get(0)[isVertical ? "scrollTop" : "scrollLeft"];
var positionProp = isVertical ? "top" : "left";
var lastPointPosition = lastItemPoint[positionProp];
var elementPosition = $element.offset()[positionProp];
var freeSize = elementPosition + scrollSize - scrollPosition - lastPointPosition;
if (freeSize < sourceElementSize) {
if (isVertical) {
var items = this._getItems();
var $lastItem = (0, _renderer2.default)(this._getPrevVisibleItem(items));
this._$modifiedItem = $lastItem;
this._modifiedItemMargin = $lastItem.get(0).style.marginBottom;
$lastItem.css("marginBottom", sourceElementSize - freeSize);
var $sortable = $lastItem.closest(".dx-sortable");
var sortable = $sortable.data("dxScrollable") || $sortable.data("dxScrollView");
sortable && sortable.update()
}
}
}
}
},
dragEnter: function() {
if (this !== this._getTargetDraggable()) {
this.option("toIndex", -1)
}
},
dragLeave: function() {
if (this !== this._getTargetDraggable()) {
this.option("toIndex", this.option("fromIndex"))
}
},
_isInsideTargetDraggable: function(event) {
var $targetDraggable = this._getTargetDraggable().$element();
var $scrollable = this._getScrollable($targetDraggable);
if ($scrollable) {
var offset = $scrollable.offset();
var validY = offset.top + $scrollable.height() >= event.pageY && offset.top <= event.pageY;
var validX = offset.left + $scrollable.width() >= event.pageX && offset.left <= event.pageX;
return validY && validX
}
return true
},
dragEnd: function(sourceEvent) {
var $sourceElement = this._getSourceElement();
var sourceDraggable = this._getSourceDraggable();
var isSourceDraggable = sourceDraggable.NAME !== this.NAME;
var toIndex = this.option("toIndex");
var isInsideTargetDraggable = this._isInsideTargetDraggable(sourceEvent.event);
if (null !== toIndex && toIndex >= 0 && isInsideTargetDraggable) {
var cancelAdd;
var cancelRemove;
if (sourceDraggable !== this) {
cancelAdd = this._fireAddEvent(sourceEvent);
if (!cancelAdd) {
cancelRemove = this._fireRemoveEvent(sourceEvent)
}
}
if (isSourceDraggable) {
_translator2.default.resetPosition($sourceElement)
}
if (this.option("moveItemOnDrop")) {
!cancelAdd && this._moveItem($sourceElement, toIndex, cancelRemove)
}
if (sourceDraggable === this) {
return this._fireReorderEvent(sourceEvent)
}
}
return (new _deferred.Deferred).resolve()
},
dragMove: function(e) {
var itemPoints = this.option("itemPoints");
if (!itemPoints) {
return
}
var isVertical = this._isVerticalOrientation();
var axisName = isVertical ? "top" : "left";
var cursorPosition = isVertical ? e.pageY : e.pageX;
var rtlEnabled = this.option("rtlEnabled");
var itemPoint;
for (var i = itemPoints.length - 1; i >= 0; i--) {
var centerPosition = itemPoints[i + 1] && (itemPoints[i][axisName] + itemPoints[i + 1][axisName]) / 2;
if ((!isVertical && rtlEnabled ? cursorPosition > centerPosition : centerPosition > cursorPosition) || void 0 === centerPosition) {
itemPoint = itemPoints[i]
} else {
break
}
}
if (itemPoint) {
this._updatePlaceholderPosition(e, itemPoint);
if (this._verticalScrollHelper.isScrolling() && this._isIndicateMode()) {
this._movePlaceholder()
}
}
},
_isIndicateMode: function() {
return "indicate" === this.option("dropFeedbackMode") || this.option("allowDropInsideItem")
},
_createPlaceholder: function() {
var $placeholderContainer;
if (this._isIndicateMode()) {
$placeholderContainer = (0, _renderer2.default)("<div>").addClass(this._addWidgetPrefix(PLACEHOLDER_CLASS)).insertBefore(this._getSourceDraggable()._$dragElement)
}
this._$placeholderElement = $placeholderContainer;
return $placeholderContainer
},
_getItems: function() {
var itemsSelector = this._getItemsSelector();
return this._$content().find(itemsSelector).not("." + this._addWidgetPrefix(PLACEHOLDER_CLASS)).not("." + this._addWidgetPrefix(CLONE_CLASS)).toArray()
},
_allowReordering: function() {
var sourceDraggable = this._getSourceDraggable();
var targetDraggable = this._getTargetDraggable();
return sourceDraggable !== targetDraggable || this.option("allowReordering")
},
_isValidPoint: function(visibleIndex, draggableVisibleIndex, dropInsideItem) {
var allowDropInsideItem = this.option("allowDropInsideItem");
var allowReordering = dropInsideItem || this._allowReordering();
if (!allowReordering && (0 !== visibleIndex || !allowDropInsideItem)) {
return false
}
if (!this._isIndicateMode()) {
return true
}
return draggableVisibleIndex === -1 || visibleIndex !== draggableVisibleIndex && (dropInsideItem || visibleIndex !== draggableVisibleIndex + 1)
},
_getItemPoints: function() {
var that = this;
var result = [];
var $item;
var offset;
var itemWidth;
var rtlEnabled = that.option("rtlEnabled");
var isVertical = that._isVerticalOrientation();
var itemElements = that._getItems();
var visibleItemElements = itemElements.filter(isElementVisible);
var visibleItemCount = visibleItemElements.length;
var $draggableItem = this._getDraggableElement();
var draggableVisibleIndex = visibleItemElements.indexOf($draggableItem.get(0));
if (visibleItemCount) {
for (var i = 0; i <= visibleItemCount; i++) {
var needCorrectLeftPosition = !isVertical && rtlEnabled ^ i === visibleItemCount;
var needCorrectTopPosition = isVertical && i === visibleItemCount;
if (i < visibleItemCount) {
$item = (0, _renderer2.default)(visibleItemElements[i]);
offset = $item.offset();
itemWidth = $item.outerWidth()
}
result.push({
dropInsideItem: false,
left: offset.left + (needCorrectLeftPosition ? itemWidth : 0),
top: offset.top + (needCorrectTopPosition ? result[i - 1].height : 0),
index: i === visibleItemCount ? itemElements.length : itemElements.indexOf($item.get(0)),
$item: $item,
width: $item.outerWidth(),
height: $item.outerHeight(),
isValid: that._isValidPoint(i, draggableVisibleIndex)
})
}
if (this.option("allowDropInsideItem")) {
var points = result;
result = [];
for (var _i = 0; _i < points.length; _i++) {
result.push(points[_i]);
if (points[_i + 1]) {
result.push((0, _extend.extend)({}, points[_i], {
dropInsideItem: true,
top: Math.floor((points[_i].top + points[_i + 1].top) / 2),
left: Math.floor((points[_i].left + points[_i + 1].left) / 2),
isValid: this._isValidPoint(_i, draggableVisibleIndex, true)
}))
}
}
}
} else {
result.push({
dropInsideItem: false,
index: 0,
isValid: true
})
}
return result
},
_updateItemPoints: function() {
this.option("itemPoints", this._getItemPoints())
},
_getElementIndex: function($itemElement) {
return this._getItems().indexOf($itemElement.get(0))
},
_getDragTemplateArgs: function($element) {
var args = this.callBase.apply(this, arguments);
args.model.fromIndex = this._getElementIndex($element);
return args
},
_togglePlaceholder: function(value) {
this._$placeholderElement && this._$placeholderElement.toggle(value)
},
_isVerticalOrientation: function() {
return "vertical" === this.option("itemOrientation")
},
_normalizeToIndex: function(toIndex, dropInsideItem) {
var isAnotherDraggable = this._getSourceDraggable() !== this._getTargetDraggable();
var fromIndex = this.option("fromIndex");
if (null === toIndex) {
return fromIndex
}
return Math.max(isAnotherDraggable || fromIndex >= toIndex || dropInsideItem ? toIndex : toIndex - 1, 0)
},
_updatePlaceholderPosition: function(e, itemPoint) {
var sourceDraggable = this._getSourceDraggable();
var toIndex = this._normalizeToIndex(itemPoint.index, itemPoint.dropInsideItem);
var eventArgs = (0, _extend.extend)(this._getEventArgs(e), {
toIndex: toIndex,
dropInsideItem: itemPoint.dropInsideItem
});
itemPoint.isValid && this._getAction("onDragChange")(eventArgs);
if (eventArgs.cancel || !itemPoint.isValid) {
if (!itemPoint.isValid) {
this.option({
dropInsideItem: false,
toIndex: null
})
}
return
}
this.option({
dropInsideItem: itemPoint.dropInsideItem,
toIndex: itemPoint.index
});
this._getAction("onPlaceholderPrepared")((0, _extend.extend)(this._getEventArgs(e), {
placeholderElement: (0, _dom.getPublicElement)(this._$placeholderElement),
dragElement: (0, _dom.getPublicElement)(sourceDraggable._$dragElement)
}));
this._updateItemPoints()
},
_makeWidthCorrection: function($item, width) {
this._$scrollable = this._getScrollable($item);
if (this._$scrollable && this._$scrollable.width() < width) {
var scrollableWidth = this._$scrollable.width();
var offsetLeft = $item.offset().left - this._$scrollable.offset().left;
var offsetRight = scrollableWidth - $item.outerWidth() - offsetLeft;
if (offsetLeft > 0) {
width = scrollableWidth - offsetLeft
} else {
if (offsetRight > 0) {
width = scrollableWidth - offsetRight
} else {
width = scrollableWidth
}
}
}
return width
},
_updatePlaceholderSizes: function($placeholderElement, itemElement) {
var that = this;
var dropInsideItem = that.option("dropInsideItem");
var $item = (0, _renderer2.default)(itemElement);
var isVertical = that._isVerticalOrientation();
var width = "";
var height = "";
$placeholderElement.toggleClass(that._addWidgetPrefix("placeholder-inside"), dropInsideItem);
if (isVertical || dropInsideItem) {
width = $item.outerWidth()
}
if (!isVertical || dropInsideItem) {
height = $item.outerHeight()
}
width = that._makeWidthCorrection($item, width);
$placeholderElement.css({
width: width,
height: height
})
},
_moveItem: function($itemElement, index, cancelRemove) {
var $prevTargetItemElement;
var $itemElements = this._getItems();
var $targetItemElement = $itemElements[index];
var sourceDraggable = this._getSourceDraggable();
if (cancelRemove) {
$itemElement = $itemElement.clone();
sourceDraggable._toggleDragSourceClass(false, $itemElement)
}
if (!$targetItemElement) {
$prevTargetItemElement = $itemElements[index - 1]
}
this._moveItemCore($itemElement, $targetItemElement, $prevTargetItemElement)
},
_moveItemCore: function($targetItem, item, prevItem) {
if (!item && !prevItem) {
$targetItem.appendTo(this.$element())
} else {
if (prevItem) {
$targetItem.insertAfter((0, _renderer2.default)(prevItem))
} else {
$targetItem.insertBefore((0, _renderer2.default)(item))
}
}
},
_getDragStartArgs: function(e, $itemElement) {
return (0, _extend.extend)(this.callBase.apply(this, arguments), {
fromIndex: this._getElementIndex($itemElement)
})
},
_getEventArgs: function(e) {
var sourceDraggable = this._getSourceDraggable();
var targetDraggable = this._getTargetDraggable();
var dropInsideItem = targetDraggable.option("dropInsideItem");
return (0, _extend.extend)(this.callBase.apply(this, arguments), {
fromIndex: sourceDraggable.option("fromIndex"),
toIndex: this._normalizeToIndex(targetDraggable.option("toIndex"), dropInsideItem),
dropInsideItem: dropInsideItem
})
},
_optionChanged: function(args) {
var _this = this;
var name = args.name;
switch (name) {
case "onDragChange":
case "onPlaceholderPrepared":
case "onAdd":
case "onRemove":
case "onReorder":
this["_" + name + "Action"] = this._createActionByOption(name);
break;
case "itemOrientation":
case "allowDropInsideItem":
case "moveItemOnDrop":
case "dropFeedbackMode":
case "itemPoints":
case "animation":
case "allowReordering":
break;
case "fromIndex":
if (!this._$sourceElement) {
[false, true].forEach(function(isDragSource) {
var fromIndex = isDragSource ? args.value : args.previousValue;
if (null !== fromIndex) {
var $fromElement = (0, _renderer2.default)(_this._getItems()[fromIndex]);
_this._toggleDragSourceClass(isDragSource, $fromElement)
}
})
}
break;
case "dropInsideItem":
this._optionChangedDropInsideItem(args);
break;
case "toIndex":
this._optionChangedToIndex(args);
break;
default:
this.callBase(args)
}
},
_optionChangedDropInsideItem: function() {
if (this._isIndicateMode() && this._$placeholderElement) {
this._movePlaceholder()
}
},
_isPositionVisible: function(position) {
var $element = this.$element();
var scrollContainer;
if ("hidden" !== $element.css("overflow")) {
scrollContainer = $element.get(0)
} else {
$element.parents().each(function() {
if ("visible" !== (0, _renderer2.default)(this).css("overflow")) {
scrollContainer = this;
return false
}
})
}
if (scrollContainer) {
var clientRect = (0, _position.getBoundingRect)(scrollContainer);
var isVerticalOrientation = this._isVerticalOrientation();
var start = isVerticalOrientation ? "top" : "left";
var end = isVerticalOrientation ? "bottom" : "right";
var window = (0, _window.getWindow)();
var pageOffset = isVerticalOrientation ? window.pageYOffset : window.pageXOffset;
if (position[start] < clientRect[start] + pageOffset || position[start] > clientRect[end] + pageOffset) {
return false
}
}
return true
},
_optionChangedToIndex: function(args) {
var toIndex = args.value;
if (this._isIndicateMode()) {
var showPlaceholder = null !== toIndex && toIndex >= 0;
this._togglePlaceholder(showPlaceholder);
if (showPlaceholder) {
this._movePlaceholder()
}
} else {
this._moveItems(args.previousValue, args.value)
}
},
_makeLeftCorrection: function(left, leftMargin) {
var that = this;
var $scrollable = that._$scrollable;
if ($scrollable && that._isVerticalOrientation() && $scrollable.scrollLeft() > leftMargin) {
left += $scrollable.scrollLeft() - leftMargin
}
return left
},
_movePlaceholder: function() {
var that = this;
var $placeholderElement = that._$placeholderElement || that._createPlaceholder();
var items = that._getItems();
var toIndex = that.option("toIndex");
var isVerticalOrientation = that._isVerticalOrientation();
var rtlEnabled = this.option("rtlEnabled");
var dropInsideItem = that.option("dropInsideItem");
var position = null;
var leftMargin = 0;
var itemElement = items[toIndex];
if (itemElement) {
var $itemElement = (0, _renderer2.default)(itemElement);
position = $itemElement.offset();
leftMargin = parseFloat($itemElement.css("marginLeft"));
if (!isVerticalOrientation && rtlEnabled && !dropInsideItem) {
position.left += $itemElement.outerWidth(true)
}
} else {
var prevVisibleItemElement = itemElement = this._getPrevVisibleItem(items, toIndex);
if (prevVisibleItemElement) {
position = (0, _renderer2.default)(prevVisibleItemElement).offset();
if (isVerticalOrientation) {
position.top += (0, _renderer2.default)(prevVisibleItemElement).outerHeight(true)
} else {
if (!rtlEnabled) {
position.left += (0, _renderer2.default)(prevVisibleItemElement).outerWidth(true)
}
}
}
}
that._updatePlaceholderSizes($placeholderElement, itemElement);
if (position && !that._isPositionVisible(position)) {
position = null
}
if (position) {
position.left = that._makeLeftCorrection(position.left, leftMargin);
that._move(position, $placeholderElement)
}
$placeholderElement.toggle(!!position)
},
_getPositions: function(items, elementSize, fromIndex, toIndex) {
var positions = [];
for (var i = 0; i < items.length; i++) {
var position = 0;
if (null === toIndex || null === fromIndex) {
positions.push(position);
continue
}
if (fromIndex === -1) {
if (i >= toIndex) {
position = elementSize
}
} else {
if (toIndex === -1) {
if (i > fromIndex) {
position = -elementSize
}
} else {
if (fromIndex < toIndex) {
if (i > fromIndex && i < toIndex) {
position = -elementSize
}
} else {
if (fromIndex > toIndex) {
if (i >= toIndex && i < fromIndex) {
position = elementSize
}
}
}
}
}
positions.push(position)
}
return positions
},
_moveItems: function(prevToIndex, toIndex) {
var fromIndex = this.option("fromIndex");
var isVerticalOrientation = this._isVerticalOrientation();
var positionPropName = isVerticalOrientation ? "top" : "left";
var $draggableItem = this._getDraggableElement();
var elementSize = isVerticalOrientation ? ($draggableItem.outerHeight() + $draggableItem.outerHeight(true)) / 2 : ($draggableItem.outerWidth() + $draggableItem.outerWidth(true)) / 2;
var items = this._getItems();
var prevPositions = this._getPositions(items, elementSize, fromIndex, prevToIndex);
var positions = this._getPositions(items, elementSize, fromIndex, toIndex);
var animationConfig = this.option("animation");
var rtlEnabled = this.option("rtlEnabled");
for (var i = 0; i < items.length; i++) {
var itemElement = items[i];
var prevPosition = prevPositions[i];
var position = positions[i];
if (null === toIndex || null === fromIndex) {
stopAnimation(itemElement)
} else {
if (prevPosition !== position) {
animate(itemElement, (0, _extend.extend)({}, animationConfig, {
to: _defineProperty({}, positionPropName, !isVerticalOrientation && rtlEnabled ? -position : position)
}))
}
}
}
},
_toggleDragSourceClass: function(value, $element) {
var $sourceElement = $element || this._$sourceElement;
this.callBase.apply(this, arguments);
if (!this._isIndicateMode()) {
$sourceElement && $sourceElement.toggleClass(this._addWidgetPrefix("source-hidden"), value)
}
},
_dispose: function() {
this.reset();
this.callBase()
},
_fireAddEvent: function(sourceEvent) {
var args = this._getEventArgs(sourceEvent);
this._getAction("onAdd")(args);
return args.cancel
},
_fireRemoveEvent: function(sourceEvent) {
var sourceDraggable = this._getSourceDraggable();
var args = this._getEventArgs(sourceEvent);
sourceDraggable._getAction("onRemove")(args);
return args.cancel
},
_fireReorderEvent: function(sourceEvent) {
var args = this._getEventArgs(sourceEvent);
this._getAction("onReorder")(args);
return args.promise || (new _deferred.Deferred).resolve()
}
});
(0, _component_registrator2.default)(SORTABLE, Sortable);
module.exports = Sortable;
module.exports.default = module.exports;