UNPKG

@fesjs/fes-design

Version:
425 lines (416 loc) 13 kB
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 };