UNPKG

@jambonn/vue-nested-draggable

Version:
1,907 lines (1,647 loc) 55.1 kB
import draggableHelper from 'draggable-helper'; function isPropTrue(v) { return v || v === ''; } //====== Helper-js ====// /** * Remove item from array. return removed count * @param arr * @param v * @returns {number} */ function arrayRemove(arr, v) { let index; let count = 0; while ((index = arr.indexOf(v)) > -1) { arr.splice(index, 1); count++; } return count; } /** * Rand item in array * @param arr * @returns {*} */ function randChoice(arr) { return arr[randInt(0, arr.length - 1)]; } /** * Rand int in range, including min and max * @param min * @param max * @returns {number} */ function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } /** * Generate random string * @returns {string} */ function randString() { const len = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 8; const seeds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let r = ''; for (let i = 0; i < len; i++) { r += randChoice(seeds); } return r; } /** * http://www.51xuediannao.com/javascript/getBoundingClientRect.html * @param el * @returns {{top: number, left: number, bottom: number, width: (number), x: number, y: number, right: number, height: (number)}} */ function getBoundingClientRect(el) { const xy = el.getBoundingClientRect(); const top = xy.top - document.documentElement.clientTop, bottom = xy.bottom, left = xy.left - document.documentElement.clientLeft, right = xy.right, width = xy.width || right - left, height = xy.height || bottom - top; return { top: top, right: right, bottom: bottom, left: left, width: width, height: height, x: left, y: top }; } /** * refer: https://stackoverflow.com/questions/871399/cross-browser-method-for-detecting-the-scrolltop-of-the-browser-window * @returns {{top: number, left: number}} */ function getScroll() { if (typeof pageYOffset != 'undefined') { //most browsers except IE before #9 return { top: pageYOffset, left: pageXOffset }; } else { const B = document.body; //IE 'quirks' let D = document.documentElement; //IE with doctype D = D.clientHeight ? D : B; return { top: D.scrollTop, left: D.scrollLeft }; } } /** * refer: https://gist.github.com/aderaaij/89547e34617b95ac29d1 * @param el * @returns {{x: number, y: number}} */ function getOffset(el) { const rect = getBoundingClientRect(el); const scroll = getScroll(); return { x: rect.left + scroll.left, y: rect.top + scroll.top }; } function binarySearch(arr, callback) { let opt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; opt = Object.assign({ start: 0, end: arr.length - 1, maxTimes: 1000 }, opt); let _opt = opt, start = _opt.start, end = _opt.end; let _opt2 = opt, returnNearestIfNoHit = _opt2.returnNearestIfNoHit, maxTimes = _opt2.maxTimes; let midNum; let mid; if (start == null) { start = 0; end = arr.length - 1; } let i = 0; let r; while (start >= 0 && start <= end) { if (i >= maxTimes) { throw Error('binarySearch: loop times is over '.concat(maxTimes, ', you can increase the limit.')); } midNum = Math.floor((end - start) / 2 + start); mid = arr[midNum]; r = callback(mid, i); if (r > 0) { end = midNum - 1; } else if (r < 0) { start = midNum + 1; } else { return { index: midNum, value: mid, count: i + 1, hit: true }; } i++; } return returnNearestIfNoHit ? { index: midNum, value: mid, count: i + 1, hit: false, greater: r > 0 } : null; } /** * source: http://youmightnotneedjquery.com/ * @param el * @param className * @returns {boolean} */ function hasClass(el, className) { if (el.classList) { return el.classList.contains(className); } else { return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className); } } //===== Tree helper =====// function _changeParent(item, parent) { const childrenKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'children'; const parentKey = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'parent'; // remove item from original list if (item[parentKey]) { arrayRemove(item[parentKey][childrenKey], item); } item[parentKey] = parent; } function insertBefore(item, target) { const childrenKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'children'; const parentKey = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'parent'; if (item === target) { return; } const siblings = target[parentKey][childrenKey]; let index = siblings.indexOf(target); if (siblings[index - 1] !== item) { if (item[parentKey] === target[parentKey]) { arrayRemove(siblings, item); index = siblings.indexOf(target); } else { _changeParent(item, target[parentKey]); } siblings.splice(index, 0, item); } } function insertAfter(item, target) { const childrenKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'children'; const parentKey = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'parent'; if (item === target) { return; } const targetParent = target[parentKey]; const siblings = targetParent[childrenKey]; let index = siblings.indexOf(target); if (siblings[index + 1] !== item) { if (item[parentKey] === target[parentKey]) { arrayRemove(siblings, item); index = siblings.indexOf(target); } else { _changeParent(item, target[parentKey]); } siblings.splice(index + 1, 0, item); } } function prependTo(item, target) { const childrenKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'children'; if (item === target) { throw "can't prepend to self"; } const targetChildren = target[childrenKey]; if (targetChildren[0] !== item) { _changeParent(item, target); targetChildren.splice(0, 0, item); } } function appendTo(item, target) { const childrenKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'children'; if (item === target) { throw "can't append to self"; } const targetChildren = target[childrenKey]; const targetChildrenLast = targetChildren[targetChildren.length - 1]; if (targetChildrenLast !== item) { _changeParent(item, target); targetChildren.push(item); } } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError('Invalid attempt to spread non-iterable instance'); } function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === '[object Arguments]') return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { let arr2; for (let i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } function _typeof(obj) { if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj; }; } return _typeof(obj); } function isArray(v) { return Object.prototype.toString.call(v) === '[object Array]'; } function breadthFirstSearch(obj, handler) { const reverse = arguments.length > 3 ? arguments[3] : undefined; const rootChildren = isArray(obj) ? obj : [obj]; // let stack = rootChildren.map(function (v, i) { return { item: v, index: i }; }); if (reverse) { stack.reverse(); } const _loop = function _loop() { const _stack$shift = stack.shift(), item = _stack$shift.item, index = _stack$shift.index, parent = _stack$shift.parent; const r = handler(item, index, parent); if (r === false) { // stop return { v: void 0 }; } else if (r === 'skip children') { return 'continue'; } else if (r === 'skip siblings') { stack = stack.filter(function (v) { return v.parent !== parent; }); } if (item.children) { let _stack; let children = item.children; if (reverse) { children = children.slice(); children.reverse(); } const pushStack = children.map(function (v, i) { return { item: v, index: i, parent: item }; }); (_stack = stack).push.apply(_stack, _toConsumableArray(pushStack)); } }; while (stack.length) { const _ret = _loop(); switch (_ret) { case 'continue': continue; default: if (_typeof(_ret) === 'object') return _ret.v; } } } function depthFirstSearch(obj, handler) { const childrenKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'children'; const reverse = arguments.length > 3 ? arguments[3] : undefined; const rootChildren = isArray(obj) ? obj : [obj]; // const StopException = function StopException() {}; const func = function func(children, parent) { if (reverse) { children = children.slice(); children.reverse(); } const len = children.length; for (let i = 0; i < len; i++) { const item = children[i]; const r = handler(item, i, parent); if (r === false) { // stop throw new StopException(); } else if (r === 'skip children') { continue; } else if (r === 'skip siblings') { break; } if (item[childrenKey] != null) { func(item[childrenKey], item); } } }; try { func(rootChildren); } catch (e) { if (e instanceof StopException) ; else { throw e; } } } // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // var script = { name: 'TreeNode', props: { data: {}, store: {}, allowAddItem: { type: Boolean, default: false }, addItemText: { type: String, default: 'Add item' }, level: { default: 0 } // readonly }, data() { return { vm: this }; }, computed: { childrenLevel() { return this.level + 1; }, isRoot() { return this.data && this.data.isRoot; }, childrenVisible() { const { data } = this; return this.isRoot || data && data.children && data.children.length && data.open; }, innerBackStyle() { const r = { marginBottom: this.store.space + 'px' }; if (!this.isRoot && this.level > 1) { r.paddingLeft = (this.level - 1) * this.store.indent + 'px'; } return r; }, actionStyle() { const r = {}; if (!this.isRoot && this.level > 0) { r.paddingLeft = this.level * this.store.indent + 'px'; } return r; } }, watch: { data: { immediate: true, handler(data) { if (data) { data._vm = this; if (!data._treeNodePropertiesCompleted && !data.isRoot) { this.store.completeNode(data, this.$parent.data); } } } } }, methods: { onClickAddItem() { this.$emit('addItem', this.data); }, onClickChildItem(data) { this.$emit('addItem', data); } } }; function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { if (typeof shadowMode !== 'boolean') { createInjectorSSR = createInjector; createInjector = shadowMode; shadowMode = false; } // Vue.extend constructor export interop. const options = typeof script === 'function' ? script.options : script; // render functions if (template && template.render) { options.render = template.render; options.staticRenderFns = template.staticRenderFns; options._compiled = true; // functional template if (isFunctionalTemplate) { options.functional = true; } } // scopedId if (scopeId) { options._scopeId = scopeId; } let hook; if (moduleIdentifier) { // server build hook = function (context) { // 2.3 injection context = context || // cached call (this.$vnode && this.$vnode.ssrContext) || // stateful (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional // 2.2 with runInNewContext: true if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__; } // inject component styles if (style) { style.call(this, createInjectorSSR(context)); } // register component module identifier for async chunk inference if (context && context._registeredComponents) { context._registeredComponents.add(moduleIdentifier); } }; // used by ssr in case component is cached and beforeCreate // never gets called options._ssrRegister = hook; } else if (style) { hook = shadowMode ? function (context) { style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); } : function (context) { style.call(this, createInjector(context)); }; } if (hook) { if (options.functional) { // register for functional component in vue file const originalRender = options.render; options.render = function renderWithStyleInjection(h, context) { hook.call(context); return originalRender(h, context); }; } else { // inject component registration as beforeCreate hook const existing = options.beforeCreate; options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; } } return script; } /* script */ const __vue_script__ = script; /* template */ var __vue_render__ = function () { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c('div', { staticClass: "tree-node", class: [_vm.data.active ? _vm.store.activatedClass : '', _vm.data.open ? _vm.store.openedClass : '', _vm.data.class], style: _vm.data.style, attrs: { "id": _vm.data._id } }, [!_vm.isRoot ? _vm._t("node-inner-back", [_c('div', { staticClass: "tree-node-inner-back", class: [_vm.data.innerBackClass], style: [_vm.innerBackStyle, _vm.data.innerBackStyle] }, [_c('div', { staticClass: "tree-node-inner", class: [_vm.data.innerClass], style: [_vm.data.innerStyle] }, [_vm._t("default", null, { "data": _vm.data, "store": _vm.store, "vm": _vm.vm })], 2)])], { "styleObj": _vm.innerBackStyle, "data": _vm.data, "store": _vm.store, "vm": _vm.vm }) : _vm._e(), _vm._v(" "), _c('transition', { attrs: { "name": _vm.store.childrenTransitionName } }, [_vm.childrenVisible ? _c('div', { staticClass: "tree-node-children" }, [_vm._l(_vm.data.children, function (child) { return _c('TreeNode', { key: child._id, attrs: { "data": child, "store": _vm.store, "level": _vm.childrenLevel, "allow-add-item": _vm.allowAddItem, "add-item-text": _vm.addItemText }, on: { "addItem": _vm.onClickChildItem }, scopedSlots: _vm._u([{ key: "default", fn: function (props) { return [_vm._t("default", null, { "data": props.data, "store": props.store, "vm": props.vm })]; } }, { key: "node-inner-back", fn: function (props) { return _vm.store.customInnerBack ? [_vm._t("node-inner-back", null, { "styleObj": props.styleObj, "data": props.data, "store": props.store, "vm": props.vm })] : undefined; } }], null, true) }); }), _vm._v(" "), _vm.allowAddItem ? _c('div', { staticClass: "tree-node-action", style: [_vm.actionStyle], attrs: { "data-level": _vm.childrenLevel } }, [_c('button', { staticClass: "tree-node-add", on: { "click": function ($event) { $event.preventDefault(); return _vm.onClickAddItem($event); } } }, [_vm._v("\n " + _vm._s(_vm.addItemText) + "\n ")])]) : _vm._e()], 2) : _vm._e()])], 2); }; var __vue_staticRenderFns__ = []; /* style */ const __vue_inject_styles__ = undefined; /* scoped */ const __vue_scope_id__ = undefined; /* module identifier */ const __vue_module_identifier__ = undefined; /* functional template */ const __vue_is_functional_template__ = false; /* style inject */ /* style inject SSR */ /* style inject shadow dom */ const __vue_component__ = /*#__PURE__*/normalizeComponent({ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined); function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } class Cache { constructor() { _defineProperty(this, "store", {}); } has(name) { return this.store.hasOwnProperty(name); } remember(name, getter) { if (!this.has(name)) { this.store[name] = { value: getter() }; } return this.store[name].value; } forget(name) { if (name) { if (this.has(name)) { delete this.store[name]; } } else { this.store = {}; } } } function attachCache(obj, cache, toCache) { for (const key in toCache) { if (toCache.hasOwnProperty(key)) { Object.defineProperty(obj, key, { get() { return cache.remember(key, () => toCache[key].call(this)); } }); } } } // from https://gist.github.com/iddan/54d5d9e58311b0495a91bf06de661380 if (!document.elementsFromPoint) { document.elementsFromPoint = elementsFromPoint; } function elementsFromPoint(x, y) { var parents = []; var parent = void 0; do { if (parent !== document.elementFromPoint(x, y)) { parent = document.elementFromPoint(x, y); parents.push(parent); parent.style.pointerEvents = 'none'; } else { parent = false; } } while (parent); parents.forEach(function (parent) { return parent.style.pointerEvents = 'all'; }); return parents; } function getTreeByPoint(x, y, trees) { const els = document.elementsFromPoint(x, y); let treeEl; let nodeEl; const betweenEls = []; for (const el of els) { if (!nodeEl) { if (hasClass(el, 'tree-node')) { nodeEl = el; } } else { // console.log(el); if (hasClass(el, 'tree')) { treeEl = el; break; } betweenEls.push(el); } } if (treeEl) { // is target tree is another tree, and be covered by other element, like modal, popup let covered = false; if (!isParent(nodeEl, treeEl)) { // cross tree for (const el of betweenEls) { if (!isParent(el, treeEl)) { covered = true; break; } } } // if (!covered) { return trees.find(v => v.$el === treeEl); } } } function isParent(child, parent) { let cur = child; while (cur) { cur = cur.parentNode; if (cur === parent) { return true; } } } const targets = { nothing: info => {}, after: info => { insertDplhAfterTo(info.dplh, info.targetNode); }, before: info => { if (isNodeDroppable(info.targetNode.parent)) { insertBefore(info.dplh, info.targetNode); } else { insertDplhAfterTo(info.dplh, info.targetNode.parent); } }, append: info => { if (isNodeDroppable(info.targetNode)) { appendTo(info.dplh, info.targetNode); if (!info.targetNode.open) info.store.toggleOpen(info.targetNode); } else { insertDplhAfterTo(info.dplh, info.targetNode); } }, prepend: info => { if (isNodeDroppable(info.targetNode)) { prependTo(info.dplh, info.targetNode); if (!info.targetNode.open) info.store.toggleOpen(info.targetNode); } else { insertDplhAfterTo(info.dplh, info.targetNode); } }, 'after target parent': info => { insertDplhAfterTo(info.dplh, info.targetNode.parent); }, // append to prev sibling 'append prev': info => { if (isNodeDroppable(info.targetPrev)) { appendTo(info.dplh, info.targetPrev); if (!info.targetPrev.open) info.store.toggleOpen(info.targetPrev); } else { insertDplhAfterTo(info.dplh, info.targetPrev); } }, // append to current tree 'append current tree': info => { if (isNodeDroppable(info.currentTree.rootData)) { appendTo(info.dplh, info.currentTree.rootData); } } }; function insertDplhAfterTo(dplh, targetNode) { if (!targetNode) { return false; } else { const closest = findParent(targetNode, node => node.parent && isNodeDroppable(node.parent)); if (closest) { insertAfter(dplh, closest); } else { return false; } } return true; } function isNodeDraggable(node) { if (!draggableIds.hasOwnProperty(node._id)) { let r; if (node.hasOwnProperty('draggable')) { r = node.draggable; } else if (node.parent) { r = isNodeDraggable(node.parent); } else { r = true; } draggableIds[node._id] = r; } return draggableIds[node._id]; } function isNodeDroppable(node) { if (!droppableIds.hasOwnProperty(node._id)) { let r; if (node.hasOwnProperty('droppable')) { r = node.droppable; } else if (node.parent) { r = isNodeDroppable(node.parent); } else { r = true; } droppableIds[node._id] = r; } return droppableIds[node._id]; } // find child, excluding dragging node default function findChild(info, children, handler, reverse) { const len = children.length; if (reverse) { for (let i = len - 1; i >= 0; i--) { const item = children[i]; // excluding dragging node if (item !== info.node) { if (handler(item, i)) { return item; } } } } else { for (let i = 0; i < len; i++) { const item = children[i]; // excluding dragging node if (item !== info.node) { if (handler(item, i)) { return item; } } } } } // start from node self function findParent(node, handle) { let current = node; while (current) { if (handle(current)) { return current; } current = current.parent; } } const rules = { 'targetNode existed': info => info.targetNode, 'targetNode is placeholder': info => info.targetNode.isDragPlaceHolder, 'targetNode at top': info => info.targetAtTop, 'targetNode at bottom': info => info.targetAtBottom, 'targetNode is the second child of root': info => info.currentTreeRootSecondChildExcludingDragging === info.targetNode, 'currentTree existed': info => info.currentTree, 'currentTree empty': info => !findChild(info, info.currentTree.rootData.children, v => v), 'placeholder existed': info => info.dplhEl, 'placeholder in currentTree': info => info.dplhElInCurrentTree, 'placeholder at top': info => info.dplhAtTop, 'targetNode is open': info => info.targetNode.open, 'targetNode has children excluding placeholder': info => findChild(info, info.targetNode.children, v => v !== info.dplh), 'targetNode is 1st child': info => findChild(info, info.targetNode.parent.children, v => v) === info.targetNode, 'targetNode is last child': info => findChild(info, info.targetNode.parent.children, v => v, true) === info.targetNode, 'on targetNode middle': info => info.offset.y <= info.tiMiddleY, 'at left': info => info.offset.x < info.tiOffset.x, 'at indent right': info => info.offset.x > info.tiOffset.x + info.currentTree.indent }; // convert rule output to Boolean for (const key of Object.keys(rules)) { const old = rules[key]; rules[key] = (...args) => Boolean(old(...args)); } let prevTree; let droppableIds = {}; let draggableIds = {}; // context is vm function autoMoveDragPlaceHolder(draggableHelperInfo) { const trees = this.store.trees; const dhStore = draggableHelperInfo.store; // make info const info = { event: draggableHelperInfo.event, el: dhStore.el, vm: this, node: this.data, store: this.store, dplh: this.store.dplh, draggableHelperData: { opt: draggableHelperInfo.options, store: dhStore } }; // attachCache(info, new Cache(), { // dragging node coordinate nodeInnerEl() { return this.el.querySelector('.tree-node-inner'); }, offset() { return getOffset(this.nodeInnerEl); }, // left top point offset2() { return { x: this.offset.x + this.nodeInnerEl.offsetWidth, y: this.offset.y + this.nodeInnerEl.offsetHeight }; }, // right bottom point offsetToViewPort() { const r = this.nodeInnerEl.getBoundingClientRect(); r.x = r.left; r.y = r.top; return r; }, // tree currentTree() { const currentTree = getTreeByPoint(this.offsetToViewPort.x, this.offsetToViewPort.y, trees); if (currentTree) { const dragStartTree = this.store; if (prevTree == null) { prevTree = dragStartTree; } if (prevTree !== currentTree) { if (!isPropTrue(dragStartTree.crossTree) || !isPropTrue(currentTree.crossTree)) { return; } prevTree = currentTree; } if (!isPropTrue(currentTree.droppable)) { return; } return currentTree; } }, currentTreeRootEl() { return document.getElementById(this.currentTree.rootData._id); }, currentTreeRootOf4() { return getOf4(this.currentTreeRootEl, this.currentTree.space); }, // the second child of currentTree root, excluding dragging node currentTreeRootSecondChildExcludingDragging() { return this.currentTree.rootData.children.slice(0, 3).filter(v => v !== this.node)[1]; }, // placeholder dplhEl() { return document.getElementById(this.dplh._id); }, dplhElInCurrentTree() { return Boolean(this.currentTree.$el.querySelector(`#${this.dplh._id}`)); }, dplhOf4() { return getOf4(this.dplhEl, this.currentTree.space); }, dplhAtTop() { return Math.abs(this.dplhOf4.y - this.currentTreeRootOf4.y) < 5; }, targetAtTop() { return Math.abs(this.tiOf4.y - this.currentTreeRootOf4.y) < 5; }, targetAtBottom() { return Math.abs(this.tiOf4.y2 - this.currentTreeRootOf4.y2) < 5; }, // most related node targetNode() { const { currentTree } = this; if (!currentTree) { throw 'no currentTree'; } // const { x, y } = this.offset; let currentNode = currentTree.rootData; while (true) { let children = currentNode.children; if (!children) { break; } if (this.node.parent === currentNode) { // dragging node is in currentNode children, remove it first children = children.slice(); children.splice(children.indexOf(this.node), 1); } if (children.length === 0) { break; } const t = binarySearch(children, node => { const el = document.getElementById(node._id); const ty = getOffset(el).y; const ty2 = ty + el.offsetHeight + currentTree.space; if (ty2 < y) { return -1; } else if (ty <= y) { return 0; } else { return 1; } }, null, null, true); if (t.hit) { currentNode = t.value; } else { if (t.bigger) { currentNode = children[t.index - 1]; } else { currentNode = t.value; } } if (!currentNode) { currentNode = children[0]; break; } if (!currentNode) { break; } const innerEl = document.getElementById(currentNode._id).querySelector('.tree-node-inner'); const of = getOf4(innerEl, currentTree.space); if (of.y <= y && y <= of.y2) { break; } } return currentNode; }, targetNodeEl() { return document.getElementById(this.targetNode._id); }, // targetNodeInnerElOffset tiInnerEl() { return this.targetNodeEl.querySelector('.tree-node-inner'); }, tiOffset() { return getOffset(this.tiInnerEl); }, tiOf4() { return getOf4(this.tiInnerEl, this.currentTree.space); }, tiMiddleY() { return this.tiOffset.y + this.tiInnerEl.offsetHeight / 2; }, // targetPrevEl() { let r = this.targetNodeEl.previousSibling; if (hasClass(r, 'dragging')) { r = r.previousSibling; } return r; }, targetPrev() { const id = this.targetPrevEl.getAttribute('id'); return this.currentTree.getNodeById(id); } }); // attachCache end // decision start ================================= const executedRuleCache = {}; // exec rule const exec = ruleId => { if (!executedRuleCache.hasOwnProperty(ruleId)) { let r; try { r = rules[ruleId](info); } catch (e) { r = e; try { if (process.env.DEVELOPE_SELF) { // only visible when develop its self // eslint-disable-next-line console.warn(`failed to execute rule '${ruleId}'`, e); } } catch (e2) {} } executedRuleCache[ruleId] = r; } return executedRuleCache[ruleId]; }; if (exec('currentTree existed') === true) { if (exec('targetNode is placeholder') === false) { if (exec('targetNode is the second child of root') === true) { if (exec('targetNode has children excluding placeholder') === false) { if (exec('on targetNode middle') === true) { targets['before'](info); } else if (exec('on targetNode middle') === false) { if (exec('at indent right') === true) { targets['append'](info); } else if (exec('at indent right') === false) { targets['after'](info); } } } else if (exec('targetNode has children excluding placeholder') === true) { targets['prepend'](info); } } else if (exec('targetNode is the second child of root') === false) { if (exec('currentTree empty') === false) { if (exec('targetNode at top') === true) { if (exec('placeholder in currentTree') === true) { if (exec('targetNode has children excluding placeholder') === false) { if (exec('on targetNode middle') === false) { if (exec('at indent right') === false) { targets['after'](info); } else if (exec('at indent right') === true) { targets['append'](info); } } else if (exec('on targetNode middle') === true) { targets['before'](info); } } else if (exec('targetNode has children excluding placeholder') === true) { if (exec('on targetNode middle') === false) { targets['prepend'](info); } else if (exec('on targetNode middle') === true) { targets['before'](info); } } } else if (exec('placeholder in currentTree') === false) { targets['before'](info); } } else if (exec('targetNode at top') === false) { if (exec('targetNode at bottom') === false) { if (exec('placeholder at top') === true) { targets['prepend'](info); } else if (exec('placeholder at top') === false) { if (exec('targetNode has children excluding placeholder') === true) { targets['prepend'](info); } else if (exec('targetNode has children excluding placeholder') === false) { if (exec('targetNode is 1st child') === false) { if (exec('targetNode is last child') === false) { if (exec('on targetNode middle') === true) { if (exec('at indent right') === true) { targets['append'](info); } else if (exec('at indent right') === false) { targets['after'](info); } } else if (exec('on targetNode middle') === false) { if (exec('at indent right') === true) { targets['append'](info); } else if (exec('at indent right') === false) { targets['after'](info); } } } else if (exec('targetNode is last child') === true) { if (exec('at indent right') === true) { targets['append'](info); } else if (exec('at indent right') === false) { targets['after'](info); } } } else if (exec('targetNode is 1st child') === true) { if (exec('targetNode is last child') === true) { targets['append'](info); } else if (exec('targetNode is last child') === false) { if (exec('on targetNode middle') === false) { if (exec('at indent right') === false) { targets['after'](info); } else if (exec('at indent right') === true) { targets['append'](info); } } else if (exec('on targetNode middle') === true) { if (exec('at indent right') === false) { targets['after'](info); } else if (exec('at indent right') === true) { targets['append'](info); } } } } } } } else if (exec('targetNode at bottom') === true) { if (exec('placeholder in currentTree') === true) { if (exec('on targetNode middle') === false) { if (exec('at indent right') === true) { targets['append'](info); } else if (exec('at indent right') === false) { targets['after'](info); } } else if (exec('on targetNode middle') === true) { targets['append'](info); } } else if (exec('placeholder in currentTree') === false) { targets['append'](info); } } } } else if (exec('currentTree empty') === true) { targets['append current tree'](info); } } } else if (exec('targetNode is placeholder') === true) { if (exec('targetNode at bottom') === false) { if (exec('targetNode is the second child of root') === false) { if (exec('targetNode is 1st child') === true) { if (exec('targetNode is last child') === false) { targets['nothing'](info); } else if (exec('targetNode is last child') === true) { if (exec('on targetNode middle') === false) { if (exec('at left') === true) { targets['after target parent'](info); } else if (exec('at left') === false) { targets['nothing'](info); } } else if (exec('on targetNode middle') === true) { if (exec('at left') === true) { targets['after target parent'](info); } else if (exec('at left') === false) { targets['nothing'](info); } } } } else if (exec('targetNode is 1st child') === false) { if (exec('targetNode is last child') === true) { if (exec('on targetNode middle') === true) { if (exec('at left') === true) { targets['after target parent'](info); } else if (exec('at left') === false) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } } else if (exec('on targetNode middle') === false) { if (exec('at left') === true) { targets['after target parent'](info); } else if (exec('at left') === false) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } } } else if (exec('targetNode is last child') === false) { if (exec('on targetNode middle') === true) { if (exec('at left') === true) { targets['nothing'](info); } else if (exec('at left') === false) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } } else if (exec('on targetNode middle') === false) { if (exec('at left') === true) { targets['nothing'](info); } else if (exec('at left') === false) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } } } } } else if (exec('targetNode is the second child of root') === true) { if (exec('on targetNode middle') === true) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } else if (exec('on targetNode middle') === false) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } } } else if (exec('targetNode at bottom') === true) { if (exec('targetNode is 1st child') === true) { if (exec('on targetNode middle') === false) { if (exec('at left') === true) { targets['after target parent'](info); } else if (exec('at left') === false) { targets['nothing'](info); } } else if (exec('on targetNode middle') === true) { if (exec('at left') === false) { targets['nothing'](info); } else if (exec('at left') === true) { targets['after target parent'](info); } } } else if (exec('targetNode is 1st child') === false) { if (exec('on targetNode middle') === false) { if (exec('at left') === true) { targets['after target parent'](info); } else if (exec('at left') === false) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } } else if (exec('on targetNode middle') === true) { if (exec('at left') === true) { targets['after target parent'](info); } else if (exec('at left') === false) { if (exec('at indent right') === true) { targets['append prev'](info); } else if (exec('at indent right') === false) { targets['nothing'](info); } } } } } } } else if (exec('currentTree existed') === false) { targets['nothing'](info); } // decision end ================================= } function getOf4(el, space) { const r = getOffset(el); r.x2 = r.x + el.offsetWidth; r.y2 = r.y + el.offsetHeight + space; return r; } autoMoveDragPlaceHolder.dragStart = function dragStart() {}; autoMoveDragPlaceHolder.dragEnd = function dragEnd() { prevTree = null; droppableIds = {}; draggableIds = {}; }; var script$1 = { extends: __vue_component__, name: 'TreeNode', mounted() { this.store.isNodeDraggable = isNodeDraggable; this.store.isNodeDroppable = isNodeDroppable; if (this.isRoot || this.data.isDragPlaceHolder) { return; } const { dplh } = this.store; this.$watch('store.draggable', draggable => { if (isPropTrue(draggable)) { const triggerEl = this.store.getTriggerEl ? this.store.getTriggerEl(this) : this.$el.querySelector('.tree-node-inner'); this._draggableDestroy = draggableHelper(triggerEl, { preventSelect: isPropTrue(this.store.preventSelect), // trigger el getEl: () => this.$el, minTranslate: 10, drag: (e, opt, store) => { autoMoveDragPlaceHolder.dragStart(); // this store is not tree const draggableHelperInfo = { event: e, options: opt, store }; if (this.store.ondragstart && this.store.ondragstart(this.data, draggableHelperInfo) === false) { return false; } if (!isNodeDraggable(this.data)) { return false; } this.store.$emit('drag', this.data); // record start position const siblings = this.data.parent.children; this.startPosition = { siblings, index: siblings.indexOf(this.data) }; dplh.innerStyle.height = store.el.offsetHeight + 'px'; insertAfter(dplh, this.data); this.data.class += ' dragging'; }, moving: (e, opt, store) => { if (store.movedCount === 0) { return; } const draggableHelperInfo = { event: e, options: opt, store }; return autoMoveDragPlaceHolder.call(this, draggableHelperInfo); }, drop: (e, opt, store) => { autoMoveDragPlaceHolder.dragEnd(); const draggableHelperInfo = { event: e, options: opt, store }; if (this.store.ondragend && this.store.ondragend(this.data, draggableHelperInfo) === false) { arrayRemove(dplh.parent.children, dplh); // can't drop, no change } else { const targetTree = dplh._vm.store; const crossTree = targetTree !== this.store; const oldTree = crossTree ? this.store : null; insertAfter(this.data, dplh); arrayRemove(dplh.parent.children, dplh); this.data.class = this.data.class.replace(/(^| )dragging( |$)/g, ' '); targetTree.$emit('drop', this.data, targetTree, oldTree); oldTree && oldTree.$emit('drop', this.data, targetTree, oldTree); // emit change event if changed const siblings = this.data.parent.children; if (siblings === this.startPosition.siblings && siblings.indexOf(this.data) === this.startPosition.index) ; else { this.store.$emit('change', this.data, targetTree, oldTree); oldTree && oldTree.$emit('change', this.data, targetTree, oldTree); } this.startPosition = null; } } }); } else { if (this._draggableDestroy) { this._draggableDestroy(); this._draggableDestroy = null; } } }, { immediate: true }); } }; /* script */ const __vue_script__$1 = script$1; /* template */ /* style */ const __vue_inject_styles__$1 = undefined; /* scoped */ const __vue_scope_id__$1 = undefined; /* module identifier */ const __vue_module_identifier__$1 = undefined; /* functional template */ const __vue_is_functional_template__$1 = undefined; /* style inject */ /* style inject SSR */ /* style inject shadow dom */ const __vue_component__$1 = /*#__PURE__*/normalizeComponent({}, __vue_inject_styles__$1, __vue_script__$1, __vue_scope_id__$1, __vue_is_functional_template__$1, __vue_module_identifier__$1, false, undefined, undefined, undefined); // var script$2 = { name: 'Tree', props: { data: {}, idLength: { type: Number, default: 5 }, indent: { type: Number, default: 16 }, activatedClass: { default: 'active' }, openedClass: { default: 'open' }, space: { type: Number, default: 10 }, // space between node, unit px allowAddItem: { type: Boolean, default: false }, addItemText: { type: String, default: 'Add item' }, childrenTransitionName: {}, // there are issues under draggable tree customInnerBack: {} }, components: { TreeNode: __vue_component__ }, data() { return { store: this, rootData: null }; }, watch: { data: { immediate: true, handler(data, old) { if (data === old) { return; } // make rootData always use a same object this.rootData = this.rootData || { isRoot: true, _id: `tree_${this._uid}_node_root`, children: [] }; breadthFirstSearch(data, (node, k, parent) => { this.completeNode(node, parent); }); this.rootData.children = data; } } }, methods: { completeNode(node, parent) { const completedData = { open: true, children: [], active: false, style: {}, class: '', innerStyle: {}, innerClass: '', innerBackStyle: {}, innerBackClass: {} }; for (const key in completedData) { if (!node.hasOwnProperty(key)) { this.$set(node, key, completedData[key]); } } this.$set(node, 'parent', parent || this.rootData); if (!node.hasOwnProperty('_id')) { node._id = `tree_${this._uid}_node_${randString(this.idLength)}`; } node._treeNodePropertiesCompleted = true; }, // pure node self pure(node, withChildren, after) { const t = Object.assign({}, node); delete t._id; delete t.parent; delete t.children; delete t.open; delete t.active; delete t.style; delete t.class; delete t.innerStyle; delete t.innerClass; delete t.innerBackStyle; delete t.innerBackClass; for (const key of Object.keys(t)) { if (key[0] === '_') { delete t[key]; } } if (withChildren && node.children) { t.children = node.children.slice(); t.children.forEach((v, k) => { t.children[k] = this.pure(v, withChildren); }); } if (after) { return after(t, node) || t; } return t; }, getNodeById(id) { let r; breadthFirstSearch(this.rootData.children, node => { if (node._id === id) { r = node; return false; } }); return r; }, getActivated() { const r = []; breadthFirstSearch(this.rootData.children, node => { if (node.active) { r.push(node); } }); return r; }, getOpened() { const r = []; breadthFirstSearch(this.rootData.children, node => { if (node.open) {