@fesjs/fes-design
Version:
fes-design for PC
425 lines (416 loc) • 13 kB
JavaScript
import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
import { isFunction } from 'lodash-es';
import { reactive, watch, nextTick } from 'vue';
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
const UPDATE_MODEL_EVENT = 'update:modelValue';
const DRAG_START_EVENT = 'dragstart';
const DRAG_END_EVENT = 'dragend';
let dragSourceCxt;
let sourceBackup;
function pushAt() {
let array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let value = arguments.length > 1 ? arguments[1] : undefined;
let index = arguments.length > 2 ? arguments[2] : undefined;
if (index < 0) {
return array.unshift(value);
}
if (index >= array.length) {
return array.push(value);
}
const evens = array.splice(index, array.length - index);
array.push(value, ...evens);
}
/**
* 数组移动
*/
function arrayMove() {
let array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : -1;
let value = arguments.length > 3 ? arguments[3] : undefined;
if (source < 0) {
// add target
pushAt(array, value, target);
return [value];
}
if (target < 0) {
// remove source
return array.splice(source, 1);
}
const evens = array.splice(source, 1);
pushAt(array, evens[0], target);
return evens;
}
// 根据目标元素
function findElement(target, parent) {
if (!parent || !target) {
return;
}
for (let index = 0; index < parent.children.length; index++) {
const el = parent.children[index];
if (el.contains(target)) {
return {
el: el,
index
};
}
}
}
class DraggableItem {
constructor() {
_defineProperty(this, "draggable", null);
_defineProperty(this, "first", {
x: 0,
y: 0
});
_defineProperty(this, "last", {
x: 0,
y: 0
});
_defineProperty(this, "style", {
transition: '',
transform: '',
opacity: null
});
_defineProperty(this, "elStyle", {});
}
// 静态样式
setDraggable() {
let draggable = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
this.draggable = draggable || null;
if (!draggable) {
this.style.opacity = null;
}
}
setOpacity() {
let opacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.4;
this.style.opacity = opacity;
}
}
const useDraggable = (containerRef, propsRef, ctx) => {
const draggableItems = reactive([]);
const current = {};
const backup = {};
const emit = (ctx === null || ctx === void 0 ? void 0 : ctx.emit) || (() => null);
let mousedownEvent;
const nextTickQueue = [];
const newNextTick = fn => {
if (propsRef.value.isDirective) {
isFunction(fn) && nextTickQueue.push(fn);
} else {
nextTick(fn);
}
};
const onUpdated = () => {
while (nextTickQueue.length) {
nextTickQueue.shift()();
}
};
const FLIP = isFirst => {
if (!containerRef.value) {
return;
}
for (let index = 0; index < containerRef.value.children.length; index++) {
const node = containerRef.value.children[index];
if (!draggableItems[index]) {
draggableItems[index] = new DraggableItem();
const elStyle = {};
for (let index = 0; index < node.style.length; index++) {
const key = node.style[index];
elStyle[key] = node.style[key];
}
draggableItems[index].elStyle = elStyle;
}
const item = draggableItems[index];
const rect = node.getBoundingClientRect();
if (isFirst) {
// First
item.first.x = rect.left;
item.first.y = rect.top;
} else {
// Last
item.last.x = rect.left;
item.last.y = rect.top;
// Invert
item.style.transform = `translate3d(${item.first.x - item.last.x}px, ${item.first.y - item.last.y}px , 0)`;
item.style.transition = '';
}
}
if (isFirst) {
return;
}
requestAnimationFrame(() => {
// Play
draggableItems.forEach(item => {
item.style.transition = 'transform 200ms ease';
item.style.transform = '';
});
});
};
// 从backup中恢复状态
const revertStatus = () => {
FLIP(true);
backup.list.forEach((item, index) => {
var _backup$draggableItem;
draggableItems[index] = (_backup$draggableItem = backup.draggableItems) === null || _backup$draggableItem === void 0 ? void 0 : _backup$draggableItem[index];
});
emit(UPDATE_MODEL_EVENT, backup.list);
newNextTick(() => {
FLIP(false);
});
};
const computeDropTarget = event => {
const target = findElement(event.target, containerRef.value);
if (!target) {
if (event.target === containerRef.value) {
// 放最后
return {
el: null,
index: propsRef.value.list.length
};
}
return null;
}
return target;
};
const resetDragWhenEnd = event => {
var _current$drag, _sourceBackup;
const index = (_current$drag = current.drag) === null || _current$drag === void 0 ? void 0 : _current$drag.index;
if (event && index >= 0) {
emit(DRAG_END_EVENT, event, propsRef.value.list[index], index);
}
backup.list = null;
if ((_sourceBackup = sourceBackup) !== null && _sourceBackup !== void 0 && _sourceBackup.list) {
sourceBackup.list = null;
}
backup.draggableItems = [];
backup.index = -1;
current.drag = null;
current.animationEnd = true;
current.isDropOverItem = false;
draggableItems.forEach(item => {
item.setDraggable(false);
item.style.transition = null;
});
};
const shareSource = () => {
dragSourceCxt = {
propsRef,
current,
draggableItems,
FLIP,
emit,
resetDragWhenEnd,
newNextTick
};
};
const backupStatus = () => {
var _current$drag2;
backup.list = [...propsRef.value.list];
backup.draggableItems = [...draggableItems];
backup.index = (_current$drag2 = current.drag) === null || _current$drag2 === void 0 ? void 0 : _current$drag2.index;
backup.revertStatus = revertStatus;
backup.resetDragWhenEnd = resetDragWhenEnd;
};
const onAnimationEnd = () => {
current.animationEnd = true;
current.isDropOverItem = false;
};
/** 拖拽开始 */
const onDragstart = event => {
mousedownEvent = event;
const {
disabled,
droppable,
list
} = propsRef.value;
if (disabled) {
return;
}
current.drag = findElement(event.target, containerRef.value);
if (!current.drag) {
return;
}
const index = current.drag.index;
const item = draggableItems[index];
onAnimationEnd(); // 动画结束
item.setDraggable(true); // 设置选中元素为可拖拽
backupStatus();
if (droppable) {
shareSource(); // 跨容器,当前即是源,分享其他方法给目标容器
sourceBackup = _objectSpread({}, backup);
}
emit(DRAG_START_EVENT, event, list[index], index);
};
const onMousemove = event => {
var _current$drag3;
if (!mousedownEvent) {
return;
}
const item = draggableItems[current === null || current === void 0 || (_current$drag3 = current.drag) === null || _current$drag3 === void 0 ? void 0 : _current$drag3.index];
if (item && (Math.abs(event.x - mousedownEvent.x) || Math.abs(event.y - mousedownEvent.y))) {
item.setOpacity();
}
};
const onDragover = event => {
event.preventDefault();
const {
droppable,
list
} = propsRef.value;
const {
animationEnd,
isDropOverItem,
drag
} = current;
const s = dragSourceCxt;
// 如果动画没结束,就直接结束
if (!animationEnd || s && !s.current.animationEnd) {
return;
}
// 目标位置
const drop = computeDropTarget(event);
if (!drop) {
return;
}
let listEvens; // 差值
let draggableItemEvens; // 差值
let dragIndex = -1;
if (droppable && !s) {
return;
} // 跨容器,不存在source
if (droppable && s && !containerRef.value.contains(s.current.drag.el)) {
// 从source容器拖拽到当前容器,source容器移除
s.FLIP(true);
current.isDropOverItem = !!drop.el;
const sDragIndex = s.current.drag.index;
if (!backup.list) {
// 跨框时,记录当前首次状态
backupStatus();
backup.index = sDragIndex;
}
listEvens = arrayMove(s.propsRef.value.list, sDragIndex)[0];
draggableItemEvens = arrayMove(s.draggableItems, sDragIndex)[0];
s.current.animationEnd = false; // 动画开始
s.current.drag = null;
s.emit(UPDATE_MODEL_EVENT, s.propsRef.value.list);
} else {
// 拖拽目标在当前容器上
dragIndex = drag.index;
if (drop.index < 0) {
drop.index = 0;
}
if (drop.index >= list.length) {
drop.index = list.length - 1;
}
if (dragIndex === drop.index || isDropOverItem) {
return;
}
}
// 更新当前容器数据
FLIP(true);
arrayMove(list, dragIndex, drop.index, listEvens);
arrayMove(draggableItems, dragIndex, drop.index, draggableItemEvens);
emit(UPDATE_MODEL_EVENT, list);
current.animationEnd = false; // 动画开始
if (droppable && s) {
s.newNextTick(() => {
s.FLIP(false);
shareSource();
});
}
newNextTick(() => {
current.drag = {
index: drop.index === -1 ? 0 : drop.index,
el: null
};
current.drag.el = containerRef.value.children[drop.index];
FLIP(false);
});
};
const checkDragEnd = async () => {
var _current$drag4;
const {
beforeDragend,
list
} = propsRef.value;
const index = current === null || current === void 0 || (_current$drag4 = current.drag) === null || _current$drag4 === void 0 ? void 0 : _current$drag4.index;
if (isFunction(beforeDragend) && index >= 0) {
var _sourceBackup2;
// 校验是否需要可以放置
let isTrue = false;
let drag = {
list: backup.list,
index: backup.index,
item: backup.list[backup.index],
resultList: list // 预期结果
};
const drop = _objectSpread(_objectSpread({}, drag), {}, {
index
});
if ((_sourceBackup2 = sourceBackup) !== null && _sourceBackup2 !== void 0 && _sourceBackup2.list && backup.list !== sourceBackup.list) {
const resultList = [...sourceBackup.list];
arrayMove(resultList, sourceBackup.index);
drag = {
list: sourceBackup.list,
index: sourceBackup.index,
item: sourceBackup.list[sourceBackup.index],
resultList
};
drop.item = drag.item;
}
try {
isTrue = await beforeDragend(drag, drop);
} catch (error) {
console.error(error);
isTrue = false;
}
if (!isTrue) {
var _sourceBackup3;
revertStatus();
(_sourceBackup3 = sourceBackup) === null || _sourceBackup3 === void 0 || _sourceBackup3.revertStatus();
}
}
};
const onDragend = async event => {
mousedownEvent = null;
const {
droppable
} = propsRef.value;
await checkDragEnd();
if (droppable && dragSourceCxt) {
var _sourceBackup4;
(_sourceBackup4 = sourceBackup) === null || _sourceBackup4 === void 0 || _sourceBackup4.resetDragWhenEnd(event);
dragSourceCxt = null;
}
resetDragWhenEnd(event);
};
resetDragWhenEnd();
watch(containerRef, () => FLIP(true), {
immediate: true
});
watch(propsRef, () => {
if (draggableItems.length !== propsRef.value.list.length) {
if (propsRef.value.isDirective) {
FLIP(true); // 指令的list更新在updated之后
} else {
nextTick(() => FLIP(true));
}
}
}, {
immediate: true,
deep: true
});
return {
onAnimationEnd,
onDragover,
onDragstart,
onDragend,
draggableItems,
nextTickQueue,
onMousemove,
onUpdated
};
};
export { DRAG_END_EVENT, DRAG_START_EVENT, DraggableItem, UPDATE_MODEL_EVENT, useDraggable };