Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-virtual-drag-list

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-virtual-drag-list - npm Package Compare versions

Comparing version 2.2.0 to 2.3.0

dist/draglist.min.js

1122

dist/draglist.js
/*!
* react-virtual-drag-list v2.2.0
* react-virtual-drag-list v2.3.0
* open source under the MIT license

@@ -41,3 +41,3 @@ * https://github.com/mfuu/react-virtual-drag-list#readme

/*!
* sortable-dnd v0.0.6
* sortable-dnd v0.1.0
* open source under the MIT license

@@ -50,2 +50,12 @@ * https://github.com/mfuu/sortable-dnd#readme

function _typeof(obj) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
}, _typeof(obj);
}
function _classCallCheck(instance, Constructor) {

@@ -117,2 +127,4 @@ if (!(instance instanceof Constructor)) {

var Safari = userAgent(/safari/i) && !userAgent(/chrome/i) && !userAgent(/android/i);
var IOS = userAgent(/iP(ad|od|hone)/i);
var ChromeForAndroid = userAgent(/chrome/i) && userAgent(/android/i);
var captureMode = {

@@ -122,130 +134,440 @@ capture: false,

};
var utils = {
on: function on(el, event, fn) {
el.addEventListener(event, fn, !IE11OrLess && captureMode);
},
off: function off(el, event, fn) {
el.removeEventListener(event, fn, !IE11OrLess && captureMode);
},
getWindowScrollingElement: function getWindowScrollingElement() {
var scrollingElement = document.scrollingElement;
var R_SPACE = /\s+/g;
/**
* detect passive event support
*/
if (scrollingElement) {
return scrollingElement;
} else {
return document.documentElement;
function supportPassive() {
// https://github.com/Modernizr/Modernizr/issues/1894
var supportPassive = false;
document.addEventListener('checkIfSupportPassive', null, {
get passive() {
supportPassive = true;
return true;
}
},
index: function index(group, el) {
if (!el || !el.parentNode) return -1;
var children = _toConsumableArray(Array.from(group.children));
});
return supportPassive;
}
/**
* add specified event listener
* @param {HTMLElement} el
* @param {String} event
* @param {Function} fn
* @param {Boolean} sp
*/
return children.indexOf(el);
},
getRect: function getRect(children, index) {
if (!children.length) return {};
if (index < 0) return {};
return children[index].getBoundingClientRect();
},
getElement: function getElement(group, dragging) {
var result = {
index: -1,
el: null,
rect: {}
};
var children = _toConsumableArray(Array.from(group.children)); // 如果能直接在子元素中找到,返回对应的index
function on(el, event, fn, sp) {
if (window.addEventListener) {
el.addEventListener(event, fn, sp || !IE11OrLess ? captureMode : false);
} else if (window.attachEvent) {
el.attachEvent('on' + event, fn);
}
}
/**
* remove specified event listener
* @param {HTMLElement} el
* @param {String} event
* @param {Function} fn
* @param {Boolean} sp
*/
var index = children.indexOf(dragging);
if (index > -1) Object.assign(result, {
index: index,
el: children[index],
rect: children[index].getBoundingClientRect()
}); // children 中无法直接找到对应的dom时,需要向下寻找
function off(el, event, fn, sp) {
if (window.removeEventListener) {
el.removeEventListener(event, fn, sp || !IE11OrLess ? captureMode : false);
} else if (window.detachEvent) {
el.detachEvent('on' + event, fn);
}
}
/**
* get element's offetTop
* @param {HTMLElement} el
*/
for (var i = 0; i < children.length; i++) {
if (this.isChildOf(dragging, children[i])) Object.assign(result, {
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;
while (parent !== null) {
result.top += parent.offsetTop;
result.left += parent.offsetLeft;
parent = parent.offsetParent;
}
return result;
}
/**
* get scroll element
* @param {HTMLElement} el
* @param {Boolean} includeSelf whether to include the passed element
* @returns {HTMLElement} scroll element
*/
function getParentAutoScrollElement(el, includeSelf) {
// skip to window
if (!el || !el.getBoundingClientRect) return getWindowScrollingElement();
var elem = el;
var 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) {
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 getWindowScrollingElement();
}
function getWindowScrollingElement() {
var scrollingElement = document.scrollingElement;
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
*/
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;
}
return rect;
}
/**
* get target Element in group
* @param {HTMLElement} group
* @param {HTMLElement} el
*/
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: children[i].getBoundingClientRect()
rect: getRect(children[i]),
offset: getOffset(children[i])
});
break;
}
}
return result;
},
// 判断子元素是否包含在父元素中
isChildOf: function isChildOf(child, parent) {
var parentNode;
return result;
}
/**
* Check if child element is contained in parent element
* @param {HTMLElement} child
* @param {HTMLElement} parent
* @returns {Boolean} true | false
*/
if (child && parent) {
parentNode = child.parentNode;
while (parentNode) {
if (parent === parentNode) return true;
parentNode = parentNode.parentNode;
function isChildOf(child, parent) {
var parentNode;
if (child && parent) {
parentNode = child.parentNode;
while (parentNode) {
if (parent === parentNode) return true;
parentNode = parentNode.parentNode;
}
}
return false;
}
/**
* add or remove element's class
* @param {HTMLElement} el element
* @param {String} name class name
* @param {Boolean} state true: add, false: remove
*/
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
*/
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;
},
animate: function animate(el, preRect) {
var _this = this;
return false;
}
var animation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 300;
var curRect = el.getBoundingClientRect();
var left = preRect.left - curRect.left;
var top = preRect.top - curRect.top;
this.css(el, 'transition', 'none');
this.css(el, 'transform', "translate3d(".concat(left, "px, ").concat(top, "px, 0)"));
el.offsetLeft; // 触发重绘
function css(el, prop, val) {
var style = el && el.style;
this.css(el, 'transition', "all ".concat(animation, "ms"));
this.css(el, 'transform', 'translate3d(0px, 0px, 0px)');
clearTimeout(el.animated);
el.animated = setTimeout(function () {
_this.css(el, 'transition', '');
if (style) {
if (val === void 0) {
if (document.defaultView && document.defaultView.getComputedStyle) {
val = document.defaultView.getComputedStyle(el, '');
} else if (el.currentStyle) {
val = el.currentStyle;
}
_this.css(el, 'transform', '');
return prop === void 0 ? val : val[prop];
} else {
if (!(prop in style) && prop.indexOf('webkit') === -1) {
prop = '-webkit-' + prop;
}
el.animated = null;
}, animation);
},
css: function css(el, prop, val) {
var style = el && el.style;
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;
}
function _nextTick(fn) {
return setTimeout(fn, 0);
}
return prop === void 0 ? val : val[prop];
var CSS_TRANSITIONS = ['-webkit-transition', '-moz-transition', '-ms-transition', '-o-transition', 'transition'];
var CSS_TRANSFORMS = ['-webkit-transform', '-moz-transform', '-ms-transform', '-o-transform', 'transform'];
function Animation() {
var animationState = [];
return {
captureAnimationState: function captureAnimationState() {
var children = _toConsumableArray(Array.from(this.$el.children));
var _getRange = getRange(children, this.dragEl, this.dropEl),
start = _getRange.start,
end = _getRange.end;
animationState.length = 0; // 重置
children.slice(start, end + 1).forEach(function (child) {
animationState.push({
target: child,
rect: getRect(child)
});
});
},
animateRange: function animateRange() {
var _this = this;
animationState.forEach(function (state) {
var target = state.target,
rect = state.rect;
_this.animate(target, rect, _this.animation);
});
},
animate: function animate(el, preRect) {
var animation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 150;
var curRect = getRect(el);
var left = preRect.left - curRect.left;
var top = preRect.top - curRect.top;
CSS_TRANSITIONS.forEach(function (ts) {
return css(el, ts, 'none');
});
CSS_TRANSFORMS.forEach(function (tf) {
return css(el, tf, "".concat(tf.split('transform')[0], "translate3d(").concat(left, "px, ").concat(top, "px, 0)"));
});
el.offsetLeft; // 触发重绘
CSS_TRANSITIONS.forEach(function (ts) {
return css(el, ts, "".concat(ts.split('transition')[0], "transform ").concat(animation, "ms"));
});
CSS_TRANSFORMS.forEach(function (tf) {
return css(el, tf, "".concat(tf.split('transform')[0], "translate3d(0px, 0px, 0px)"));
});
clearTimeout(el.animated);
el.animated = setTimeout(function () {
CSS_TRANSITIONS.forEach(function (ts) {
return css(el, ts, '');
});
CSS_TRANSFORMS.forEach(function (tf) {
return css(el, tf, '');
});
el.animated = null;
}, animation);
}
};
}
function getRange(children, drag, drop) {
var start = children.indexOf(drag);
var end = children.indexOf(drop);
return start < end ? {
start: start,
end: end
} : {
start: end,
end: start
};
}
function DNDEvent() {
return {
_bindEventListener: function _bindEventListener() {
this._onStart = this._onStart.bind(this);
this._onMove = this._onMove.bind(this);
this._onDrop = this._onDrop.bind(this);
var _this$options = this.options,
supportPointer = _this$options.supportPointer,
supportTouch = _this$options.supportTouch,
supportPassive = _this$options.supportPassive;
if (supportPointer) {
on(this.$el, 'pointerdown', this._onStart, supportPassive);
} else if (supportTouch) {
on(this.$el, 'touchstart', this._onStart, supportPassive);
} else {
if (!(prop in style) && prop.indexOf('webkit') === -1) {
prop = '-webkit-' + prop;
}
on(this.$el, 'mousedown', this._onStart, supportPassive);
}
},
_unbindEventListener: function _unbindEventListener() {
var supportPassive = this.options.supportPassive;
off(this.$el, 'pointerdown', this._onStart, supportPassive);
off(this.$el, 'touchstart', this._onStart, supportPassive);
off(this.$el, 'mousedown', this._onStart, supportPassive);
style[prop] = val + (typeof val === 'string' ? '' : 'px');
if (this.nativeDraggable) {
off(this.$el, 'dragover', this);
off(this.$el, 'dragenter', this);
}
}
},
debounce: function debounce(fn, delay) {
return function () {
var _this2 = this;
},
_onMoveEvents: function _onMoveEvents(touch) {
var _this$options2 = this.options,
supportPointer = _this$options2.supportPointer,
ownerDocument = _this$options2.ownerDocument,
supportPassive = _this$options2.supportPassive;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
if (supportPointer) {
on(ownerDocument, 'pointermove', this._onMove, supportPassive);
} else if (touch) {
on(ownerDocument, 'touchmove', this._onMove, supportPassive);
} else {
on(ownerDocument, 'mousemove', this._onMove, supportPassive);
}
clearTimeout(fn.id);
fn.id = setTimeout(function () {
fn.call.apply(fn, [_this2].concat(args));
}, delay);
};
}
};
},
_onUpEvents: function _onUpEvents() {
var _this$options3 = this.options,
ownerDocument = _this$options3.ownerDocument,
supportPassive = _this$options3.supportPassive;
on(ownerDocument, 'pointerup', this._onDrop, supportPassive);
on(ownerDocument, 'pointercancel', this._onDrop, supportPassive);
on(ownerDocument, 'touchend', this._onDrop, supportPassive);
on(ownerDocument, 'touchcancel', this._onDrop, supportPassive);
on(ownerDocument, 'mouseup', this._onDrop, supportPassive);
},
_offMoveEvents: function _offMoveEvents() {
var _this$options4 = this.options,
ownerDocument = _this$options4.ownerDocument,
supportPassive = _this$options4.supportPassive;
off(ownerDocument, 'pointermove', this._onMove, supportPassive);
off(ownerDocument, 'touchmove', this._onMove, supportPassive);
off(ownerDocument, 'mousemove', this._onMove, supportPassive);
},
_offUpEvents: function _offUpEvents() {
var _this$options5 = this.options,
ownerDocument = _this$options5.ownerDocument,
supportPassive = _this$options5.supportPassive;
off(ownerDocument, 'pointerup', this._onDrop, supportPassive);
off(ownerDocument, 'pointercancel', this._onDrop, supportPassive);
off(ownerDocument, 'touchend', this._onDrop, supportPassive);
off(ownerDocument, 'touchcancel', this._onDrop, supportPassive);
off(ownerDocument, 'mouseup', this._onDrop, supportPassive);
}
};
}
/**

@@ -255,17 +577,20 @@ * 拖拽前后差异初始化

var Diff = /*#__PURE__*/function () {
function Diff() {
_classCallCheck(this, Diff);
this.old = {
var Differ = /*#__PURE__*/function () {
function Differ() {
_classCallCheck(this, Differ);
this._old_ = {
node: null,
rect: {}
rect: {},
offset: {}
};
this["new"] = {
this._new_ = {
node: null,
rect: {}
rect: {},
offset: {}
};
}
_createClass(Diff, [{
_createClass(Differ, [{
key: "get",

@@ -283,9 +608,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: {}
};

@@ -295,3 +622,3 @@ }

return Diff;
return Differ;
}();

@@ -316,9 +643,4 @@ /**

value: function init(el, rect) {
if (!el) {
console.error('Ghost Element is required');
return;
}
if (!el) return;
this.$el = el;
this.rect = rect;
var _this$options = this.options,

@@ -341,6 +663,4 @@ ghostClass = _this$options.ghostClass,

this.$el.style.pointerEvents = 'none';
for (var key in ghostStyle) {
utils.css(this.$el, key, ghostStyle[key]);
}
this.$el.style.cursor = 'move';
this.setStyle(ghostStyle);
}

@@ -359,2 +679,14 @@ }, {

}, {
key: "setStyle",
value: function setStyle(style) {
for (var key in style) {
css(this.$el, key, style[key]);
}
}
}, {
key: "rect",
value: function rect() {
return this.$el.getBoundingClientRect();
}
}, {
key: "move",

@@ -369,2 +701,3 @@ value: function move() {

this.$el.style.transform = "translate3d(".concat(this.x, "px, ").concat(this.y, "px, 0)");
if (this.$el.style.cursor !== 'move') this.$el.style.cursor = 'move';
}

@@ -380,268 +713,345 @@ }, {

return Ghost;
}();
}(); // -------------------------------- Sortable ----------------------------------
var documentExists = typeof document !== 'undefined';
var supportDraggable = documentExists && !ChromeForAndroid && !IOS && 'draggable' in document.createElement('div');
/**
* @interface Options {
*
* group: HTMLElement,
*
* draggable?: Function, return element node selected when dragging, or null
*
* dragEnd?: Function, The callback function when the drag is completed
*
* ghostStyle?: Object,
*
* ghostClass?: String,
*
* }
* @class Sortable
* @param {HTMLElement} el
* @param {Object} options
*/
function Sortable(el, options) {
if (!(el && el.nodeType && el.nodeType === 1)) {
throw "Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(el));
}
var Sortable = /*#__PURE__*/function () {
function Sortable(options) {
_classCallCheck(this, Sortable);
this.$el = el; // root element
this.group = options.group; // 父级元素
this.options = options = Object.assign({}, options);
this.scrollEl = getParentAutoScrollElement(this.$el, true); // 获取页面滚动元素
this.dragging = options.dragging; // 必须为函数且必须返回一个 HTMLElement (e) => return e.target
this.dragEl = null; // 拖拽元素
this.dragEnd = options.dragEnd; // 拖拽完成时的回调函数,返回两个值(olddom, newdom) => {}
this.dropEl = null; // 释放元素
this.ghostStyle = options.ghostStyle; // 克隆元素包含的属性
this.differ = null; // 记录拖拽前后差异
this.ghostClass = options.ghostClass; // 克隆元素的类名
this.ghost = null; // 拖拽时蒙版元素
this.animation = options.animation || 300; // 动画延迟
this.calcXY = {
x: 0,
y: 0
}; // 记录拖拽移动时坐标
this.isMousedown = false; // 记录鼠标按下
var defaults = {
disabled: false,
//
animation: 150,
// 动画延时
ghostClass: '',
// 拖拽元素Class类名
ghostStyle: {},
// 拖拽元素样式
chosenClass: '',
// 选中元素样式
draggable: undefined,
// String: css selecter, Function: (e) => return true
dragging: undefined,
// 必须为函数且必须返回一个 HTMLElement: (e) => return e.target
dragEnd: undefined,
// 拖拽完成时的回调函数: (old, new, changed) => {}
forceFallback: false,
// 忽略 HTML5拖拽行为,强制回调进行
stopPropagation: false,
// 阻止捕获和冒泡阶段中当前事件的进一步传播
supportPassive: supportPassive(),
supportPointer: 'PointerEvent' in window && !Safari,
supportTouch: 'ontouchstart' in window,
ownerDocument: this.$el.ownerDocument
}; // Set default options
this.isMousemove = false; // 记录鼠标移动
for (var name in defaults) {
!(name in this.options) && (this.options[name] = defaults[name]);
}
this.dragEl = null; // 拖拽元素
this.differ = new Differ();
this.ghost = new Ghost(this.options);
Object.assign(this, Animation(), DNDEvent());
this.dropEl = null; // 释放元素
this._bindEventListener();
this.diff = new Diff(); // 记录拖拽前后差异
this.nativeDraggable = this.options.forceFallback ? false : supportDraggable;
this.ghost = new Ghost({
ghostClass: this.ghostClass,
ghostStyle: this.ghostStyle
});
this.supportPointer = 'PointerEvent' in window && !Safari;
this.calcXY = {
x: 0,
y: 0
};
utils.debounce(this.init(), 50); // 避免重复执行多次
if (this.nativeDraggable) {
on(this.$el, 'dragover', this);
on(this.$el, 'dragenter', this);
}
_createClass(Sortable, [{
key: "init",
value: function init() {
if (!this.group) {
console.error('Error: group is required');
return;
}
this._handleDestroy();
}
this._bindEventListener();
Sortable.prototype =
/** @lends Sortable.prototype */
{
constructor: Sortable,
destroy: function destroy() {
this._unbindEventListener();
this._resetState();
},
// -------------------------------- drag and drop ----------------------------------
_onStart: function _onStart(
/** Event|TouchEvent */
evt) {
var _this$options2 = this.options,
disabled = _this$options2.disabled,
dragging = _this$options2.dragging,
draggable = _this$options2.draggable,
stopPropagation = _this$options2.stopPropagation;
if (/mousedown|pointerdown/.test(evt.type) && evt.button !== 0 || disabled) return; // only left button and enabled
var touch = evt.touches && evt.touches[0] || evt.pointerType && evt.pointerType === 'touch' && evt;
var e = touch || evt; // Safari ignores further event handling after mousedown
if (!this.nativeDraggable && Safari && e.target && e.target.tagName.toUpperCase() === 'SELECT') return;
if (e.target === this.$el) return true;
if (typeof draggable === 'function') {
if (!draggable(e)) return true;
} else if (typeof draggable === 'string') {
if (!matches(e.target, draggable)) return true;
} else if (draggable !== undefined) {
throw new Error("draggable expected \"function\" or \"string\" but received \"".concat(_typeof(draggable), "\""));
}
}, {
key: "destroy",
value: function destroy() {
this._unbindEventListener();
this._resetState();
if (evt.preventDefault !== void 0) evt.preventDefault();
if (stopPropagation) evt.stopPropagation();
try {
if (document.selection) {
// Timeout neccessary for IE9
_nextTick(function () {
document.selection.empty();
});
} else {
window.getSelection().removeAllRanges();
} // 获取拖拽元素
var element = typeof dragging === 'function' ? dragging(e) : getElement(this.$el, e.target).el; // 不存在拖拽元素时不允许拖拽
if (!element) return true;
if (element.animated) return;
this.dragEl = element;
} catch (error) {
throw new Error(error);
}
}, {
key: "_onStart",
value: function _onStart(e) {
if (e.button !== 0) return true;
if (e.target === this.group) return true;
try {
// 获取拖拽元素
var element = this.dragging ? this.dragging(e) : e.target; // 不存在拖拽元素时不允许拖拽
window.sortableDndOnDown = true; // 获取当前元素在列表中的位置
if (!element) return true;
if (element.animated) return;
this.dragEl = element;
} catch (e) {
//
return true;
}
var _getElement = getElement(this.$el, this.dragEl),
index = _getElement.index,
el = _getElement.el,
rect = _getElement.rect,
offset = _getElement.offset;
this.isMousedown = true; // 获取当前元素在列表中的位置
if (!el || index < 0) return true; // 将拖拽元素克隆一份作为蒙版
var _utils$getElement = utils.getElement(this.group, this.dragEl),
index = _utils$getElement.index,
el = _utils$getElement.el,
rect = _utils$getElement.rect;
var ghostEl = this.dragEl.cloneNode(true);
this.ghost.init(ghostEl, rect);
this.ghost.set('x', rect.left);
this.ghost.set('y', rect.top);
this.differ._old_.rect = rect;
this.differ._old_.offset = offset;
this.differ._old_.node = this.dragEl;
this.calcXY = {
x: e.clientX,
y: e.clientY
};
if (!el || index < 0) return true; // 将拖拽元素克隆一份作为蒙版
this._onMoveEvents(touch);
var ghostEl = this.dragEl.cloneNode(true);
this.ghost.init(ghostEl, rect);
this.diff.old.rect = rect;
this.ghost.set('x', rect.left);
this.ghost.set('y', rect.top); // 记录拖拽移动时坐标
this._onUpEvents(touch);
},
_onMove: function _onMove(
/** Event|TouchEvent */
evt) {
if (evt.preventDefault !== void 0) evt.preventDefault(); // prevent scrolling
this.calcXY = {
x: e.clientX,
y: e.clientY
};
var touch = evt.touches && evt.touches[0];
var e = touch || evt;
var clientX = e.clientX,
clientY = e.clientY;
var target = touch ? document.elementFromPoint(clientX, clientY) : e.target;
var _this$options3 = this.options,
chosenClass = _this$options3.chosenClass,
stopPropagation = _this$options3.stopPropagation;
if (stopPropagation) evt.stopPropagation();
toggleClass(this.dragEl, chosenClass, true);
this.ghost.move();
if (!window.sortableDndOnDown) return;
if (clientX < 0 || clientY < 0) return;
window.sortableDndOnMove = true;
this.ghost.set('x', this.ghost.x + clientX - this.calcXY.x);
this.ghost.set('y', this.ghost.y + clientY - this.calcXY.y);
this.calcXY = {
x: clientX,
y: clientY
};
this.ghost.move(); // 判断边界值
this._onMoveEvents();
var rc = getRect(this.$el);
this._onUpEvents();
if (clientX < rc.left || clientX > rc.right || clientY < rc.top || clientY > rc.bottom) {
this.ghost.setStyle({
cursor: 'not-allowed'
});
return;
}
}, {
key: "_onMove",
value: function _onMove(e) {
this.ghost.move();
e.preventDefault();
if (!this.isMousedown) return;
if (e.clientX < 0 || e.clientY < 0) return;
document.body.style.cursor = 'grabbing';
this.isMousemove = true;
this.ghost.set('x', this.ghost.x + e.clientX - this.calcXY.x);
this.ghost.set('y', this.ghost.y + e.clientY - this.calcXY.y);
this.calcXY = {
x: e.clientX,
y: e.clientY
};
this.ghost.move();
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.group, e.target),
index = _utils$getElement2.index,
el = _utils$getElement2.el,
rect = _utils$getElement2.rect;
var left = rect.left,
right = rect.right,
top = rect.top,
bottom = rect.bottom;
if (!el || index < 0 || top < 0) return; // 加上当前滚动距离
var left = rect.left,
right = rect.right,
top = rect.top,
bottom = rect.bottom;
if (!el || index < 0) return;
if (top < 0 || top - this.ghost.rect.height / 3 < 0) return;
var _this$scrollEl = this.scrollEl,
scrollTop = _this$scrollEl.scrollTop,
scrollLeft = _this$scrollEl.scrollLeft;
var boundaryL = rc.left + scrollLeft;
var boundaryT = rc.top + scrollTop; // 如果目标元素超出当前可视区,不允许拖动
if (e.clientX > left && e.clientX < right && e.clientY > top && e.clientY < bottom) {
this.dropEl = el; // 拖拽前后元素不一致时交换
if (this.scrollEl !== this.$el && (rc.left < 0 || rc.top < 0)) {
if (rc.top < 0 && top < boundaryT || rc.left < 0 && left < boundaryL) return;
} else if (top < rc.top || left < rc.left) return;
if (this.dropEl !== this.dragEl) {
var dragRect = this.dragEl.getBoundingClientRect();
var dropRect = this.dropEl.getBoundingClientRect();
if (this.dropEl.animated) return;
if (clientX > left && clientX < right && clientY > top && clientY < bottom) {
this.dropEl = el; // 拖拽前后元素不一致时交换
if (utils.index(this.group, this.dragEl) < index) {
this.group.insertBefore(this.dragEl, this.dropEl.nextElementSibling);
} else {
this.group.insertBefore(this.dragEl, this.dropEl);
} // 设置动画
if (this.dropEl !== this.dragEl) {
if (this.dropEl.animated) return;
this.captureAnimationState();
var _offset = getOffset(this.dragEl); // 获取拖拽元素的 offset 值
// 优先比较 top 值,top 值相同再比较 left
utils.animate(this.dragEl, dragRect, this.animation);
utils.animate(this.dropEl, dropRect, this.animation);
this.diff.old.node = this.dragEl;
this.diff["new"].node = this.dropEl;
if (_offset.top < offset.top || _offset.left < offset.left) {
this.$el.insertBefore(this.dragEl, this.dropEl.nextElementSibling);
} else {
this.$el.insertBefore(this.dragEl, this.dropEl);
}
this.diff["new"].rect = this.dropEl.getBoundingClientRect();
this.animateRange();
this.differ._new_.node = this.dropEl;
this.differ._new_.rect = getRect(this.dropEl);
}
}
}, {
key: "_onDrop",
value: function _onDrop() {
this._offMoveEvents();
},
_onDrop: function _onDrop(
/** Event|TouchEvent */
evt) {
this._offMoveEvents();
this._offUpEvents();
this._offUpEvents();
document.body.style.cursor = '';
var _this$options4 = this.options,
dragEnd = _this$options4.dragEnd,
chosenClass = _this$options4.chosenClass,
stopPropagation = _this$options4.stopPropagation;
if (stopPropagation) evt.stopPropagation(); // 阻止事件冒泡
if (this.isMousedown && this.isMousemove) {
// 拖拽完成触发回调函数
if (this.dragEnd && typeof this.dragEnd === 'function') this.dragEnd(this.diff.old, this.diff["new"]);
}
toggleClass(this.dragEl, chosenClass, false);
this.isMousedown = false;
this.isMousemove = false;
this.diff.destroy();
this.ghost.destroy();
}
}, {
key: "_checkRange",
value: function _checkRange(e) {
var _this$group$getBoundi = this.group.getBoundingClientRect(),
top = _this$group$getBoundi.top,
left = _this$group$getBoundi.left,
right = _this$group$getBoundi.right,
bottom = _this$group$getBoundi.bottom;
if (window.sortableDndOnDown && window.sortableDndOnMove) {
// 重新获取一次拖拽元素的 offset 值作为拖拽完成后的 offset 值
this.differ._new_.offset = getOffset(this.dragEl); // 拖拽完成触发回调函数
if (e.clientX < left || e.clientX > right || e.clientY < top || e.clientY > bottom) {
document.body.style.cursor = 'not-allowed';
}
}
}, {
key: "_resetState",
value: function _resetState() {
this.isMousedown = false;
this.isMousemove = false;
this.dragEl = null;
this.dropEl = null;
this.ghost.destroy();
this.diff = new Diff();
}
}, {
key: "_bindEventListener",
value: function _bindEventListener() {
this._onStart = this._onStart.bind(this);
this._onMove = this._onMove.bind(this);
this._onDrop = this._onDrop.bind(this);
var _this$differ = this.differ,
_old_ = _this$differ._old_,
_new_ = _this$differ._new_; // 通过 offset 比较是否进行了元素交换
if (this.supportPointer) {
utils.on(this.group, 'pointerdown', this._onStart);
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(_old_, _new_, changed);
} else {
utils.on(this.group, 'mousedown', this._onStart);
throw new Error("Sortable-dnd Error: dragEnd expected \"function\" but received \"".concat(_typeof(dragEnd), "\""));
}
}
}, {
key: "_onMoveEvents",
value: function _onMoveEvents() {
if (this.supportPointer) {
utils.on(document, 'pointermove', this._onMove);
} else {
utils.on(document, 'mousemove', this._onMove);
}
this.differ.destroy();
this.ghost.destroy();
this._removeWindowState();
},
// -------------------------------- auto destroy ----------------------------------
_handleDestroy: function _handleDestroy() {
var _this = this;
var observer = null;
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
if (MutationObserver) {
var ownerDocument = this.options.ownerDocument;
if (!ownerDocument) return;
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
});
}
}, {
key: "_onUpEvents",
value: function _onUpEvents() {
if (this.supportPointer) {
utils.on(document, 'pointerup', this._onDrop);
} else {
utils.on(document, 'mouseup', this._onDrop);
}
}
}, {
key: "_unbindEventListener",
value: function _unbindEventListener() {
utils.off(this.group, 'mousedown', this._onStart);
utils.off(this.group, 'pointerdown', this._onStart);
}
}, {
key: "_offMoveEvents",
value: function _offMoveEvents() {
utils.off(document, 'mousemove', this._onMove);
utils.off(document, 'pointermove', this._onMove);
}
}, {
key: "_offUpEvents",
value: function _offUpEvents() {
utils.off(document, 'mouseup', this._onDrop);
utils.off(document, 'pointerup', this._onDrop);
}
}]);
return Sortable;
}();
window.onbeforeunload = function () {
if (observer) observer.disconnect();
observer = null;
_this._unbindEventListener();
_this._resetState();
};
},
// -------------------------------- reset state ----------------------------------
_resetState: function _resetState() {
this.dragEl = null;
this.dropEl = null;
this.ghost.destroy();
this.differ.destroy();
this.calcXY = {
x: 0,
y: 0
};
this._removeWindowState();
},
_removeWindowState: function _removeWindowState() {
window.sortableDndOnDown = null;
window.sortableDndOnMove = null;
delete window.sortableDndOnDown;
delete window.sortableDndOnMove;
}
};
return Sortable;

@@ -731,3 +1141,3 @@ });

function Virtual(props, ref) {
const { header, footer, children, dataSource = [], dataKey, keeps = 30, size = 50, height = '100%', delay = 10, draggable = true, draggableOnly = true, dragStyle = { backgroundImage: MASKIMAGE } } = props;
const { header, footer, children, dataSource = [], dataKey, keeps = 30, size = 50, height = '100%', delay = 10, disabled = false, draggable = undefined, dragging = undefined, ghostClass = '', ghostStyle = { backgroundImage: MASKIMAGE }, chosenClass = '', animation = 150 } = props;
// =============================== State ===============================

@@ -970,25 +1380,13 @@ const [cloneList, setCloneList] = React.useState([]);

destroyDraggable();
dragRef.current = new sortable({
group: groupRef.current,
ghostStyle: dragStyle,
dragging: (e) => {
const draggable = e.target.getAttribute('draggable');
if (draggableOnly && !draggable)
return null;
if (props.dragElement) {
return props.dragElement(e, groupRef.current);
}
else {
let result = e.target;
while ([].indexOf.call(groupRef.current.children, result) < 0) {
result = result.parentNode;
}
return result;
}
},
dragEnd: (pre, cur) => {
dragRef.current = new sortable(groupRef.current, {
disabled,
animation,
draggable,
dragging,
ghostStyle,
ghostClass,
chosenClass,
dragEnd: (pre, cur, changed) => {
if (!(pre && cur))
return;
if (pre.rect.top === cur.rect.top)
return;
const dragState = {

@@ -1011,8 +1409,10 @@ oldNode: pre.node, oldItem: null, oldIndex: null,

const newArr = [...dragList.current];
newArr.splice(dragState.oldIndex, 1);
newArr.splice(dragState.newIndex, 0, dragState.oldItem);
setCloneList(() => [...newArr]);
dragList.current = [...newArr];
if (changed) {
newArr.splice(dragState.oldIndex, 1);
newArr.splice(dragState.newIndex, 0, dragState.oldItem);
setCloneList(() => [...newArr]);
dragList.current = [...newArr];
}
const callback = props[CALLBACKS.dragend];
callback && callback(newArr);
callback && callback(newArr, { ...pre, item: dragState.oldItem, index: dragState.oldIndex }, { ...pre, item: dragState.newItem, index: dragState.newIndex }, changed);
}

@@ -1019,0 +1419,0 @@ });

{
"name": "react-virtual-drag-list",
"version": "2.2.0",
"description": "支持拖拽排序的虚拟列表组件",
"version": "2.3.0",
"description": "A virtual scrolling list component that can be sorted by dragging",
"main": "dist/draglist.js",

@@ -22,2 +22,3 @@ "files": [

"drag",
"drop",
"react",

@@ -37,3 +38,3 @@ "virtual",

"dependencies": {
"sortable-dnd": "^0.0.6"
"sortable-dnd": "^0.1.0"
},

@@ -40,0 +41,0 @@ "devDependencies": {

@@ -39,4 +39,4 @@ <p>

}
const handleDragEnd = (arr) => {
console.log(arr, 'new arr after drag end')
const handleDragEnd = (arr, _old, _new, changed) => {
console.log(arr, _old, _new, changed, 'new state after drag end')
}

@@ -71,23 +71,26 @@

| **Prop** | **Type** | **Required?** | **Description** | **Default** |
|-----------------|------------|------------|------------------|------------------|
| `dataKey` | String | ✓ | the unique identifier of each piece of data, in the form of `'a.b.c'` | - |
| `dataSource` | Array | ✓ | data list | `[]` |
| `size` | Number | ✓ | estimated height of each row | 50 |
| `keeps` | Number | | the number of lines rendered by the virtual scroll | 30 |
| `delay` | Number | | Delay time of debounce function | 10 |
| `draggable` | Boolean | | whether to support drag and drop. You need to specify a draggable element and set the `draggable` attribute for it | `true` |
| `draggableOnly` | Boolean | | Whether to drag and drop only elements with the draggable attribute set. When true, selecting the parent element will not produce a dragging effect | true |
| `dragElement` | Function | | The function that selects the dragged element, **must have a return value with a dom node**, has two parameters: e(the currently selected element), parent(the parent node of the list) | - |
| `header` | JSX.Element| | top of list | - |
| `footer` | JSX.Element| | bottom of list | - |
| `v-top` | Function | | callback function that fires when scrolling to the top | - |
| `v-bottom` | Function | | callback function that fires when scrolling to the bottom | - |
| `v-dragend` | Function | | event when drag is complete | - |
| `dragStyle` | Object | | mask style while dragging | - |
| `itemStyle` | Object | | style for each line | - |
| `itemClass` | String | | class for each line | - |
| **Prop** | **Type** | **Required?** | **Description** | **Default** |
|-----------------|--------------|---------------|------------------|------------------|
| `dataKey` | `String` | ✓ | the unique identifier of each piece of data, in the form of `'a.b.c'` | - |
| `dataSource` | `Array` | ✓ | data list | `[]` |
| `size` | `Number` | ✓ | estimated height of each row | `50` |
| `keeps` | `Number` | | the number of lines rendered by the virtual scroll | `30` |
| `delay` | `Number` | | Delay time of debounce function | `10` |
| `header` | `JSX.Element`| | top of list | - |
| `footer` | `JSX.Element`| | bottom of list | - |
| `v-top` | `Function` | | callback function that fires when scrolling to the top | - |
| `v-bottom` | `Function` | | callback function that fires when scrolling to the bottom | - |
| `v-dragend` | `Function` | | event when drag is complete | - |
| `itemStyle` | `Object` | | style for each line | `{}` |
| `itemClass` | `String` | | class for each line | `''` |
| `disabled` | `Boolean` | | Disables the sortable if set to true | `false` |
| `animation` | `Number` | | Drag-and-drop's animation delay | `150` |
| `draggable` | `Function/String` | | Specifies which items inside the element should be draggable, the function type must return a boolean | `undefined` |
| `dragging` | `Function` | | Specifies the drag element, which must return an HTMLElement, such as `(e) => e.target` | `undefined` |
| `ghostStyle` | `Object` | | The style of the mask element when dragging | `{}` |
| `ghostClass` | `String` | | The class of the mask element when dragging | `''` |
| `chosenClass` | `String` | | The class of the selected element when dragging | `''` |
## Methods
Use the methods exposed in the component by setting `ref`, like this:
Use the methods exposed in the component by setting `ref`
```jsx

@@ -94,0 +97,0 @@ ...

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc