sortable-dnd
Advanced tools
Comparing version 0.0.11 to 0.0.12
/*! | ||
* sortable-dnd v0.0.11 | ||
* sortable-dnd v0.0.12 | ||
* open source under the MIT license | ||
@@ -95,227 +95,272 @@ * https://github.com/mfuu/sortable-dnd#readme | ||
var R_SPACE = /\s+/g; | ||
var utils = { | ||
/** | ||
* add specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
on: function on(el, event, fn) { | ||
if (window.addEventListener) { | ||
el.addEventListener(event, fn, !IE11OrLess && captureMode); | ||
} else if (window.attachEvent) { | ||
el.addEventListener('on' + event, fn); | ||
} | ||
}, | ||
/** | ||
* add specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
/** | ||
* remove specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
off: function off(el, event, fn) { | ||
if (window.removeEventListener) { | ||
el.removeEventListener(event, fn, !IE11OrLess && captureMode); | ||
} else if (window.detachEvent) { | ||
el.detachEvent('on' + event, fn); | ||
} | ||
}, | ||
getWindowScrollingElement: function getWindowScrollingElement() { | ||
var scrollingElement = document.scrollingElement; | ||
function on(el, event, fn) { | ||
if (window.addEventListener) { | ||
el.addEventListener(event, fn, !IE11OrLess && captureMode); | ||
} else if (window.attachEvent) { | ||
el.addEventListener('on' + event, fn); | ||
} | ||
} | ||
/** | ||
* remove specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
if (scrollingElement) { | ||
return scrollingElement; | ||
} else { | ||
return document.documentElement; | ||
} | ||
}, | ||
function off(el, event, fn) { | ||
if (window.removeEventListener) { | ||
el.removeEventListener(event, fn, !IE11OrLess && captureMode); | ||
} else if (window.detachEvent) { | ||
el.detachEvent('on' + event, fn); | ||
} | ||
} | ||
/** | ||
* get element's offetTop | ||
* @param {HTMLElement} el | ||
*/ | ||
/** | ||
* get specified element's index in group | ||
* @param {HTMLElement} group | ||
* @param {HTMLElement} el | ||
* @returns {Number} index | ||
*/ | ||
index: function index(group, el) { | ||
if (!el || !el.parentNode) return -1; | ||
function getOffset(el) { | ||
var result = { | ||
top: 0, | ||
left: 0, | ||
height: 0, | ||
width: 0 | ||
}; | ||
result.height = el.offsetHeight; | ||
result.width = el.offsetWidth; | ||
result.top = el.offsetTop; | ||
result.left = el.offsetLeft; | ||
var parent = el.offsetParent; | ||
var children = _toConsumableArray(Array.from(group.children)); | ||
while (parent !== null) { | ||
result.top += parent.offsetTop; | ||
result.left += parent.offsetLeft; | ||
parent = parent.offsetParent; | ||
} | ||
return children.indexOf(el); | ||
}, | ||
return result; | ||
} | ||
/** | ||
* get scroll element | ||
* @param {HTMLElement} el | ||
* @param {Boolean} includeSelf whether to include the passed element | ||
* @returns {HTMLElement} scroll element | ||
*/ | ||
/** | ||
* Returns the "bounding client rect" of given element | ||
* @param {HTMLElement} el The element whose boundingClientRect is wanted | ||
*/ | ||
getRect: function getRect(el) { | ||
if (!el.getBoundingClientRect && el !== window) return; | ||
var rect = { | ||
top: 0, | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
height: 0, | ||
width: 0 | ||
}; | ||
var elRect; | ||
function getParentAutoScrollElement(el, includeSelf) { | ||
// skip to window | ||
if (!el || !el.getBoundingClientRect) return getWindowScrollingElement(); | ||
var elem = el; | ||
var gotSelf = false; | ||
if (el !== window && el.parentNode && el !== this.getWindowScrollingElement()) { | ||
elRect = el.getBoundingClientRect(); | ||
rect.top = elRect.top; | ||
rect.left = elRect.left; | ||
rect.bottom = elRect.bottom; | ||
rect.right = elRect.right; | ||
rect.height = elRect.height; | ||
rect.width = elRect.width; | ||
} else { | ||
rect.top = 0; | ||
rect.left = 0; | ||
rect.bottom = window.innerHeight; | ||
rect.right = window.innerWidth; | ||
rect.height = window.innerHeight; | ||
rect.width = window.innerWidth; | ||
do { | ||
// we don't need to get elem css if it isn't even overflowing in the first place (performance) | ||
if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) { | ||
var elemCSS = css(elem); | ||
if (elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') || elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')) { | ||
if (!elem.getBoundingClientRect || elem === document.body) return getWindowScrollingElement(); | ||
if (gotSelf || includeSelf) return elem; | ||
gotSelf = true; | ||
} | ||
} | ||
} while (elem = elem.parentNode); | ||
return rect; | ||
}, | ||
return getWindowScrollingElement(); | ||
} | ||
function getWindowScrollingElement() { | ||
var scrollingElement = document.scrollingElement; | ||
/** | ||
* get target Element in group | ||
* @param {HTMLElement} group | ||
* @param {HTMLElement} el | ||
*/ | ||
getElement: function getElement(group, el) { | ||
var result = { | ||
index: -1, | ||
el: null, | ||
rect: {} | ||
}; | ||
if (scrollingElement) { | ||
return scrollingElement; | ||
} else { | ||
return document.documentElement; | ||
} | ||
} | ||
/** | ||
* Returns the "bounding client rect" of given element | ||
* @param {HTMLElement} el The element whose boundingClientRect is wanted | ||
*/ | ||
var children = _toConsumableArray(Array.from(group.children)); // 如果能直接在子元素中找到,返回对应的index | ||
function getRect(el) { | ||
if (!el.getBoundingClientRect && el !== window) return; | ||
var rect = { | ||
top: 0, | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
height: 0, | ||
width: 0 | ||
}; | ||
var elRect; | ||
if (el !== window && el.parentNode && el !== getWindowScrollingElement()) { | ||
elRect = el.getBoundingClientRect(); | ||
rect.top = elRect.top; | ||
rect.left = elRect.left; | ||
rect.bottom = elRect.bottom; | ||
rect.right = elRect.right; | ||
rect.height = elRect.height; | ||
rect.width = elRect.width; | ||
} else { | ||
rect.top = 0; | ||
rect.left = 0; | ||
rect.bottom = window.innerHeight; | ||
rect.right = window.innerWidth; | ||
rect.height = window.innerHeight; | ||
rect.width = window.innerWidth; | ||
} | ||
var index = children.indexOf(el); | ||
if (index > -1) Object.assign(result, { | ||
index: index, | ||
el: children[index], | ||
rect: this.getRect(children[index]) | ||
}); // children 中无法直接找到对应的dom时,需要向下寻找 | ||
return rect; | ||
} | ||
/** | ||
* get target Element in group | ||
* @param {HTMLElement} group | ||
* @param {HTMLElement} el | ||
*/ | ||
for (var i = 0; i < children.length; i++) { | ||
if (this.isChildOf(el, children[i])) Object.assign(result, { | ||
function getElement(group, el) { | ||
var result = { | ||
index: -1, | ||
el: null, | ||
rect: {}, | ||
offset: {} | ||
}; | ||
var children = _toConsumableArray(Array.from(group.children)); // 如果能直接在子元素中找到,返回对应的index | ||
var index = children.indexOf(el); | ||
if (index > -1) Object.assign(result, { | ||
index: index, | ||
el: children[index], | ||
rect: getRect(children[index]), | ||
offset: getOffset(children[index]) | ||
}); // children 中无法直接找到对应的dom时,需要向下寻找 | ||
for (var i = 0; i < children.length; i++) { | ||
if (isChildOf(el, children[i])) { | ||
Object.assign(result, { | ||
index: i, | ||
el: children[i], | ||
rect: this.getRect(children[i]) | ||
rect: getRect(children[i]), | ||
offset: getOffset(children[i]) | ||
}); | ||
break; | ||
} | ||
} | ||
return result; | ||
}, | ||
return result; | ||
} | ||
/** | ||
* Check if child element is contained in parent element | ||
* @param {HTMLElement} child | ||
* @param {HTMLElement} parent | ||
* @returns {Boolean} true | false | ||
*/ | ||
/** | ||
* Check if child element is contained in parent element | ||
* @param {HTMLElement} child | ||
* @param {HTMLElement} parent | ||
* @returns {Boolean} true | false | ||
*/ | ||
isChildOf: function isChildOf(child, parent) { | ||
var parentNode; | ||
function isChildOf(child, parent) { | ||
var parentNode; | ||
if (child && parent) { | ||
parentNode = child.parentNode; | ||
if (child && parent) { | ||
parentNode = child.parentNode; | ||
while (parentNode) { | ||
if (parent === parentNode) return true; | ||
parentNode = parentNode.parentNode; | ||
} | ||
while (parentNode) { | ||
if (parent === parentNode) return true; | ||
parentNode = parentNode.parentNode; | ||
} | ||
} | ||
return false; | ||
}, | ||
return false; | ||
} | ||
/** | ||
* add or remove element's class | ||
* @param {HTMLElement} el element | ||
* @param {String} name class name | ||
* @param {Boolean} state true: add, false: remove | ||
*/ | ||
/** | ||
* add or remove element's class | ||
* @param {HTMLElement} el element | ||
* @param {String} name class name | ||
* @param {Boolean} state true: add, false: remove | ||
*/ | ||
toggleClass: function toggleClass(el, name, state) { | ||
if (el && name) { | ||
if (el.classList) { | ||
el.classList[state ? 'add' : 'remove'](name); | ||
} else { | ||
var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' '); | ||
el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' '); | ||
} | ||
function toggleClass(el, name, state) { | ||
if (el && name) { | ||
if (el.classList) { | ||
el.classList[state ? 'add' : 'remove'](name); | ||
} else { | ||
var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' '); | ||
el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' '); | ||
} | ||
}, | ||
} | ||
} | ||
/** | ||
* Check if a DOM element matches a given selector | ||
* @param {HTMLElement} el | ||
* @param {String} selector | ||
* @returns | ||
*/ | ||
/** | ||
* Check if a DOM element matches a given selector | ||
* @param {HTMLElement} el | ||
* @param {String} selector | ||
* @returns | ||
*/ | ||
matches: function matches(el, selector) { | ||
if (!selector) return; | ||
selector[0] === '>' && (selector = selector.substring(1)); | ||
function matches(el, selector) { | ||
if (!selector) return; | ||
selector[0] === '>' && (selector = selector.substring(1)); | ||
if (el) { | ||
try { | ||
if (el.matches) { | ||
return el.matches(selector); | ||
} else if (el.msMatchesSelector) { | ||
return el.msMatchesSelector(selector); | ||
} else if (el.webkitMatchesSelector) { | ||
return el.webkitMatchesSelector(selector); | ||
} | ||
} catch (error) { | ||
return false; | ||
if (el) { | ||
try { | ||
if (el.matches) { | ||
return el.matches(selector); | ||
} else if (el.msMatchesSelector) { | ||
return el.msMatchesSelector(selector); | ||
} else if (el.webkitMatchesSelector) { | ||
return el.webkitMatchesSelector(selector); | ||
} | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
return false; | ||
}, | ||
css: function css(el, prop, val) { | ||
var style = el && el.style; | ||
return false; | ||
} | ||
function css(el, prop, val) { | ||
var style = el && el.style; | ||
if (style) { | ||
if (val === void 0) { | ||
if (document.defaultView && document.defaultView.getComputedStyle) { | ||
val = document.defaultView.getComputedStyle(el, ''); | ||
} else if (el.currentStyle) { | ||
val = el.currentStyle; | ||
} | ||
return prop === void 0 ? val : val[prop]; | ||
} else { | ||
if (!(prop in style) && prop.indexOf('webkit') === -1) { | ||
prop = '-webkit-' + prop; | ||
} | ||
style[prop] = val + (typeof val === 'string' ? '' : 'px'); | ||
if (style) { | ||
if (val === void 0) { | ||
if (document.defaultView && document.defaultView.getComputedStyle) { | ||
val = document.defaultView.getComputedStyle(el, ''); | ||
} else if (el.currentStyle) { | ||
val = el.currentStyle; | ||
} | ||
} | ||
}, | ||
debounce: function debounce(fn, delay) { | ||
return function () { | ||
var _this = this; | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
return prop === void 0 ? val : val[prop]; | ||
} else { | ||
if (!(prop in style) && prop.indexOf('webkit') === -1) { | ||
prop = '-webkit-' + prop; | ||
} | ||
clearTimeout(fn.id); | ||
fn.id = setTimeout(function () { | ||
fn.call.apply(fn, [_this].concat(args)); | ||
}, delay); | ||
}; | ||
}, | ||
_nextTick: function _nextTick(fn) { | ||
return setTimeout(fn, 0); | ||
style[prop] = val + (typeof val === 'string' ? '' : 'px'); | ||
} | ||
} | ||
}; | ||
} | ||
function debounce(fn, delay) { | ||
return function () { | ||
var _this = this; | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
clearTimeout(fn.id); | ||
fn.id = setTimeout(function () { | ||
fn.call.apply(fn, [_this].concat(args)); | ||
}, delay); | ||
}; | ||
} | ||
function _nextTick(fn) { | ||
return setTimeout(fn, 0); | ||
} | ||
function Animation() { | ||
@@ -336,3 +381,3 @@ var animationState = []; | ||
target: child, | ||
rect: utils.getRect(child) | ||
rect: getRect(child) | ||
}); | ||
@@ -353,15 +398,15 @@ }); | ||
var animation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 150; | ||
var curRect = utils.getRect(el); | ||
var curRect = getRect(el); | ||
var left = preRect.left - curRect.left; | ||
var top = preRect.top - curRect.top; | ||
utils.css(el, 'transition', 'none'); | ||
utils.css(el, 'transform', "translate3d(".concat(left, "px, ").concat(top, "px, 0)")); | ||
css(el, 'transition', 'none'); | ||
css(el, 'transform', "translate3d(".concat(left, "px, ").concat(top, "px, 0)")); | ||
el.offsetLeft; // 触发重绘 | ||
utils.css(el, 'transition', "all ".concat(animation, "ms")); | ||
utils.css(el, 'transform', 'translate3d(0px, 0px, 0px)'); | ||
css(el, 'transition', "all ".concat(animation, "ms")); | ||
css(el, 'transform', 'translate3d(0px, 0px, 0px)'); | ||
clearTimeout(el.animated); | ||
el.animated = setTimeout(function () { | ||
utils.css(el, 'transition', ''); | ||
utils.css(el, 'transform', ''); | ||
css(el, 'transition', ''); | ||
css(el, 'transform', ''); | ||
el.animated = null; | ||
@@ -389,17 +434,19 @@ }, animation); | ||
var Diff = /*#__PURE__*/function () { | ||
function Diff() { | ||
_classCallCheck(this, Diff); | ||
var Differ = /*#__PURE__*/function () { | ||
function Differ() { | ||
_classCallCheck(this, Differ); | ||
this.old = { | ||
this._old_ = { | ||
node: null, | ||
rect: {} | ||
rect: {}, | ||
offset: {} | ||
}; | ||
this["new"] = { | ||
this._new_ = { | ||
node: null, | ||
rect: {} | ||
rect: {}, | ||
offset: {} | ||
}; | ||
} | ||
_createClass(Diff, [{ | ||
_createClass(Differ, [{ | ||
key: "get", | ||
@@ -417,9 +464,11 @@ value: function get(key) { | ||
value: function destroy() { | ||
this.old = { | ||
this._old_ = { | ||
node: null, | ||
rect: {} | ||
rect: {}, | ||
offset: {} | ||
}; | ||
this["new"] = { | ||
this._new_ = { | ||
node: null, | ||
rect: {} | ||
rect: {}, | ||
offset: {} | ||
}; | ||
@@ -429,3 +478,3 @@ } | ||
return Diff; | ||
return Differ; | ||
}(); | ||
@@ -452,3 +501,2 @@ /** | ||
this.$el = el; | ||
this.rect = rect; | ||
var _this$options = this.options, | ||
@@ -473,3 +521,3 @@ ghostClass = _this$options.ghostClass, | ||
for (var key in ghostStyle) { | ||
utils.css(this.$el, key, ghostStyle[key]); | ||
css(this.$el, key, ghostStyle[key]); | ||
} | ||
@@ -489,2 +537,7 @@ } | ||
}, { | ||
key: "rect", | ||
value: function rect() { | ||
return this.$el.getBoundingClientRect(); | ||
} | ||
}, { | ||
key: "move", | ||
@@ -519,2 +572,4 @@ value: function move() { | ||
this.options = options = Object.assign({}, options); | ||
this.scrollEl = getParentAutoScrollElement(this.$el, true); // 获取页面滚动元素 | ||
this.dragEl = null; // 拖拽元素 | ||
@@ -524,3 +579,3 @@ | ||
this.diff = null; // 记录拖拽前后差异 | ||
this.differ = null; // 记录拖拽前后差异 | ||
@@ -534,3 +589,3 @@ this.ghost = null; // 拖拽时蒙版元素 | ||
utils.debounce(this.init(), 50); // 避免重复执行多次 | ||
debounce(this.init(), 50); // 避免重复执行多次 | ||
} | ||
@@ -568,3 +623,3 @@ | ||
this.diff = new Diff(); | ||
this.differ = new Differ(); | ||
this.ghost = new Ghost(this.options); | ||
@@ -576,41 +631,4 @@ Object.assign(this, Animation()); | ||
this._handleDestroy(); | ||
} | ||
}, { | ||
key: "_handleDestroy", | ||
value: function _handleDestroy() { | ||
var _this = this; | ||
} // -------------------------------- drag and drop ---------------------------------- | ||
var observer = null; | ||
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; | ||
if (MutationObserver) { | ||
observer = new MutationObserver(function () { | ||
if (!_this.$el) { | ||
observer.disconnect(); | ||
observer = null; | ||
_this._unbindEventListener(); | ||
_this._resetState(); | ||
} | ||
}); | ||
observer.observe(this.$el.parentNode, { | ||
childList: true, | ||
// 观察目标子节点的变化,是否有添加或者删除 | ||
attributes: false, | ||
// 观察属性变动 | ||
subtree: false // 观察后代节点,默认为 false | ||
}); | ||
} | ||
window.onbeforeunload = function () { | ||
if (observer) observer.disconnect(); | ||
observer = null; | ||
_this._unbindEventListener(); | ||
_this._resetState(); | ||
}; | ||
} | ||
}, { | ||
@@ -623,8 +641,8 @@ key: "_onStart", | ||
var touch = evt.touches && evt.touches[0] || evt.pointerType && evt.pointerType === 'touch' && evt; | ||
var target = (touch || evt).target; | ||
var e = touch || evt; | ||
if (typeof draggable === 'function') { | ||
if (!draggable(touch || evt)) return true; | ||
if (!draggable(e)) return true; | ||
} else if (typeof draggable === 'string') { | ||
if (!utils.matches(target, draggable)) return true; | ||
if (!matches(e.target, draggable)) return true; | ||
} else if (draggable !== undefined) { | ||
@@ -636,3 +654,3 @@ throw new Error("draggable expected \"function\" or \"string\" but received \"".concat(_typeof(draggable), "\"")); | ||
if (target === this.$el) return true; | ||
if (e.target === this.$el) return true; | ||
@@ -642,3 +660,3 @@ try { | ||
// Timeout neccessary for IE9 | ||
utils._nextTick(function () { | ||
_nextTick(function () { | ||
document.selection.empty(); | ||
@@ -651,3 +669,3 @@ }); | ||
var element = dragging && typeof dragging === 'function' ? dragging(touch || evt) : utils.getElement(this.$el, target).el; // 不存在拖拽元素时不允许拖拽 | ||
var element = typeof dragging === 'function' ? dragging(e) : getElement(this.$el, e.target).el; // 不存在拖拽元素时不允许拖拽 | ||
@@ -663,6 +681,7 @@ if (!element) return true; | ||
var _utils$getElement = utils.getElement(this.$el, this.dragEl), | ||
index = _utils$getElement.index, | ||
el = _utils$getElement.el, | ||
rect = _utils$getElement.rect; | ||
var _getElement = getElement(this.$el, this.dragEl), | ||
index = _getElement.index, | ||
el = _getElement.el, | ||
rect = _getElement.rect, | ||
offset = _getElement.offset; | ||
@@ -675,6 +694,8 @@ if (!el || index < 0) return true; // 将拖拽元素克隆一份作为蒙版 | ||
this.ghost.set('y', rect.top); | ||
this.diff.old.rect = rect; | ||
this.differ._old_.rect = rect; | ||
this.differ._old_.offset = offset; | ||
this.differ._old_.node = this.dragEl; | ||
this.calcXY = { | ||
x: (touch || evt).clientX, | ||
y: (touch || evt).clientY | ||
x: e.clientX, | ||
y: e.clientY | ||
}; | ||
@@ -696,3 +717,3 @@ | ||
var chosenClass = this.options.chosenClass; | ||
utils.toggleClass(this.dragEl, chosenClass, true); | ||
toggleClass(this.dragEl, chosenClass, true); | ||
this.ghost.move(); | ||
@@ -711,9 +732,8 @@ if (!window.sortableDndOnDown) return; | ||
this._checkRange(e); | ||
var _getElement2 = getElement(this.$el, target), | ||
index = _getElement2.index, | ||
el = _getElement2.el, | ||
rect = _getElement2.rect, | ||
offset = _getElement2.offset; | ||
var _utils$getElement2 = utils.getElement(this.$el, target), | ||
index = _utils$getElement2.index, | ||
el = _utils$getElement2.el, | ||
rect = _utils$getElement2.rect; | ||
var left = rect.left, | ||
@@ -723,4 +743,20 @@ right = rect.right, | ||
bottom = rect.bottom; | ||
if (!el || index < 0 || top < 0) return; | ||
if (!el || index < 0 || top < 0) return; // 判断边界值 | ||
var _rect = getRect(this.$el); | ||
this._checkRange(e, _rect); | ||
var _this$scrollEl = this.scrollEl, | ||
scrollTop = _this$scrollEl.scrollTop, | ||
scrollLeft = _this$scrollEl.scrollLeft; // 如果目标元素超出当前可视区,不允许拖动 | ||
if (this.scrollEl !== this.$el && (_rect.left < 0 || _rect.top < 0)) { | ||
if (rect.top < _rect.top + scrollTop && _rect.top < 0) return; | ||
if (rect.left < _rect.left + scrollLeft && _rect.left < 0) return; | ||
} else { | ||
if (rect.top < _rect.top) return; | ||
if (rect.left < _rect.left) return; | ||
} | ||
if (clientX > left && clientX < right && clientY > top && clientY < bottom) { | ||
@@ -733,3 +769,7 @@ this.dropEl = el; // 拖拽前后元素不一致时交换 | ||
if (utils.index(this.$el, this.dragEl) < index) { | ||
var _offset = getOffset(this.dragEl); // 获取拖拽元素的 offset 值 | ||
// 优先比较 top 值,top 值相同再比较 left | ||
if (_offset.top < offset.top || _offset.left < offset.left) { | ||
this.$el.insertBefore(this.dragEl, this.dropEl.nextElementSibling); | ||
@@ -741,7 +781,5 @@ } else { | ||
this.animateRange(); | ||
this.diff.old.node = this.dragEl; | ||
this.diff["new"].node = this.dropEl; | ||
this.differ._new_.node = this.dropEl; | ||
this.differ._new_.rect = getRect(this.dropEl); | ||
} | ||
this.diff["new"].rect = utils.getRect(this.dropEl); | ||
} | ||
@@ -760,8 +798,18 @@ } | ||
chosenClass = _this$options3.chosenClass; | ||
utils.toggleClass(this.dragEl, chosenClass, false); | ||
toggleClass(this.dragEl, chosenClass, false); | ||
if (window.sortableDndOnDown && window.sortableDndOnMove) { | ||
// 拖拽完成触发回调函数 | ||
// 重新获取一次拖拽元素的 offset 值作为拖拽完成后的 offset 值 | ||
this.differ._new_.offset = getOffset(this.dragEl); // 拖拽完成触发回调函数 | ||
var _this$differ = this.differ, | ||
_old_ = _this$differ._old_, | ||
_new_ = _this$differ._new_; // 通过 offset 比较是否进行了元素交换 | ||
var changed = _old_.offset.top !== _new_.offset.top || _old_.offset.left !== _new_.offset.left; // 如果拖拽前后没有发生交换,重新赋值一次 | ||
if (!changed) this.differ._new_.node = this.differ._old_.node; | ||
if (typeof dragEnd === 'function') { | ||
dragEnd(this.diff.old, this.diff["new"]); | ||
dragEnd(_old_, _new_, changed); | ||
} else { | ||
@@ -772,3 +820,3 @@ throw new Error("Sortable-dnd Error: dragEnd expected \"function\" but received \"".concat(_typeof(dragEnd), "\"")); | ||
this.diff.destroy(); | ||
this.differ.destroy(); | ||
this.ghost.destroy(); | ||
@@ -780,8 +828,7 @@ | ||
key: "_checkRange", | ||
value: function _checkRange(e) { | ||
var _utils$getRect = utils.getRect(this.$el), | ||
top = _utils$getRect.top, | ||
left = _utils$getRect.left, | ||
right = _utils$getRect.right, | ||
bottom = _utils$getRect.bottom; | ||
value: function _checkRange(e, groupRect) { | ||
var top = groupRect.top, | ||
left = groupRect.left, | ||
right = groupRect.right, | ||
bottom = groupRect.bottom; | ||
@@ -792,4 +839,45 @@ if (e.clientX < left || e.clientX > right || e.clientY < top || e.clientY > bottom) { | ||
} | ||
} | ||
} // -------------------------------- auto destroy ---------------------------------- | ||
}, { | ||
key: "_handleDestroy", | ||
value: function _handleDestroy() { | ||
var _this = this; | ||
var observer = null; | ||
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; | ||
if (MutationObserver) { | ||
var ownerDocument = this.options.ownerDocument; | ||
observer = new MutationObserver(function () { | ||
if (!ownerDocument.body.contains(_this.$el)) { | ||
observer.disconnect(); | ||
observer = null; | ||
_this._unbindEventListener(); | ||
_this._resetState(); | ||
} | ||
}); | ||
observer.observe(this.$el.parentNode, { | ||
childList: true, | ||
// 观察目标子节点的变化,是否有添加或者删除 | ||
attributes: false, | ||
// 观察属性变动 | ||
subtree: false // 观察后代节点,默认为 false | ||
}); | ||
} | ||
window.onbeforeunload = function () { | ||
if (observer) observer.disconnect(); | ||
observer = null; | ||
_this._unbindEventListener(); | ||
_this._resetState(); | ||
}; | ||
} // -------------------------------- reset state ---------------------------------- | ||
}, { | ||
key: "_resetState", | ||
@@ -800,3 +888,3 @@ value: function _resetState() { | ||
this.ghost.destroy(); | ||
this.diff.destroy(); | ||
this.differ.destroy(); | ||
this.calcXY = { | ||
@@ -816,3 +904,4 @@ x: 0, | ||
delete window.sortableDndOnMove; | ||
} | ||
} // -------------------------------- listener ---------------------------------- | ||
}, { | ||
@@ -827,6 +916,6 @@ key: "_bindEventListener", | ||
if (supportPointer) { | ||
utils.on(this.$el, 'pointerdown', this._onStart); | ||
on(this.$el, 'pointerdown', this._onStart); | ||
} else { | ||
utils.on(this.$el, 'mousedown', this._onStart); | ||
utils.on(this.$el, 'touchstart', this._onStart); | ||
on(this.$el, 'mousedown', this._onStart); | ||
on(this.$el, 'touchstart', this._onStart); | ||
} | ||
@@ -837,5 +926,5 @@ } | ||
value: function _unbindEventListener() { | ||
utils.off(this.$el, 'pointerdown', this._onStart); | ||
utils.off(this.$el, 'touchstart', this._onStart); | ||
utils.off(this.$el, 'mousedown', this._onStart); | ||
off(this.$el, 'pointerdown', this._onStart); | ||
off(this.$el, 'touchstart', this._onStart); | ||
off(this.$el, 'mousedown', this._onStart); | ||
} | ||
@@ -850,7 +939,7 @@ }, { | ||
if (supportPointer) { | ||
utils.on(ownerDocument, 'pointermove', this._onMove); | ||
on(ownerDocument, 'pointermove', this._onMove); | ||
} else if (touch) { | ||
utils.on(ownerDocument, 'touchmove', this._onMove); | ||
on(ownerDocument, 'touchmove', this._onMove); | ||
} else { | ||
utils.on(ownerDocument, 'mousemove', this._onMove); | ||
on(ownerDocument, 'mousemove', this._onMove); | ||
} | ||
@@ -862,6 +951,6 @@ } | ||
var ownerDocument = this.options.ownerDocument; | ||
utils.on(ownerDocument, 'pointerup', this._onDrop); | ||
utils.on(ownerDocument, 'touchend', this._onDrop); | ||
utils.on(ownerDocument, 'touchcancel', this._onDrop); | ||
utils.on(ownerDocument, 'mouseup', this._onDrop); | ||
on(ownerDocument, 'pointerup', this._onDrop); | ||
on(ownerDocument, 'touchend', this._onDrop); | ||
on(ownerDocument, 'touchcancel', this._onDrop); | ||
on(ownerDocument, 'mouseup', this._onDrop); | ||
} | ||
@@ -872,5 +961,5 @@ }, { | ||
var ownerDocument = this.options.ownerDocument; | ||
utils.off(ownerDocument, 'pointermove', this._onMove); | ||
utils.off(ownerDocument, 'touchmove', this._onMove); | ||
utils.off(ownerDocument, 'mousemove', this._onMove); | ||
off(ownerDocument, 'pointermove', this._onMove); | ||
off(ownerDocument, 'touchmove', this._onMove); | ||
off(ownerDocument, 'mousemove', this._onMove); | ||
} | ||
@@ -881,6 +970,6 @@ }, { | ||
var ownerDocument = this.options.ownerDocument; | ||
utils.off(ownerDocument, 'mouseup', this._onDrop); | ||
utils.off(ownerDocument, 'touchend', this._onDrop); | ||
utils.off(ownerDocument, 'touchcancel', this._onDrop); | ||
utils.off(ownerDocument, 'pointerup', this._onDrop); | ||
off(ownerDocument, 'mouseup', this._onDrop); | ||
off(ownerDocument, 'touchend', this._onDrop); | ||
off(ownerDocument, 'touchcancel', this._onDrop); | ||
off(ownerDocument, 'pointerup', this._onDrop); | ||
} | ||
@@ -887,0 +976,0 @@ }]); |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Sortable=e()}(this,function(){"use strict";function s(t){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}function r(t,e,n){e&&i(t.prototype,e),n&&i(t,n),Object.defineProperty(t,"prototype",{writable:!1})}function l(t){return function(t){if(Array.isArray(t))return a(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return a(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Map"===(n="Object"===n&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?a(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function t(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var h=t(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),c=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),u={capture:!1,passive:!1},d=/\s+/g,f={on:function(t,e,n){window.addEventListener?t.addEventListener(e,n,!h&&u):window.attachEvent&&t.addEventListener("on"+e,n)},off:function(t,e,n){window.removeEventListener?t.removeEventListener(e,n,!h&&u):window.detachEvent&&t.detachEvent("on"+e,n)},getWindowScrollingElement:function(){var t=document.scrollingElement;return t||document.documentElement},index:function(t,e){return e&&e.parentNode?l(Array.from(t.children)).indexOf(e):-1},getRect:function(t){var e;if(t.getBoundingClientRect||t===window)return e={top:0,left:0,bottom:0,right:0,height:0,width:0},t!==window&&t.parentNode&&t!==this.getWindowScrollingElement()?(t=t.getBoundingClientRect(),e.top=t.top,e.left=t.left,e.bottom=t.bottom,e.right=t.right,e.height=t.height,e.width=t.width):(e.top=0,e.left=0,e.bottom=window.innerHeight,e.right=window.innerWidth,e.height=window.innerHeight,e.width=window.innerWidth),e},getElement:function(t,e){var n={index:-1,el:null,rect:{}},o=l(Array.from(t.children)),t=o.indexOf(e);-1<t&&Object.assign(n,{index:t,el:o[t],rect:this.getRect(o[t])});for(var i=0;i<o.length;i++)this.isChildOf(e,o[i])&&Object.assign(n,{index:i,el:o[i],rect:this.getRect(o[i])});return n},isChildOf:function(t,e){var n;if(t&&e)for(n=t.parentNode;n;){if(e===n)return!0;n=n.parentNode}return!1},toggleClass:function(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(d," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(d," ")))},matches:function(t,e){if(e){if(">"===e[0]&&(e=e.substring(1)),t)try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return!1}return!1}},css:function(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=e in o||-1!==e.indexOf("webkit")?e:"-webkit-"+e]=n+("string"==typeof n?"":"px")}},debounce:function(i,r){return function(){for(var t=this,e=arguments.length,n=new Array(e),o=0;o<e;o++)n[o]=arguments[o];clearTimeout(i.id),i.id=setTimeout(function(){i.call.apply(i,[t].concat(n))},r)}},_nextTick:function(t){return setTimeout(t,0)}};function p(){var o=[];return{captureAnimationState:function(){var t=l(Array.from(this.$el.children)),e=function(t,e,n){e=t.indexOf(e),t=t.indexOf(n);return e<t?{start:e,end:t}:{start:t,end:e}}(t,this.dragEl,this.dropEl),n=e.start,e=e.end;o.length=0,t.slice(n,e+1).forEach(function(t){o.push({target:t,rect:f.getRect(t)})})},animateRange:function(){var n=this;o.forEach(function(t){var e=t.target,t=t.rect;n.animate(e,t,n.animation)})},animate:function(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:150,o=f.getRect(t),i=e.left-o.left,e=e.top-o.top;f.css(t,"transition","none"),f.css(t,"transform","translate3d(".concat(i,"px, ").concat(e,"px, 0)")),t.offsetLeft,f.css(t,"transition","all ".concat(n,"ms")),f.css(t,"transform","translate3d(0px, 0px, 0px)"),clearTimeout(t.animated),t.animated=setTimeout(function(){f.css(t,"transition",""),f.css(t,"transform",""),t.animated=null},n)}}}var v=function(){function t(){o(this,t),this.old={node:null,rect:{}},this.new={node:null,rect:{}}}return r(t,[{key:"get",value:function(t){return this[t]}},{key:"set",value:function(t,e){this[t]=e}},{key:"destroy",value:function(){this.old={node:null,rect:{}},this.new={node:null,rect:{}}}}]),t}(),g=function(){function e(t){o(this,e),this.options=t,this.x=0,this.y=0,this.exist=!1}return r(e,[{key:"init",value:function(t,e){if(t){this.$el=t,this.rect=e;var n,t=this.options,o=t.ghostClass,t=t.ghostStyle,i=void 0===t?{}:t,t=e.width,e=e.height;for(n in this.$el.class=o,this.$el.style.width=t+"px",this.$el.style.height=e+"px",this.$el.style.transform="",this.$el.style.transition="",this.$el.style.position="fixed",this.$el.style.left=0,this.$el.style.top=0,this.$el.style.zIndex=1e5,this.$el.style.opacity=.8,this.$el.style.pointerEvents="none",i)f.css(this.$el,n,i[n])}}},{key:"get",value:function(t){return this[t]}},{key:"set",value:function(t,e){this[t]=e,this[t]=e}},{key:"move",value:function(){this.exist||(document.body.appendChild(this.$el),this.exist=!0),this.$el.style.transform="translate3d(".concat(this.x,"px, ").concat(this.y,"px, 0)")}},{key:"destroy",value:function(){this.$el&&this.$el.remove(),this.exist=!1}}]),e}();return function(){function n(t,e){if(o(this,n),!t)throw new Error("container element is required");this.$el=t,this.options=e=Object.assign({},e),this.dragEl=null,this.dropEl=null,this.diff=null,this.ghost=null,this.calcXY={x:0,y:0},f.debounce(this.init(),50)}return r(n,[{key:"destroy",value:function(){this._unbindEventListener(),this._resetState()}},{key:"init",value:function(){var t,e={animation:150,ghostClass:"",ghostStyle:{},chosenClass:"",draggable:void 0,dragging:null,dragEnd:null,supportPointer:"PointerEvent"in window&&!c,ownerDocument:this.$el.ownerDocument};for(t in e)t in this.options||(this.options[t]=e[t]);this.diff=new v,this.ghost=new g(this.options),Object.assign(this,p()),this._bindEventListener(),this._handleDestroy()}},{key:"_handleDestroy",value:function(){var t=this,e=null,n=window.MutationObserver||window.WebKitMutationObserver;n&&(e=new n(function(){t.$el||(e.disconnect(),e=null,t._unbindEventListener(),t._resetState())})).observe(this.$el.parentNode,{childList:!0,attributes:!1,subtree:!1}),window.onbeforeunload=function(){e&&e.disconnect(),e=null,t._unbindEventListener(),t._resetState()}}},{key:"_onStart",value:function(t){var e=this.options,n=e.dragging,e=e.draggable,o=t.touches&&t.touches[0]||t.pointerType&&"touch"===t.pointerType&&t,i=(o||t).target;if("function"==typeof e){if(!e(o||t))return!0}else if("string"==typeof e){if(!f.matches(i,e))return!0}else if(void 0!==e)throw new Error('draggable expected "function" or "string" but received "'.concat(s(e),'"'));if(!/mousedown|pointerdown/.test(t.type)||0===t.button){if(i===this.$el)return!0;try{document.selection?f._nextTick(function(){document.selection.empty()}):window.getSelection().removeAllRanges();var r=n&&"function"==typeof n?n(o||t):f.getElement(this.$el,i).el;if(!r)return!0;if(r.animated)return;this.dragEl=r}catch(t){throw new Error(t)}window.sortableDndOnDown=!0;e=f.getElement(this.$el,this.dragEl),n=e.index,i=e.el,r=e.rect;if(!i||n<0)return!0;e=this.dragEl.cloneNode(!0);this.ghost.init(e,r),this.ghost.set("x",r.left),this.ghost.set("y",r.top),this.diff.old.rect=r,this.calcXY={x:(o||t).clientX,y:(o||t).clientY},this._onMoveEvents(o),this._onUpEvents(o)}}},{key:"_onMove",value:function(t){t.preventDefault();var e=t.touches&&t.touches[0],t=e||t,n=t.clientX,o=t.clientY,e=e?document.elementFromPoint(n,o):t.target,i=this.options.chosenClass;if(f.toggleClass(this.dragEl,i,!0),this.ghost.move(),window.sortableDndOnDown&&!(n<0||o<0)){document.body.style.cursor="grabbing",window.sortableDndOnMove=!0,this.ghost.set("x",this.ghost.x+n-this.calcXY.x),this.ghost.set("y",this.ghost.y+o-this.calcXY.y),this.calcXY={x:n,y:o},this.ghost.move(),this._checkRange(t);var i=f.getElement(this.$el,e),t=i.index,e=i.el,i=i.rect,r=i.left,s=i.right,l=i.top,i=i.bottom;if(!(!e||t<0||l<0)&&r<n&&n<s&&l<o&&o<i){if(this.dropEl=e,this.dropEl!==this.dragEl){if(this.dropEl.animated)return;this.captureAnimationState(),f.index(this.$el,this.dragEl)<t?this.$el.insertBefore(this.dragEl,this.dropEl.nextElementSibling):this.$el.insertBefore(this.dragEl,this.dropEl),this.animateRange(),this.diff.old.node=this.dragEl,this.diff.new.node=this.dropEl}this.diff.new.rect=f.getRect(this.dropEl)}}}},{key:"_onDrop",value:function(){this._offMoveEvents(),this._offUpEvents(),document.body.style.cursor="";var t=this.options,e=t.dragEnd,t=t.chosenClass;if(f.toggleClass(this.dragEl,t,!1),window.sortableDndOnDown&&window.sortableDndOnMove){if("function"!=typeof e)throw new Error('Sortable-dnd Error: dragEnd expected "function" but received "'.concat(s(e),'"'));e(this.diff.old,this.diff.new)}this.diff.destroy(),this.ghost.destroy(),this._removeWindowState()}},{key:"_checkRange",value:function(t){var e=f.getRect(this.$el),n=e.top,o=e.left,i=e.right,e=e.bottom;(t.clientX<o||t.clientX>i||t.clientY<n||t.clientY>e)&&(document.body.style.cursor="not-allowed")}},{key:"_resetState",value:function(){this.dragEl=null,this.dropEl=null,this.ghost.destroy(),this.diff.destroy(),this.calcXY={x:0,y:0},this._removeWindowState()}},{key:"_removeWindowState",value:function(){window.sortableDndOnDown=null,window.sortableDndOnMove=null,delete window.sortableDndOnDown,delete window.sortableDndOnMove}},{key:"_bindEventListener",value:function(){this._onStart=this._onStart.bind(this),this._onMove=this._onMove.bind(this),this._onDrop=this._onDrop.bind(this),this.options.supportPointer?f.on(this.$el,"pointerdown",this._onStart):(f.on(this.$el,"mousedown",this._onStart),f.on(this.$el,"touchstart",this._onStart))}},{key:"_unbindEventListener",value:function(){f.off(this.$el,"pointerdown",this._onStart),f.off(this.$el,"touchstart",this._onStart),f.off(this.$el,"mousedown",this._onStart)}},{key:"_onMoveEvents",value:function(t){var e=this.options,n=e.supportPointer,e=e.ownerDocument;n?f.on(e,"pointermove",this._onMove):t?f.on(e,"touchmove",this._onMove):f.on(e,"mousemove",this._onMove)}},{key:"_onUpEvents",value:function(){var t=this.options.ownerDocument;f.on(t,"pointerup",this._onDrop),f.on(t,"touchend",this._onDrop),f.on(t,"touchcancel",this._onDrop),f.on(t,"mouseup",this._onDrop)}},{key:"_offMoveEvents",value:function(){var t=this.options.ownerDocument;f.off(t,"pointermove",this._onMove),f.off(t,"touchmove",this._onMove),f.off(t,"mousemove",this._onMove)}},{key:"_offUpEvents",value:function(){var t=this.options.ownerDocument;f.off(t,"mouseup",this._onDrop),f.off(t,"touchend",this._onDrop),f.off(t,"touchcancel",this._onDrop),f.off(t,"pointerup",this._onDrop)}}]),n}()}); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Sortable=e()}(this,function(){"use strict";function s(t){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}function r(t,e,n){e&&i(t.prototype,e),n&&i(t,n),Object.defineProperty(t,"prototype",{writable:!1})}function l(t){return function(t){if(Array.isArray(t))return a(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return a(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Map"===(n="Object"===n&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?a(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function t(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var h=t(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),f=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),c={capture:!1,passive:!1},u=/\s+/g;function d(t,e,n){window.addEventListener?t.addEventListener(e,n,!h&&c):window.attachEvent&&t.addEventListener("on"+e,n)}function e(t,e,n){window.removeEventListener?t.removeEventListener(e,n,!h&&c):window.detachEvent&&t.detachEvent("on"+e,n)}function p(t){for(var e={top:0,left:0,height:0,width:0},n=(e.height=t.offsetHeight,e.width=t.offsetWidth,e.top=t.offsetTop,e.left=t.offsetLeft,t.offsetParent);null!==n;)e.top+=n.offsetTop,e.left+=n.offsetLeft,n=n.offsetParent;return e}function v(){var t=document.scrollingElement;return t||document.documentElement}function y(t){var e;if(t.getBoundingClientRect||t===window)return e={top:0,left:0,bottom:0,right:0,height:0,width:0},t!==window&&t.parentNode&&t!==v()?(t=t.getBoundingClientRect(),e.top=t.top,e.left=t.left,e.bottom=t.bottom,e.right=t.right,e.height=t.height,e.width=t.width):(e.top=0,e.left=0,e.bottom=window.innerHeight,e.right=window.innerWidth,e.height=window.innerHeight,e.width=window.innerWidth),e}function g(t,e){var n={index:-1,el:null,rect:{},offset:{}},o=l(Array.from(t.children)),t=o.indexOf(e);-1<t&&Object.assign(n,{index:t,el:o[t],rect:y(o[t]),offset:p(o[t])});for(var i=0;i<o.length;i++)if(function(t,e){var n;if(t&&e)for(n=t.parentNode;n;){if(e===n)return 1;n=n.parentNode}return}(e,o[i])){Object.assign(n,{index:i,el:o[i],rect:y(o[i]),offset:p(o[i])});break}return n}function w(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(u," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(u," ")))}function m(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=e in o||-1!==e.indexOf("webkit")?e:"-webkit-"+e]=n+("string"==typeof n?"":"px")}}function b(){var o=[];return{captureAnimationState:function(){var t=l(Array.from(this.$el.children)),e=function(t,e,n){e=t.indexOf(e),t=t.indexOf(n);return e<t?{start:e,end:t}:{start:t,end:e}}(t,this.dragEl,this.dropEl),n=e.start,e=e.end;o.length=0,t.slice(n,e+1).forEach(function(t){o.push({target:t,rect:y(t)})})},animateRange:function(){var n=this;o.forEach(function(t){var e=t.target,t=t.rect;n.animate(e,t,n.animation)})},animate:function(t,e){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:150,o=y(t),i=e.left-o.left,e=e.top-o.top;m(t,"transition","none"),m(t,"transform","translate3d(".concat(i,"px, ").concat(e,"px, 0)")),t.offsetLeft,m(t,"transition","all ".concat(n,"ms")),m(t,"transform","translate3d(0px, 0px, 0px)"),clearTimeout(t.animated),t.animated=setTimeout(function(){m(t,"transition",""),m(t,"transform",""),t.animated=null},n)}}}var _=function(){function t(){o(this,t),this._old_={node:null,rect:{},offset:{}},this._new_={node:null,rect:{},offset:{}}}return r(t,[{key:"get",value:function(t){return this[t]}},{key:"set",value:function(t,e){this[t]=e}},{key:"destroy",value:function(){this._old_={node:null,rect:{},offset:{}},this._new_={node:null,rect:{},offset:{}}}}]),t}(),E=function(){function e(t){o(this,e),this.options=t,this.x=0,this.y=0,this.exist=!1}return r(e,[{key:"init",value:function(t,e){if(t){this.$el=t;var n,t=this.options,o=t.ghostClass,t=t.ghostStyle,i=void 0===t?{}:t,t=e.width,e=e.height;for(n in this.$el.class=o,this.$el.style.width=t+"px",this.$el.style.height=e+"px",this.$el.style.transform="",this.$el.style.transition="",this.$el.style.position="fixed",this.$el.style.left=0,this.$el.style.top=0,this.$el.style.zIndex=1e5,this.$el.style.opacity=.8,this.$el.style.pointerEvents="none",i)m(this.$el,n,i[n])}}},{key:"get",value:function(t){return this[t]}},{key:"set",value:function(t,e){this[t]=e,this[t]=e}},{key:"rect",value:function(){return this.$el.getBoundingClientRect()}},{key:"move",value:function(){this.exist||(document.body.appendChild(this.$el),this.exist=!0),this.$el.style.transform="translate3d(".concat(this.x,"px, ").concat(this.y,"px, 0)")}},{key:"destroy",value:function(){this.$el&&this.$el.remove(),this.exist=!1}}]),e}();return function(){function n(t,e){if(o(this,n),!t)throw new Error("container element is required");this.$el=t,this.options=e=Object.assign({},e),this.scrollEl=function(t,e){if(!t||!t.getBoundingClientRect)return v();var n=t,o=!1;do{if(n.clientWidth<n.scrollWidth||n.clientHeight<n.scrollHeight){var i=m(n);if(n.clientWidth<n.scrollWidth&&("auto"==i.overflowX||"scroll"==i.overflowX)||n.clientHeight<n.scrollHeight&&("auto"==i.overflowY||"scroll"==i.overflowY)){if(!n.getBoundingClientRect||n===document.body)return v();if(o||e)return n;o=!0}}}while(n=n.parentNode);return v()}(this.$el,!0),this.dragEl=null,this.dropEl=null,this.differ=null,this.ghost=null,this.calcXY={x:0,y:0},this.init()}return r(n,[{key:"destroy",value:function(){this._unbindEventListener(),this._resetState()}},{key:"init",value:function(){var t,e={animation:150,ghostClass:"",ghostStyle:{},chosenClass:"",draggable:void 0,dragging:null,dragEnd:null,supportPointer:"PointerEvent"in window&&!f,ownerDocument:this.$el.ownerDocument};for(t in e)t in this.options||(this.options[t]=e[t]);this.differ=new _,this.ghost=new E(this.options),Object.assign(this,b()),this._bindEventListener(),this._handleDestroy()}},{key:"_onStart",value:function(t){var e=this.options,n=e.dragging,e=e.draggable,o=t.touches&&t.touches[0]||t.pointerType&&"touch"===t.pointerType&&t,i=o||t;if("function"==typeof e){if(!e(i))return!0}else if("string"==typeof e){if(!function(t,e){if(e&&(">"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}(i.target,e))return!0}else if(void 0!==e)throw new Error('draggable expected "function" or "string" but received "'.concat(s(e),'"'));if(!/mousedown|pointerdown/.test(t.type)||0===t.button){if(i.target===this.$el)return!0;try{document.selection?setTimeout(function(){document.selection.empty()},0):window.getSelection().removeAllRanges();var r="function"==typeof n?n(i):g(this.$el,i.target).el;if(!r)return!0;if(r.animated)return;this.dragEl=r}catch(t){throw new Error(t)}window.sortableDndOnDown=!0;e=g(this.$el,this.dragEl),t=e.index,n=e.el,r=e.rect,e=e.offset;if(!n||t<0)return!0;n=this.dragEl.cloneNode(!0);this.ghost.init(n,r),this.ghost.set("x",r.left),this.ghost.set("y",r.top),this.differ._old_.rect=r,this.differ._old_.offset=e,this.differ._old_.node=this.dragEl,this.calcXY={x:i.clientX,y:i.clientY},this._onMoveEvents(o),this._onUpEvents(o)}}},{key:"_onMove",value:function(t){t.preventDefault();var e=t.touches&&t.touches[0],t=e||t,n=t.clientX,o=t.clientY,e=e?document.elementFromPoint(n,o):t.target,i=this.options.chosenClass;if(w(this.dragEl,i,!0),this.ghost.move(),window.sortableDndOnDown&&!(n<0||o<0)){document.body.style.cursor="grabbing",window.sortableDndOnMove=!0,this.ghost.set("x",this.ghost.x+n-this.calcXY.x),this.ghost.set("y",this.ghost.y+o-this.calcXY.y),this.calcXY={x:n,y:o},this.ghost.move();var i=g(this.$el,e),e=i.index,r=i.el,s=i.rect,i=i.offset,l=s.left,a=s.right,h=s.top,f=s.bottom;if(!(!r||e<0||h<0)){var e=y(this.$el),t=(this._checkRange(t,e),this.scrollEl),c=t.scrollTop,t=t.scrollLeft;if(this.scrollEl!==this.$el&&(e.left<0||e.top<0)){if(s.top<e.top+c&&e.top<0)return;if(s.left<e.left+t&&e.left<0)return}else{if(s.top<e.top)return;if(s.left<e.left)return}l<n&&n<a&&h<o&&o<f&&(this.dropEl=r,this.dropEl===this.dragEl||this.dropEl.animated||(this.captureAnimationState(),(c=p(this.dragEl)).top<i.top||c.left<i.left?this.$el.insertBefore(this.dragEl,this.dropEl.nextElementSibling):this.$el.insertBefore(this.dragEl,this.dropEl),this.animateRange(),this.differ._new_.node=this.dropEl,this.differ._new_.rect=y(this.dropEl)))}}}},{key:"_onDrop",value:function(){this._offMoveEvents(),this._offUpEvents(),document.body.style.cursor="";var t=this.options,e=t.dragEnd,t=t.chosenClass;if(w(this.dragEl,t,!1),window.sortableDndOnDown&&window.sortableDndOnMove){this.differ._new_.offset=p(this.dragEl);var t=this.differ,n=t._old_,t=t._new_,o=n.offset.top!==t.offset.top||n.offset.left!==t.offset.left;if(o||(this.differ._new_.node=this.differ._old_.node),"function"!=typeof e)throw new Error('Sortable-dnd Error: dragEnd expected "function" but received "'.concat(s(e),'"'));e(n,t,o)}this.differ.destroy(),this.ghost.destroy(),this._removeWindowState()}},{key:"_checkRange",value:function(t,e){var n=e.top,o=e.left,i=e.right,e=e.bottom;(t.clientX<o||t.clientX>i||t.clientY<n||t.clientY>e)&&(document.body.style.cursor="not-allowed")}},{key:"_handleDestroy",value:function(){var t,e=this,n=null,o=window.MutationObserver||window.WebKitMutationObserver;o&&(t=this.options.ownerDocument,(n=new o(function(){t.body.contains(e.$el)||(n.disconnect(),n=null,e._unbindEventListener(),e._resetState())})).observe(this.$el.parentNode,{childList:!0,attributes:!1,subtree:!1})),window.onbeforeunload=function(){n&&n.disconnect(),n=null,e._unbindEventListener(),e._resetState()}}},{key:"_resetState",value:function(){this.dragEl=null,this.dropEl=null,this.ghost.destroy(),this.differ.destroy(),this.calcXY={x:0,y:0},this._removeWindowState()}},{key:"_removeWindowState",value:function(){window.sortableDndOnDown=null,window.sortableDndOnMove=null,delete window.sortableDndOnDown,delete window.sortableDndOnMove}},{key:"_bindEventListener",value:function(){this._onStart=this._onStart.bind(this),this._onMove=this._onMove.bind(this),this._onDrop=this._onDrop.bind(this),this.options.supportPointer?d(this.$el,"pointerdown",this._onStart):(d(this.$el,"mousedown",this._onStart),d(this.$el,"touchstart",this._onStart))}},{key:"_unbindEventListener",value:function(){e(this.$el,"pointerdown",this._onStart),e(this.$el,"touchstart",this._onStart),e(this.$el,"mousedown",this._onStart)}},{key:"_onMoveEvents",value:function(t){var e=this.options,n=e.supportPointer,e=e.ownerDocument;d(e,n?"pointermove":t?"touchmove":"mousemove",this._onMove)}},{key:"_onUpEvents",value:function(){var t=this.options.ownerDocument;d(t,"pointerup",this._onDrop),d(t,"touchend",this._onDrop),d(t,"touchcancel",this._onDrop),d(t,"mouseup",this._onDrop)}},{key:"_offMoveEvents",value:function(){var t=this.options.ownerDocument;e(t,"pointermove",this._onMove),e(t,"touchmove",this._onMove),e(t,"mousemove",this._onMove)}},{key:"_offUpEvents",value:function(){var t=this.options.ownerDocument;e(t,"mouseup",this._onDrop),e(t,"touchend",this._onDrop),e(t,"touchcancel",this._onDrop),e(t,"pointerup",this._onDrop)}}]),n}()}); |
{ | ||
"name": "sortable-dnd", | ||
"version": "0.0.11", | ||
"version": "0.0.12", | ||
"description": "JS Library for Drag and Drop, supports Sortable and Draggable", | ||
@@ -5,0 +5,0 @@ "main": "dist/sortable.js", |
@@ -20,11 +20,11 @@ <p> | ||
<li> | ||
<i>drag me</i> | ||
<i class="drag">drag me</i> | ||
<p>1</p> | ||
</li> | ||
<li> | ||
<i>drag me</i> | ||
<i class="drag">drag me</i> | ||
<p>2</p> | ||
</li> | ||
<li> | ||
<i>drag me</i> | ||
<i class="drag">drag me</i> | ||
<p>3</p> | ||
@@ -43,6 +43,6 @@ </li> | ||
chosenClass: 'chosen', | ||
draggable: (e) => { | ||
return e.target.tagName === 'I' ? true : false | ||
}, | ||
// or draggable: 'i' | ||
draggable: (e) => e.target.tagName === 'I' ? true : false, // use function | ||
// draggable: 'i' // use tagName | ||
// draggable: '.drag' // use class | ||
// draggable: '#drag' // use id | ||
dragging: (e) => { | ||
@@ -67,3 +67,3 @@ return e.target | ||
| `dragging` | `Function` | (e) => e.target | Specifies the drag and drop element, which must return an HTMLElement | | ||
| `dragEnd` | `Function` | (pre, cur) => {} | The callback function when the drag is completed | | ||
| `dragEnd` | `Function` | (pre, cur, changed) => {} | The callback function when the drag is completed | | ||
| `ghostStyle` | `Object` | {} | The style of the mask element when dragging | | ||
@@ -70,0 +70,0 @@ | `ghostClass` | `String` | '' | The class of the mask element when dragging | |
@@ -1,2 +0,2 @@ | ||
import utils from './utils.js' | ||
import { getRect, css } from './utils.js' | ||
@@ -18,3 +18,3 @@ export default function Animation() { | ||
target: child, | ||
rect: utils.getRect(child) | ||
rect: getRect(child) | ||
}) | ||
@@ -32,17 +32,17 @@ }) | ||
animate(el, preRect, animation = 150) { | ||
const curRect = utils.getRect(el) | ||
const curRect = getRect(el) | ||
const left = preRect.left - curRect.left | ||
const top = preRect.top - curRect.top | ||
utils.css(el, 'transition', 'none') | ||
utils.css(el, 'transform', `translate3d(${left}px, ${top}px, 0)`) | ||
css(el, 'transition', 'none') | ||
css(el, 'transform', `translate3d(${left}px, ${top}px, 0)`) | ||
el.offsetLeft // 触发重绘 | ||
utils.css(el, 'transition', `all ${animation}ms`) | ||
utils.css(el, 'transform', 'translate3d(0px, 0px, 0px)') | ||
css(el, 'transition', `all ${animation}ms`) | ||
css(el, 'transform', 'translate3d(0px, 0px, 0px)') | ||
clearTimeout(el.animated) | ||
el.animated = setTimeout(() => { | ||
utils.css(el, 'transition', '') | ||
utils.css(el, 'transform', '') | ||
css(el, 'transition', '') | ||
css(el, 'transform', '') | ||
el.animated = null | ||
@@ -49,0 +49,0 @@ }, animation) |
@@ -1,2 +0,14 @@ | ||
import utils from './utils.js' | ||
import { | ||
on, | ||
off, | ||
css, | ||
matches, | ||
getRect, | ||
debounce, | ||
getOffset, | ||
_nextTick, | ||
getElement, | ||
toggleClass, | ||
getParentAutoScrollElement | ||
} from './utils.js' | ||
import { Safari } from './Brower.js' | ||
@@ -8,6 +20,6 @@ import Animation from './Animation.js' | ||
*/ | ||
class Diff { | ||
class Differ { | ||
constructor() { | ||
this.old = { node: null, rect: {} } | ||
this.new = { node: null, rect: {} } | ||
this._old_ = { node: null, rect: {}, offset: {} } | ||
this._new_ = { node: null, rect: {}, offset: {} } | ||
} | ||
@@ -24,4 +36,4 @@ | ||
destroy() { | ||
this.old = { node: null, rect: {} } | ||
this.new = { node: null, rect: {} } | ||
this._old_ = { node: null, rect: {}, offset: {} } | ||
this._new_ = { node: null, rect: {}, offset: {} } | ||
} | ||
@@ -44,3 +56,2 @@ } | ||
this.$el = el | ||
this.rect = rect | ||
const { ghostClass, ghostStyle = {} } = this.options | ||
@@ -62,3 +73,3 @@ const { width, height } = rect | ||
for (const key in ghostStyle) { | ||
utils.css(this.$el, key, ghostStyle[key]) | ||
css(this.$el, key, ghostStyle[key]) | ||
} | ||
@@ -76,2 +87,6 @@ } | ||
rect() { | ||
return this.$el.getBoundingClientRect() | ||
} | ||
move() { | ||
@@ -98,10 +113,11 @@ // 将初始化放在 move 事件中,避免与鼠标点击事件冲突 | ||
this.options = options = Object.assign({}, options) | ||
this.scrollEl = getParentAutoScrollElement(this.$el, true) // 获取页面滚动元素 | ||
this.dragEl = null // 拖拽元素 | ||
this.dropEl = null // 释放元素 | ||
this.diff = null // 记录拖拽前后差异 | ||
this.differ = null // 记录拖拽前后差异 | ||
this.ghost = null // 拖拽时蒙版元素 | ||
this.calcXY = { x: 0, y: 0 } // 记录拖拽移动时坐标 | ||
utils.debounce(this.init(), 50) // 避免重复执行多次 | ||
debounce(this.init(), 50) // 避免重复执行多次 | ||
} | ||
@@ -133,3 +149,3 @@ | ||
this.diff = new Diff() | ||
this.differ = new Differ() | ||
this.ghost = new Ghost(this.options) | ||
@@ -143,38 +159,14 @@ | ||
_handleDestroy() { | ||
let observer = null | ||
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver | ||
if (MutationObserver) { | ||
observer = new MutationObserver(() => { | ||
if (!this.$el) { | ||
observer.disconnect() | ||
observer = null | ||
this._unbindEventListener() | ||
this._resetState() | ||
} | ||
}) | ||
observer.observe(this.$el.parentNode, { | ||
childList: true, // 观察目标子节点的变化,是否有添加或者删除 | ||
attributes: false, // 观察属性变动 | ||
subtree: false // 观察后代节点,默认为 false | ||
}) | ||
} | ||
window.onbeforeunload = () => { | ||
if (observer) observer.disconnect() | ||
observer = null | ||
this._unbindEventListener() | ||
this._resetState() | ||
} | ||
} | ||
// -------------------------------- drag and drop ---------------------------------- | ||
_onStart(evt) { | ||
const { dragging, draggable } = this.options | ||
const touch = (evt.touches && evt.touches[0]) || (evt.pointerType && evt.pointerType === 'touch' && evt) | ||
const target = (touch || evt).target | ||
const e = touch || evt | ||
if (typeof draggable === 'function') { | ||
if (!draggable(touch || evt)) return true | ||
if (!draggable(e)) return true | ||
} else if (typeof draggable === 'string') { | ||
if (!utils.matches(target, draggable)) return true | ||
if (!matches(e.target, draggable)) return true | ||
} else if (draggable !== undefined) { | ||
@@ -185,3 +177,3 @@ throw new Error(`draggable expected "function" or "string" but received "${typeof draggable}"`) | ||
if (/mousedown|pointerdown/.test(evt.type) && evt.button !== 0) return // only left button and enabled | ||
if (target === this.$el) return true | ||
if (e.target === this.$el) return true | ||
@@ -191,3 +183,3 @@ try { | ||
// Timeout neccessary for IE9 | ||
utils._nextTick(() => { document.selection.empty() }) | ||
_nextTick(() => { document.selection.empty() }) | ||
} else { | ||
@@ -198,5 +190,3 @@ window.getSelection().removeAllRanges() | ||
// 获取拖拽元素 | ||
const element = dragging && typeof dragging === 'function' | ||
? dragging(touch || evt) | ||
: utils.getElement(this.$el, target).el | ||
const element = typeof dragging === 'function' ? dragging(e) : getElement(this.$el, e.target).el | ||
@@ -216,3 +206,3 @@ // 不存在拖拽元素时不允许拖拽 | ||
// 获取当前元素在列表中的位置 | ||
const { index, el, rect } = utils.getElement(this.$el, this.dragEl) | ||
const { index, el, rect, offset } = getElement(this.$el, this.dragEl) | ||
@@ -227,5 +217,7 @@ if (!el || index < 0) return true | ||
this.diff.old.rect = rect | ||
this.differ._old_.rect = rect | ||
this.differ._old_.offset = offset | ||
this.differ._old_.node = this.dragEl | ||
this.calcXY = { x: (touch || evt).clientX, y: (touch || evt).clientY } | ||
this.calcXY = { x: e.clientX, y: e.clientY } | ||
@@ -245,3 +237,3 @@ this._onMoveEvents(touch) | ||
const { chosenClass } = this.options | ||
utils.toggleClass(this.dragEl, chosenClass, true) | ||
toggleClass(this.dragEl, chosenClass, true) | ||
this.ghost.move() | ||
@@ -260,5 +252,3 @@ | ||
this._checkRange(e) | ||
const { index, el, rect } = utils.getElement(this.$el, target) | ||
const { index, el, rect, offset } = getElement(this.$el, target) | ||
const { left, right, top, bottom } = rect | ||
@@ -268,2 +258,18 @@ | ||
// 判断边界值 | ||
const _rect = getRect(this.$el) | ||
this._checkRange(e, _rect) | ||
const { scrollTop, scrollLeft } = this.scrollEl | ||
// 如果目标元素超出当前可视区,不允许拖动 | ||
if (this.scrollEl !== this.$el && (_rect.left < 0 || _rect.top < 0)) { | ||
if (rect.top < (_rect.top + scrollTop) && _rect.top < 0) return | ||
if (rect.left < (_rect.left + scrollLeft) && _rect.left < 0) return | ||
} else { | ||
if (rect.top < _rect.top) return | ||
if (rect.left < _rect.left) return | ||
} | ||
if (clientX > left && clientX < right && clientY > top && clientY < bottom) { | ||
@@ -277,4 +283,7 @@ this.dropEl = el | ||
this.captureAnimationState() | ||
const _offset = getOffset(this.dragEl) // 获取拖拽元素的 offset 值 | ||
if (utils.index(this.$el, this.dragEl) < index) { | ||
// 优先比较 top 值,top 值相同再比较 left | ||
if (_offset.top < offset.top || _offset.left < offset.left) { | ||
this.$el.insertBefore(this.dragEl, this.dropEl.nextElementSibling) | ||
@@ -286,7 +295,5 @@ } else { | ||
this.animateRange() | ||
this.diff.old.node = this.dragEl | ||
this.diff.new.node = this.dropEl | ||
this.differ._new_.node = this.dropEl | ||
this.differ._new_.rect = getRect(this.dropEl) | ||
} | ||
this.diff.new.rect = utils.getRect(this.dropEl) | ||
} | ||
@@ -302,8 +309,20 @@ } | ||
utils.toggleClass(this.dragEl, chosenClass, false) | ||
toggleClass(this.dragEl, chosenClass, false) | ||
if (window.sortableDndOnDown && window.sortableDndOnMove) { | ||
// 重新获取一次拖拽元素的 offset 值作为拖拽完成后的 offset 值 | ||
this.differ._new_.offset = getOffset(this.dragEl) | ||
// 拖拽完成触发回调函数 | ||
const { _old_, _new_ } = this.differ | ||
// 通过 offset 比较是否进行了元素交换 | ||
const changed = _old_.offset.top !== _new_.offset.top || _old_.offset.left !== _new_.offset.left | ||
// 如果拖拽前后没有发生交换,重新赋值一次 | ||
if (!changed) this.differ._new_.node = this.differ._old_.node | ||
if (typeof dragEnd === 'function') { | ||
dragEnd(this.diff.old, this.diff.new) | ||
dragEnd(_old_, _new_, changed) | ||
} else { | ||
@@ -314,3 +333,3 @@ throw new Error(`Sortable-dnd Error: dragEnd expected "function" but received "${typeof dragEnd}"`) | ||
this.diff.destroy() | ||
this.differ.destroy() | ||
this.ghost.destroy() | ||
@@ -320,4 +339,4 @@ this._removeWindowState() | ||
_checkRange(e) { | ||
const { top, left, right, bottom } = utils.getRect(this.$el) | ||
_checkRange(e, groupRect) { | ||
const { top, left, right, bottom } = groupRect | ||
@@ -330,2 +349,32 @@ if (e.clientX < left || e.clientX > right || e.clientY < top || e.clientY > bottom) { | ||
// -------------------------------- auto destroy ---------------------------------- | ||
_handleDestroy() { | ||
let observer = null | ||
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver | ||
if (MutationObserver) { | ||
const { ownerDocument } = this.options | ||
observer = new MutationObserver(() => { | ||
if (!ownerDocument.body.contains(this.$el)) { | ||
observer.disconnect() | ||
observer = null | ||
this._unbindEventListener() | ||
this._resetState() | ||
} | ||
}) | ||
observer.observe(this.$el.parentNode, { | ||
childList: true, // 观察目标子节点的变化,是否有添加或者删除 | ||
attributes: false, // 观察属性变动 | ||
subtree: false // 观察后代节点,默认为 false | ||
}) | ||
} | ||
window.onbeforeunload = () => { | ||
if (observer) observer.disconnect() | ||
observer = null | ||
this._unbindEventListener() | ||
this._resetState() | ||
} | ||
} | ||
// -------------------------------- reset state ---------------------------------- | ||
_resetState() { | ||
@@ -335,3 +384,3 @@ this.dragEl = null | ||
this.ghost.destroy() | ||
this.diff.destroy() | ||
this.differ.destroy() | ||
this.calcXY = { x: 0, y: 0 } | ||
@@ -348,2 +397,3 @@ this._removeWindowState() | ||
// -------------------------------- listener ---------------------------------- | ||
_bindEventListener() { | ||
@@ -356,6 +406,6 @@ this._onStart = this._onStart.bind(this) | ||
if (supportPointer) { | ||
utils.on(this.$el, 'pointerdown', this._onStart) | ||
on(this.$el, 'pointerdown', this._onStart) | ||
} else { | ||
utils.on(this.$el, 'mousedown', this._onStart) | ||
utils.on(this.$el, 'touchstart', this._onStart) | ||
on(this.$el, 'mousedown', this._onStart) | ||
on(this.$el, 'touchstart', this._onStart) | ||
} | ||
@@ -365,5 +415,5 @@ } | ||
_unbindEventListener() { | ||
utils.off(this.$el, 'pointerdown', this._onStart) | ||
utils.off(this.$el, 'touchstart', this._onStart) | ||
utils.off(this.$el, 'mousedown', this._onStart) | ||
off(this.$el, 'pointerdown', this._onStart) | ||
off(this.$el, 'touchstart', this._onStart) | ||
off(this.$el, 'mousedown', this._onStart) | ||
} | ||
@@ -374,7 +424,7 @@ | ||
if (supportPointer) { | ||
utils.on(ownerDocument, 'pointermove', this._onMove) | ||
on(ownerDocument, 'pointermove', this._onMove) | ||
} else if (touch) { | ||
utils.on(ownerDocument, 'touchmove', this._onMove) | ||
on(ownerDocument, 'touchmove', this._onMove) | ||
} else { | ||
utils.on(ownerDocument, 'mousemove', this._onMove) | ||
on(ownerDocument, 'mousemove', this._onMove) | ||
} | ||
@@ -385,6 +435,6 @@ } | ||
const { ownerDocument } = this.options | ||
utils.on(ownerDocument, 'pointerup', this._onDrop) | ||
utils.on(ownerDocument, 'touchend', this._onDrop) | ||
utils.on(ownerDocument, 'touchcancel', this._onDrop) | ||
utils.on(ownerDocument, 'mouseup', this._onDrop) | ||
on(ownerDocument, 'pointerup', this._onDrop) | ||
on(ownerDocument, 'touchend', this._onDrop) | ||
on(ownerDocument, 'touchcancel', this._onDrop) | ||
on(ownerDocument, 'mouseup', this._onDrop) | ||
} | ||
@@ -394,5 +444,5 @@ | ||
const { ownerDocument } = this.options | ||
utils.off(ownerDocument, 'pointermove', this._onMove) | ||
utils.off(ownerDocument, 'touchmove', this._onMove) | ||
utils.off(ownerDocument, 'mousemove', this._onMove) | ||
off(ownerDocument, 'pointermove', this._onMove) | ||
off(ownerDocument, 'touchmove', this._onMove) | ||
off(ownerDocument, 'mousemove', this._onMove) | ||
} | ||
@@ -402,6 +452,6 @@ | ||
const { ownerDocument } = this.options | ||
utils.off(ownerDocument, 'mouseup', this._onDrop) | ||
utils.off(ownerDocument, 'touchend', this._onDrop) | ||
utils.off(ownerDocument, 'touchcancel', this._onDrop) | ||
utils.off(ownerDocument, 'pointerup', this._onDrop) | ||
off(ownerDocument, 'mouseup', this._onDrop) | ||
off(ownerDocument, 'touchend', this._onDrop) | ||
off(ownerDocument, 'touchcancel', this._onDrop) | ||
off(ownerDocument, 'pointerup', this._onDrop) | ||
} | ||
@@ -408,0 +458,0 @@ } |
446
src/utils.js
@@ -10,11 +10,9 @@ import { IE11OrLess } from './Brower.js' | ||
export default { | ||
/** | ||
* add specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
on(el, event, fn) { | ||
/** | ||
* add specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
export function on(el, event, fn) { | ||
if (window.addEventListener) { | ||
@@ -25,205 +23,281 @@ el.addEventListener(event, fn, !IE11OrLess && captureMode) | ||
} | ||
}, | ||
/** | ||
* remove specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
off(el, event, fn) { | ||
if (window.removeEventListener) { | ||
el.removeEventListener(event, fn, !IE11OrLess && captureMode) | ||
} else if (window.detachEvent) { | ||
el.detachEvent('on' + event, fn) | ||
} | ||
}, | ||
} | ||
getWindowScrollingElement() { | ||
let scrollingElement = document.scrollingElement | ||
if (scrollingElement) { | ||
return scrollingElement | ||
} else { | ||
return document.documentElement | ||
} | ||
}, | ||
/** | ||
* remove specified event listener | ||
* @param {HTMLElement} el | ||
* @param {String} event | ||
* @param {Function} fn | ||
*/ | ||
export function off(el, event, fn) { | ||
if (window.removeEventListener) { | ||
el.removeEventListener(event, fn, !IE11OrLess && captureMode) | ||
} else if (window.detachEvent) { | ||
el.detachEvent('on' + event, fn) | ||
} | ||
} | ||
/** | ||
* get specified element's index in group | ||
* @param {HTMLElement} group | ||
* @param {HTMLElement} el | ||
* @returns {Number} index | ||
*/ | ||
index(group, el) { | ||
if (!el || !el.parentNode) return -1 | ||
/** | ||
* get element's offetTop | ||
* @param {HTMLElement} el | ||
*/ | ||
export function getOffset(el) { | ||
let result = { | ||
top: 0, | ||
left: 0, | ||
height: 0, | ||
width: 0 | ||
} | ||
result.height = el.offsetHeight | ||
result.width = el.offsetWidth | ||
result.top = el.offsetTop | ||
result.left = el.offsetLeft | ||
const children = [...Array.from(group.children)] | ||
return children.indexOf(el) | ||
}, | ||
let parent = el.offsetParent | ||
/** | ||
* Returns the "bounding client rect" of given element | ||
* @param {HTMLElement} el The element whose boundingClientRect is wanted | ||
*/ | ||
getRect(el) { | ||
if (!el.getBoundingClientRect && el !== window) return | ||
while (parent !== null) { | ||
result.top += parent.offsetTop | ||
result.left += parent.offsetLeft | ||
parent = parent.offsetParent | ||
} | ||
const rect = { | ||
top: 0, | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
height: 0, | ||
width: 0, | ||
} | ||
return result | ||
} | ||
let elRect | ||
if (el !== window && el.parentNode && el !== this.getWindowScrollingElement()) { | ||
elRect = el.getBoundingClientRect() | ||
rect.top = elRect.top | ||
rect.left = elRect.left | ||
rect.bottom = elRect.bottom | ||
rect.right = elRect.right | ||
rect.height = elRect.height | ||
rect.width = elRect.width | ||
} else { | ||
rect.top = 0 | ||
rect.left = 0 | ||
rect.bottom = window.innerHeight | ||
rect.right = window.innerWidth | ||
rect.height = window.innerHeight | ||
rect.width = window.innerWidth | ||
} | ||
/** | ||
* get scroll element | ||
* @param {HTMLElement} el | ||
* @param {Boolean} includeSelf whether to include the passed element | ||
* @returns {HTMLElement} scroll element | ||
*/ | ||
export function getParentAutoScrollElement(el, includeSelf) { | ||
// skip to window | ||
if (!el || !el.getBoundingClientRect) return getWindowScrollingElement() | ||
return rect | ||
}, | ||
let elem = el | ||
let gotSelf = false | ||
do { | ||
// we don't need to get elem css if it isn't even overflowing in the first place (performance) | ||
if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) { | ||
let elemCSS = css(elem) | ||
if ( | ||
elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') || | ||
elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll') | ||
) { | ||
if (!elem.getBoundingClientRect || elem === document.body) return getWindowScrollingElement() | ||
/** | ||
* get target Element in group | ||
* @param {HTMLElement} group | ||
* @param {HTMLElement} el | ||
*/ | ||
getElement(group, el) { | ||
const result = { index: -1, el: null, rect: {} } | ||
const children = [...Array.from(group.children)] | ||
if (gotSelf || includeSelf) return elem | ||
gotSelf = true | ||
} | ||
} | ||
} while (elem = elem.parentNode) | ||
// 如果能直接在子元素中找到,返回对应的index | ||
const index = children.indexOf(el) | ||
if (index > -1) | ||
return getWindowScrollingElement() | ||
} | ||
export function getWindowScrollingElement() { | ||
let scrollingElement = document.scrollingElement | ||
if (scrollingElement) { | ||
return scrollingElement | ||
} else { | ||
return document.documentElement | ||
} | ||
} | ||
/** | ||
* get specified element's index in group | ||
* @param {HTMLElement} group | ||
* @param {HTMLElement} el | ||
* @returns {Number} index | ||
*/ | ||
export function getIndex(group, el) { | ||
if (!el || !el.parentNode) return -1 | ||
const children = [...Array.from(group.children)] | ||
return children.indexOf(el) | ||
} | ||
/** | ||
* Returns the "bounding client rect" of given element | ||
* @param {HTMLElement} el The element whose boundingClientRect is wanted | ||
*/ | ||
export function getRect(el) { | ||
if (!el.getBoundingClientRect && el !== window) return | ||
const rect = { | ||
top: 0, | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
height: 0, | ||
width: 0, | ||
} | ||
let elRect | ||
if (el !== window && el.parentNode && el !== getWindowScrollingElement()) { | ||
elRect = el.getBoundingClientRect() | ||
rect.top = elRect.top | ||
rect.left = elRect.left | ||
rect.bottom = elRect.bottom | ||
rect.right = elRect.right | ||
rect.height = elRect.height | ||
rect.width = elRect.width | ||
} else { | ||
rect.top = 0 | ||
rect.left = 0 | ||
rect.bottom = window.innerHeight | ||
rect.right = window.innerWidth | ||
rect.height = window.innerHeight | ||
rect.width = window.innerWidth | ||
} | ||
return rect | ||
} | ||
/** | ||
* get target Element in group | ||
* @param {HTMLElement} group | ||
* @param {HTMLElement} el | ||
*/ | ||
export function getElement(group, el) { | ||
const result = { index: -1, el: null, rect: {}, offset: {} } | ||
const children = [...Array.from(group.children)] | ||
// 如果能直接在子元素中找到,返回对应的index | ||
const index = children.indexOf(el) | ||
if (index > -1) | ||
Object.assign(result, { | ||
index, | ||
el: children[index], | ||
rect: getRect(children[index]), | ||
offset: getOffset(children[index]) | ||
}) | ||
// children 中无法直接找到对应的dom时,需要向下寻找 | ||
for (let i = 0; i < children.length; i++) { | ||
if (isChildOf(el, children[i])) { | ||
Object.assign(result, { | ||
index, | ||
el: children[index], | ||
rect: this.getRect(children[index]) | ||
index: i, | ||
el: children[i], | ||
rect: getRect(children[i]), | ||
offset: getOffset(children[i]) | ||
}) | ||
// children 中无法直接找到对应的dom时,需要向下寻找 | ||
for (let i = 0; i < children.length; i++) { | ||
if (this.isChildOf(el, children[i])) | ||
Object.assign(result, { | ||
index: i, | ||
el: children[i], | ||
rect: this.getRect(children[i]) | ||
}) | ||
break | ||
} | ||
} | ||
return result | ||
} | ||
return result | ||
}, | ||
/** | ||
* Check if child element is contained in parent element | ||
* @param {HTMLElement} child | ||
* @param {HTMLElement} parent | ||
* @returns {Boolean} true | false | ||
*/ | ||
isChildOf(child, parent) { | ||
let parentNode | ||
if (child && parent) { | ||
parentNode = child.parentNode | ||
while (parentNode) { | ||
if (parent === parentNode) return true | ||
parentNode = parentNode.parentNode | ||
} | ||
/** | ||
* Check if child element is contained in parent element | ||
* @param {HTMLElement} child | ||
* @param {HTMLElement} parent | ||
* @returns {Boolean} true | false | ||
*/ | ||
export function isChildOf(child, parent) { | ||
let parentNode | ||
if (child && parent) { | ||
parentNode = child.parentNode | ||
while (parentNode) { | ||
if (parent === parentNode) return true | ||
parentNode = parentNode.parentNode | ||
} | ||
return false | ||
}, | ||
} | ||
return false | ||
} | ||
/** | ||
* add or remove element's class | ||
* @param {HTMLElement} el element | ||
* @param {String} name class name | ||
* @param {Boolean} state true: add, false: remove | ||
*/ | ||
toggleClass(el, name, state) { | ||
if (el && name) { | ||
if (el.classList) { | ||
el.classList[state ? 'add' : 'remove'](name) | ||
} else { | ||
const className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ') | ||
el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ') | ||
} | ||
/** | ||
* add or remove element's class | ||
* @param {HTMLElement} el element | ||
* @param {String} name class name | ||
* @param {Boolean} state true: add, false: remove | ||
*/ | ||
export function toggleClass(el, name, state) { | ||
if (el && name) { | ||
if (el.classList) { | ||
el.classList[state ? 'add' : 'remove'](name) | ||
} else { | ||
const className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ') | ||
el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ') | ||
} | ||
}, | ||
} | ||
} | ||
/** | ||
* Check if a DOM element matches a given selector | ||
* @param {HTMLElement} el | ||
* @param {String} selector | ||
* @returns | ||
*/ | ||
matches(el, selector) { | ||
if (!selector) return | ||
selector[0] === '>' && (selector = selector.substring(1)) | ||
if (el) { | ||
try { | ||
if (el.matches) { | ||
return el.matches(selector) | ||
} else if (el.msMatchesSelector) { | ||
return el.msMatchesSelector(selector) | ||
} else if (el.webkitMatchesSelector) { | ||
return el.webkitMatchesSelector(selector) | ||
} | ||
} catch(error) { | ||
return false | ||
/** | ||
* Check if a DOM element matches a given selector | ||
* @param {HTMLElement} el | ||
* @param {String} selector | ||
* @returns | ||
*/ | ||
export function matches(el, selector) { | ||
if (!selector) return | ||
selector[0] === '>' && (selector = selector.substring(1)) | ||
if (el) { | ||
try { | ||
if (el.matches) { | ||
return el.matches(selector) | ||
} else if (el.msMatchesSelector) { | ||
return el.msMatchesSelector(selector) | ||
} else if (el.webkitMatchesSelector) { | ||
return el.webkitMatchesSelector(selector) | ||
} | ||
} catch(error) { | ||
return false | ||
} | ||
return false | ||
}, | ||
} | ||
css(el, prop, val) { | ||
let style = el && el.style | ||
if (style) { | ||
if (val === void 0) { | ||
if (document.defaultView && document.defaultView.getComputedStyle) { | ||
val = document.defaultView.getComputedStyle(el, '') | ||
} else if (el.currentStyle) { | ||
val = el.currentStyle | ||
} | ||
return prop === void 0 ? val : val[prop] | ||
} else { | ||
if (!(prop in style) && prop.indexOf('webkit') === -1) { | ||
prop = '-webkit-' + prop | ||
} | ||
style[prop] = val + (typeof val === 'string' ? '' : 'px') | ||
return false | ||
} | ||
export function css(el, prop, val) { | ||
let style = el && el.style | ||
if (style) { | ||
if (val === void 0) { | ||
if (document.defaultView && document.defaultView.getComputedStyle) { | ||
val = document.defaultView.getComputedStyle(el, '') | ||
} else if (el.currentStyle) { | ||
val = el.currentStyle | ||
} | ||
return prop === void 0 ? val : val[prop] | ||
} else { | ||
if (!(prop in style) && prop.indexOf('webkit') === -1) { | ||
prop = '-webkit-' + prop | ||
} | ||
style[prop] = val + (typeof val === 'string' ? '' : 'px') | ||
} | ||
}, | ||
} | ||
} | ||
debounce(fn, delay) { | ||
return function (...args) { | ||
clearTimeout(fn.id) | ||
fn.id = setTimeout(() => { | ||
fn.call(this, ...args) | ||
}, delay) | ||
} | ||
}, | ||
_nextTick(fn) { | ||
return setTimeout(fn, 0) | ||
export function debounce(fn, delay) { | ||
return function (...args) { | ||
clearTimeout(fn.id) | ||
fn.id = setTimeout(() => { | ||
fn.call(this, ...args) | ||
}, delay) | ||
} | ||
} | ||
export function _nextTick(fn) { | ||
return setTimeout(fn, 0) | ||
} | ||
export default { | ||
on, | ||
off, | ||
css, | ||
getRect, | ||
matches, | ||
getIndex, | ||
debounce, | ||
_nextTick, | ||
isChildOf, | ||
getElement, | ||
toggleClass, | ||
getWindowScrollingElement, | ||
getParentAutoScrollElement, | ||
} |
67430
1522