├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── README_CN.md ├── README_ko.md ├── UPGRADE_GUIDE.md ├── babel.config.js ├── build └── build-lib.js ├── dist ├── vue-draggable-nested-tree.cjs.js ├── vue-draggable-nested-tree.es.js ├── vue-draggable-nested-tree.js ├── vue-draggable-nested-tree.min.js └── vue-draggable-nested-tree.min.js.map ├── ie11-example ├── README.txt ├── css │ └── style.css ├── index.html ├── index.pug ├── js │ └── index.js ├── license.txt └── scss │ └── style.scss ├── package-lock.json ├── package.json ├── public └── index.html ├── src ├── App.vue ├── components │ ├── DraggableTree.vue │ ├── DraggableTreeNode.vue │ ├── Tree.vue │ ├── TreeNode.vue │ ├── autoMoveDragPlaceHolder.js │ └── temporarily-fix-overlapping-tree-issue.js ├── examples │ ├── Base.vue │ ├── CollapsingAnimation.vue │ ├── CustomTreeNode.vue │ ├── Empty.vue │ ├── MaxLevel.vue │ └── RTL.vue ├── lib-entry.js ├── main.js └── plugins │ ├── Cache.js │ └── utils.js ├── tea.yaml ├── update.sh ├── version-plus.js ├── vue.config.js └── warn.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.sw* 21 | 22 | # dev 23 | /src/components/autoMoveDragPlaceHolderDev.js 24 | /src/components/_info.json 25 | 26 | __local* -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ie11-example 2 | update.sh 3 | version-plus.js 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 phphe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # vue-draggable-nested-tree vue 可拖拽树, 可跨树拖拽 4 | 5 | **This project is no longer maintained, please move to the new monorepo [he-tree](https://github.com/phphe/he-tree).** 6 | 7 | **本项目不再维护, 请移步到新的 monorepo [he-tree](https://github.com/phphe/he-tree).** 8 | 9 | 这是可拖拽树组件. 此组件没有 css, 您需要自己添加您喜欢的样式, 参考 demo, 只有几个样式, 不难. 10 | 此组件不负责节点的具体渲染, 暴露了一个节点渲染插槽, 请参考 demo 自行渲染. 11 | This is a draggable tree component. This component does not have css, you need to add your style refer to demo. The demo style is less, not difficult. 12 | This component doesn't render node. It exposes a node rendering slot. Please refer to the demo for rendering. 13 | 14 | - [demo / 示例/演示](https://codepen.io/phphe/pen/KRapQm) 15 | - [ie11 example / ie11 示例](https://github.com/phphe/vue-draggable-nested-tree/tree/master/ie11-example) 16 | - [English Doc](https://github.com/phphe/vue-draggable-nested-tree/blob/master/README.md) 17 | - [中文文档](https://github.com/phphe/vue-draggable-nested-tree/blob/master/README_CN.md) 18 | - [한국어 Korean](https://github.com/phphe/vue-draggable-nested-tree/blob/master/README_ko.md) by [cjstk7168](https://github.com/cjstk7168) 19 | 20 | # touch 21 | 22 | 已支持简单触摸(单点). 23 | Support touch(single point). 24 | 25 | # Donation / 打赏 26 | 27 | [Paypal](https://www.paypal.me/phphe) | [Alipay/支付宝](https://github.com/phphe/my-alipay-wechat-qr-code/blob/master/alipay.jpg) | [Wechat/微信](https://github.com/phphe/my-alipay-wechat-qr-code/blob/master/wechat.png) 28 | 29 | # Indexes 30 | 31 | - [vue-draggable-nested-tree](#vue_draggable_nested_tree) 32 | - [install](#install) 33 | - [usage](#usage) 34 | - [import](#import) 35 | - [data](#data) 36 | - [template](#template) 37 | - [template for old browsers(eg: IE)](#template_for_old_browsers) 38 | - [api](#api) 39 | - [Tree props](#tree_props) 40 | - [Noraml - Tree props](#noraml_tree_props) 41 | - [Hooks - Tree props](#hooks_tree_props) 42 | - [draggableHelperInfo: {event, options, store}](#draggable_helper_info) 43 | - [Tree properties](#tree_properties) 44 | - [Tree events](#tree_events) 45 | - [Tree methods](#tree_methods) 46 | - [node properties](#node_properties) 47 | - [node deep properties example](#node_deep_properties_example) 48 | - [other](#other) 49 | - [demo css](#demo_css) 50 | - [examples](#examples) 51 | - [draggable & droppable](#draggable_&_droppable) 52 | - [Traverse tree](#traverse_tree) 53 | - [draggable library](#draggable_library) 54 | 55 | 56 | 57 | # install 58 | 59 | ```sh 60 | npm i vue-draggable-nested-tree 61 | ``` 62 | 63 | 64 | 65 | # usage 66 | 67 | 68 | 69 | ### import 70 | 71 | ```js 72 | import { DraggableTree } from "vue-draggable-nested-tree"; 73 | // vue-draggable-nested-tree contains Tree, TreeNode, DraggableTree, DraggableTreeNode 74 | // import the component and register it as global or local component 75 | ``` 76 | 77 | 78 | 79 | ### data 80 | 81 | ```js 82 | data: [ 83 | { text: "node 1" }, 84 | { text: "node 2" }, 85 | { text: "node 3 undraggable", draggable: false }, 86 | { text: "node 4" }, 87 | { text: "node 4 undroppable", droppable: false }, 88 | { 89 | text: "node 5", 90 | children: [ 91 | { text: "node 1" }, 92 | { text: "node 2", children: [{ text: "node 3" }, { text: "node 4" }] }, 93 | { 94 | text: "node 2 undroppable", 95 | droppable: false, 96 | children: [{ text: "node 3" }, { text: "node 4" }], 97 | }, 98 | { 99 | text: "node 2", 100 | children: [ 101 | { text: "node 3" }, 102 | { text: "node 4 undroppable", droppable: false }, 103 | ], 104 | }, 105 | { text: "node 3" }, 106 | { text: "node 4" }, 107 | { text: "node 3" }, 108 | { text: "node 4" }, 109 | { text: "node 3" }, 110 | { text: "node 4" }, 111 | { text: "node 3" }, 112 | { text: "node 4" }, 113 | ], 114 | }, 115 | ]; 116 | ``` 117 | 118 | 119 | 120 | ### template 121 | 122 | ```pug 123 | Tree(:data="data" draggable crossTree) 124 | div(slot-scope="{data, store, vm}") 125 | //- data is node 126 | //- store is the tree 127 | //- vm is node Vue instance, you can get node level by vm.level 128 | template(v-if="!data.isDragPlaceHolder") 129 | b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}}  130 | span {{data.text}} 131 | ``` 132 | 133 | 134 | 135 | ### template for old browsers(eg: IE) 136 | 137 | ```pug 138 | //- slot-scope="{data, store, vm}" may not work in old browsers, replace with slot-scope="slot" 139 | Tree(:data="data" draggable crossTree) 140 | div(slot-scope="slot") 141 | //- data is node 142 | //- store is the tree 143 | //- vm is node Vue instance, you can get node level by vm.level 144 | template(v-if="!slot.data.isDragPlaceHolder") 145 | b(v-if="slot.data.children && slot.data.children.length" @click="slot.store.toggleOpen(slot.data)") {{slot.data.open ? '-' : '+'}}  146 | span {{slot.data.text}} 147 | ``` 148 | 149 | 150 | 151 | # api 152 | 153 | **The 'store' is the tree vm** 154 | 155 | 156 | ### Tree props 157 | 158 | 159 | 160 | ###### Noraml - Tree props 161 | 162 | ```js 163 | // base tree 164 | data: {}, // type Array 165 | indent: {default: 16}, 166 | activatedClass: {default: 'active'}, 167 | openedClass: {default: 'open'}, 168 | space: {default: 10}, // space between node, unit px 169 | // draggable tree 170 | preventSelect: {default: true}, // if to prevent drag handler text be selected when drag, excluding input and textarea 171 | getTriggerEl: {type: Function}, // get the el trigger drag, default is node self. arguments(nodeVm) 172 | draggable: {}, // is the tree draggable, default false 173 | droppable: {default: true}, // is the tree droppable, default true 174 | crossTree: {}, // can a node of the tree be dragged into other tree, or receive other tree node 175 | ``` 176 | 177 | 178 | 179 | ###### Hooks - Tree props 180 | 181 | ```js 182 | ondragstart: {type: Function}, // hook. return false to prevent drag. arguments(node, draggableHelperInfo) 183 | ondragend: {type: Function}, // hook. return false to prevent drop. arguments(node, draggableHelperInfo) 184 | ``` 185 | 186 | 187 | 188 | ###### draggableHelperInfo 189 | 190 | {event, options, store} 191 | 192 | 193 | ### Tree properties 194 | 195 | ```js 196 | // base 197 | rootData, // generated by tree 198 | // draggable 199 | dplh, // drag placeholder. globally unique. 200 | trees, // array, all trees in the app. globally unique. 201 | ``` 202 | 203 | 204 | 205 | ### Tree events 206 | 207 | ```js 208 | // store is the tree vm 209 | drag(node), // on drag start. 210 | drop(node, targetTree, oldTree), // after drop. 211 | change(node, targetTree, oldTree), // after drop, only when the node position changed 212 | nodeOpenChanged(node); // on a node is closed or open 213 | ``` 214 | 215 | - targetTree and oldTree are tree vm. 216 | - oldTree is available only when cross tree. Otherwise null. 217 | - if cross tree, both targetTree and oldTree will emit drop and change. 218 | 219 | 220 | 221 | ### Tree methods 222 | 223 | ```js 224 | pure(node, withChildren, after) 225 | /* 226 | pure 227 | return a node data without runtime properties.(!: property which starts with '_' will be removed) 228 | withChildren: optional. after: Function, optional 229 | the code about after(t is computed node data): 230 | if (after) { 231 | return after(t, node) || t 232 | } 233 | return t 234 | */ 235 | getNodeById(id) 236 | getActivated() 237 | getOpened() 238 | activeNode(node, inactiveOld) 239 | toggleActive(node, inactiveOld) 240 | openNode(node, closeOld) 241 | toggleOpen(node, closeOld) 242 | // follow methods are easy, so I paste their soure code 243 | getPureData(after) { return this.pure(this.rootData, true, after).children } // after: Function, optional 244 | deleteNode(node) { return hp.arrayRemove(node.parent.children, node) } 245 | // add node: like array. eg: node.children.push(newNodeData) 246 | // update node: just assign to the node properties directly 247 | isNodeDraggable(node) 248 | isNodeDroppable(node) 249 | ``` 250 | 251 | 252 | 253 | ### node properties 254 | 255 | ```js 256 | // base 257 | _id 258 | _vm 259 | parent 260 | children: [], 261 | open, 262 | active: false, 263 | style: {}, 264 | class: '', 265 | innerStyle: {}, 266 | innerClass: '', 267 | innerBackStyle: {}, 268 | innerBackClass: {}, 269 | // draggable 270 | draggable // default true. Please check 'draggable & droppable' below 271 | droppable // default true. Please check 'draggable & droppable' below 272 | isDragPlaceHolder 273 | ``` 274 | 275 | 276 | 277 | #### node deep properties example 278 | 279 | ```js 280 | node._vm; // vm 281 | node._vm.level; // 节点层级, 只读 282 | node._vm.store; // tree 283 | node.parent._vm; // parent node vm 284 | node._vm.store; 285 | ``` 286 | 287 | 288 | 289 | # other 290 | 291 | 292 | 293 | ### demo css 294 | 295 | ```css 296 | .he-tree { 297 | border: 1px solid #ccc; 298 | padding: 20px; 299 | width: 300px; 300 | } 301 | .tree-node { 302 | } 303 | .tree-node-inner { 304 | padding: 5px; 305 | border: 1px solid #ccc; 306 | cursor: pointer; 307 | } 308 | .draggable-placeholder { 309 | } 310 | .draggable-placeholder-inner { 311 | border: 1px dashed #0088f8; 312 | box-sizing: border-box; 313 | background: rgba(0, 136, 249, 0.09); 314 | color: #0088f9; 315 | text-align: center; 316 | padding: 0; 317 | display: flex; 318 | align-items: center; 319 | } 320 | ``` 321 | 322 | 323 | 324 | ### examples 325 | 326 | clone the package, and 327 | 328 | ```sh 329 | npm install 330 | npm run dev 331 | ``` 332 | 333 | - [Base](https://github.com/phphe/vue-draggable-nested-tree/blob/master/src/examples/Base.vue) 334 | - [MaxLevel](https://github.com/phphe/vue-draggable-nested-tree/blob/master/src/examples/MaxLevel.vue) 335 | 336 | 337 | 338 | ### draggable & droppable 339 | 340 | A node is default draggable and droppable. You can set draggable and droppable property of a node. The another way is listen event 'drag', traverse all data to set draggable or droppable property. 341 | 342 | 343 | ### Traverse tree 344 | 345 | Recommend to use my other library [tree-helper](https://github.com/phphe/tree-helper). It has 2 traverse methods: depthFirstSearch, breadthFirstSearch. 346 | 347 | 348 | ### draggable library 349 | 350 | [draggable-helper](https://github.com/phphe/draggable-helper) is my another library for drag. And it also is using by this component. You can use it to help you drag functions. 351 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | 2 | # vue-draggable-nested-tree vue可拖拽树, 可跨树拖拽 3 | 这是可拖拽树组件. 此组件没有css, 您需要自己添加您喜欢的样式, 参考demo, 只有几个样式, 不难. 4 | 此组件不负责节点的具体渲染, 暴露了一个节点渲染插槽, 请参考demo自行渲染. 5 | This is a draggable tree component. This component does not have css, you need to add your style refer to demo. The demo style is less, not difficult. 6 | This component doesn't render node. It exposes a node rendering slot. Please refer to the demo for rendering. 7 | * [demo / 示例/演示](https://codepen.io/phphe/pen/KRapQm) 8 | * [ie11 example / ie11示例](https://github.com/phphe/vue-draggable-nested-tree/tree/master/ie11-example) 9 | * [English Doc](https://github.com/phphe/vue-draggable-nested-tree/blob/master/README.md) 10 | * [中文文档](https://github.com/phphe/vue-draggable-nested-tree/blob/master/README_CN.md) 11 | 12 | # touch 13 | 已支持简单触摸(单点). 14 | Support touch(single point). 15 | # Donation / 打赏 16 | [Paypal](https://www.paypal.me/phphe) | [Alipay/支付宝](https://github.com/phphe/my-alipay-wechat-qr-code/blob/master/alipay.jpg) | [Wechat/微信](https://github.com/phphe/my-alipay-wechat-qr-code/blob/master/wechat.png) 17 | # Indexes 18 | * [vue-draggable-nested-tree](#vue_draggable_nested_tree) 19 | * [安装](#install) 20 | * [使用](#usage) 21 | * [import/引入](#import) 22 | * [data](#data) 23 | * [template](#template) 24 | * [旧浏览器(IE)模板写法](#template_for_old_browsers) 25 | * [api](#api) 26 | * [Tree props](#tree_props) 27 | * [Noraml - Tree props](#noraml_tree_props) 28 | * [Hooks - Tree props/钩子](#hooks_tree_props) 29 | * [draggableHelperInfo: {event, options, store}](#draggable_helper_info) 30 | * [Tree properties/树的属性](#tree_properties) 31 | * [Tree events/树的事件](#tree_events) 32 | * [Tree methods/树的方法](#tree_methods) 33 | * [node properties/节点的属性](#node_properties) 34 | * [node deep properties example/节点的深层属性调用示例](#node_deep_properties_example) 35 | * [其他](#other) 36 | * [示例css](#demo_css) 37 | * [例子](#examples) 38 | * [draggable & droppable/可拖拽与可放置](#draggable_&_droppable) 39 | * [Traverse tree/遍历树](#traverse_tree) 40 | * [draggable library/用来支持拖拽的库](#draggable_library) 41 | 42 | 43 | # 安装 44 | ```sh 45 | npm i vue-draggable-nested-tree 46 | ``` 47 | 48 | # 使用 49 | 50 | ### import / 引入 51 | ```js 52 | import {DraggableTree} from 'vue-draggable-nested-tree' 53 | // vue-draggable-nested-tree contains Tree, TreeNode, DraggableTree, DraggableTreeNode 54 | // import the component and register it as global or local component 55 | ``` 56 | 57 | ### data / 数据 58 | ```js 59 | data: [ 60 | {text: 'node 1'}, 61 | {text: 'node 2'}, 62 | {text: 'node 3 undraggable', draggable: false}, 63 | {text: 'node 4'}, 64 | {text: 'node 4 undroppable', droppable: false}, 65 | {text: 'node 5', children: [ 66 | {text: 'node 1'}, 67 | {text: 'node 2', children: [ 68 | {text: 'node 3'}, 69 | {text: 'node 4'}, 70 | ]}, 71 | {text: 'node 2 undroppable', droppable: false, children: [ 72 | {text: 'node 3'}, 73 | {text: 'node 4'}, 74 | ]}, 75 | {text: 'node 2', children: [ 76 | {text: 'node 3'}, 77 | {text: 'node 4 undroppable', droppable: false}, 78 | ]}, 79 | {text: 'node 3'}, 80 | {text: 'node 4'}, 81 | {text: 'node 3'}, 82 | {text: 'node 4'}, 83 | {text: 'node 3'}, 84 | {text: 'node 4'}, 85 | {text: 'node 3'}, 86 | {text: 'node 4'}, 87 | ]}, 88 | ] 89 | ``` 90 | 91 | ### template / 模板 92 | ```pug 93 | Tree(:data="data" draggable crossTree) 94 | div(slot-scope="{data, store, vm}") 95 | //- data是节点数据 96 | //- store是树的实例 97 | //- vm是节点实例, vm.level是节点的层级 98 | template(v-if="!data.isDragPlaceHolder") 99 | b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}}  100 | span {{data.text}} 101 | ``` 102 | 103 | ### 旧浏览器(IE)模板写法 104 | ```pug 105 | //- slot-scope="{data, store, vm}" 旧浏览器不支持解构赋值. 如果你直接在网页内联模板里这样写(不经过编译)就会出错. 106 | Tree(:data="data" draggable crossTree) 107 | div(slot-scope="slot") 108 | //- data is node 109 | //- store is the tree 110 | //- vm is node Vue instance, you can get node level by vm.level 111 | template(v-if="!slot.data.isDragPlaceHolder") 112 | b(v-if="slot.data.children && slot.data.children.length" @click="slot.store.toggleOpen(slot.data)") {{slot.data.open ? '-' : '+'}}  113 | span {{slot.data.text}} 114 | ``` 115 | 116 | # api 117 | **下文的'store'是树的vue实例(vm), nodeVm是节点的实例** 118 | 119 | ### Tree props 120 | 121 | ###### 基础props - Tree props 122 | ```js 123 | // base tree 124 | data: {}, // 数组类型 125 | indent: {default: 16}, // 缩进 126 | activatedClass: {default: 'active'}, 127 | openedClass: {default: 'open'}, 128 | space: {default: 10}, // 节点垂直方向的间距 129 | // draggable tree 130 | preventSelect: {default: true}, // 是否阻止拖拽时文字被选中, 不影响输入框(input, textarea)中文字的选择 131 | getTriggerEl: {type: Function}, // 用来指定触发拖拽的元素, 默认是节点自己, 比如你可以指定其中的一个按钮来触发拖拽. 参数(nodeVm) 132 | draggable: {}, // 树是否启用拖拽, 默认否 133 | droppable: {default: true}, // 树是否可被拖进, 默认是. 如果否, 则该树的节点不能在其内移动位置,其他树的节点也不能拖入其中 134 | crossTree: {}, // 是否启用跨树, 默认否. 该树的节点是否可拖拽到其他树, 其他树的节点是否可拖拽到该树 135 | ``` 136 | 137 | ###### 钩子 - Tree props 138 | ```js 139 | ondragstart: {type: Function}, // 拖拽开始时执行, 返回false将阻止拖拽. 参数(node, draggableHelperInfo) 140 | ondragend: {type: Function}, // 拖拽结束时执行(抬起鼠标或松手), 返回false将阻止拖动中的元素放到该位置. 参数(node, draggableHelperInfo) 141 | ``` 142 | 143 | ###### draggableHelperInfo 144 | {event, options, store} 145 | 我使用我的另一个库draggable-helper来实现底层拖拽, draggableHelperInfo是draggable-helper传回的原始数据 146 | 147 | ### Tree properties / 树的属性 148 | ```js 149 | // base 150 | rootData, // 根节点, 由树生成 151 | // draggable 152 | dplh, // 拖动时用来表示位置的节点, 全局唯一. 153 | trees, // 数组, 所有树. 全局唯一. 154 | ``` 155 | 156 | ### Tree events / 树的事件 157 | ```js 158 | // store is the tree vm 159 | drag(node), // 拖动开始. 160 | drop(node, targetTree, oldTree), // 拖动结束后. 161 | change(node, targetTree, oldTree), // 拖动结束后并且有节点位置发生了改变 162 | nodeOpenChanged(node) // 当节点被展开或折叠时 163 | ``` 164 | * targetTree: 目标树. oldTree: 旧树 165 | * targetTree 和 oldTree 都是树的实例. 166 | * oldTree仅当跨树拖拽时存在, 否则null. 167 | * 如果跨树, targetTree 和 oldTree都会触发drop和change事件. 168 | 169 | 170 | ### Tree methods / 树的方法 171 | ```js 172 | 173 | pure(node, withChildren, after) 174 | /* 175 | pure 176 | 获得干净数据(不含运行时的属性例如_id之类的), 下划线开头的属性会被删掉. withChildren为true的话则会把节点的子节点的数据也获取到. 177 | withChildren: 可选. after: Function, 可选, after可以自定义返回数据 178 | 关于after的源码(t是干净的节点数据): 179 | if (after) { 180 | return after(t, node) || t 181 | } 182 | return t 183 | */ 184 | getNodeById(id) 185 | getActivated() 186 | getOpened() 187 | activeNode(node, inactiveOld) 188 | toggleActive(node, inactiveOld) 189 | openNode(node, closeOld) 190 | toggleOpen(node, closeOld) 191 | // 下面的方法很简单, 所以附上源码. 192 | getPureData(after) { return this.pure(this.rootData, true, after).children } // 获取树的干净数据 after: Function, 可选 193 | deleteNode(node) { return hp.arrayRemove(node.parent.children, node) } // 删除节点 194 | // 增加节点, 像操作数组一样就可以了. 例子: node.children.push(newNodeData) 195 | // 更新节点, 直接修改节点属性就可以了 196 | isNodeDraggable(node) // 判断节点是否draggable 197 | isNodeDroppable(node) // 判断节点是否droppable 198 | ``` 199 | 200 | ### node properties / 节点属性 201 | ```js 202 | // base 203 | _id // 生成的id 204 | _vm // 节点的实例 205 | parent // 父节点 206 | children: [], // 子节点 207 | open, // 是否打开 208 | active: false, 209 | style: {}, // 可以控制节点的style 210 | class: '', // 可以控制节点的class 211 | // inner是指节点里的.tree-node-inner, innerBack是指节点里的.tree-node-inner-back, 可以审查元素查看构成节点的html元素 212 | innerStyle: {}, 213 | innerClass: '', 214 | innerBackStyle: {}, 215 | innerBackClass: {}, 216 | // draggable 217 | draggable // 是否可拖动, 默认是. 参考下面的 'draggable & droppable' 218 | droppable // 是否可拖入, 默认是. 参考下面的 'draggable & droppable' 219 | isDragPlaceHolder // 该节点是不是拖动占位节点 220 | ``` 221 | 222 | #### node deep properties example / 节点的深层属性调用示例 223 | 在一些回调函数和事件里的参数可能只有node, 而想访问更多(例如节点实例, 节点的爸爸, 节点所在树)怎么办? 其实可以直接通过node访问, 因为他们都嵌套在里面了 224 | ```js 225 | node._vm // vm 226 | node._vm.level // node level, readonly 227 | node._vm.store // tree 228 | node.parent._vm // parent node vm 229 | node._vm.store 230 | ``` 231 | 232 | # other / 其他 233 | 234 | ### 示例css 235 | ```css 236 | .he-tree{ 237 | border: 1px solid #ccc; 238 | padding: 20px; 239 | width: 300px; 240 | } 241 | .tree-node{ 242 | } 243 | .tree-node-inner{ 244 | padding: 5px; 245 | border: 1px solid #ccc; 246 | cursor: pointer; 247 | } 248 | .draggable-placeholder{ 249 | } 250 | .draggable-placeholder-inner{ 251 | border: 1px dashed #0088F8; 252 | box-sizing: border-box; 253 | background: rgba(0, 136, 249, 0.09); 254 | color: #0088f9; 255 | text-align: center; 256 | padding: 0; 257 | display: flex; 258 | align-items: center; 259 | } 260 | ``` 261 | 262 | ### 例子 263 | 如果要本地运行这些例子: 264 | ```sh 265 | npm install 266 | npm run dev 267 | ``` 268 | * [基础](https://github.com/phphe/vue-draggable-nested-tree/blob/master/src/examples/Base.vue) 269 | * [MaxLevel / 指定最大层级](https://github.com/phphe/vue-draggable-nested-tree/blob/master/src/examples/MaxLevel.vue) 270 | 271 | 272 | ### draggable & droppable / 关于可拖动和可拖入 273 | 一个节点默认可拖动和可拖入. 可以单独指定节点的该俩属性. 另一个方式是在drag事件中遍历树来为每一个节点指定该俩属性. 274 | 275 | ### Traverse tree / 遍历树 276 | 推荐使用我的另一个库 [tree-helper](https://github.com/phphe/tree-helper). 两个遍历方法(只是普通遍历的话都可以): depthFirstSearch/深度优先, breadthFirstSearch/广度优先. 277 | 278 | ### draggable library / 用来支持拖拽的库 279 | [draggable-helper](https://github.com/phphe/draggable-helper) 我的另一个关于拖拽底层的库. 如果你要开发关于拖拽的功能可以使用它. 280 | -------------------------------------------------------------------------------- /README_ko.md: -------------------------------------------------------------------------------- 1 | 2 | # vue-draggable-nested-tree vue 3 | 이 프로젝트는 드래그가 가능한 트리 컴포넌트입니다. 이 컴포넌트는 css를 포함하지 않으며, 데모를 사용하기위해 당신의 style을 추가할 필요가 있습니다. 데모의 style은 어렵지 않습니다. 4 | 5 | * [데모](https://codepen.io/phphe/pen/KRapQm) 6 | * [ie11 예제](https://github.com/phphe/vue-draggable-nested-tree/tree/master/ie11-example) 7 | * [영어 문서](https://github.com/phphe/vue-draggable-nested-tree/blob/master/README.md) 8 | * [중국어 문서](https://github.com/phphe/vue-draggable-nested-tree/blob/master/README_CN.md) 9 | 10 | # 터치 11 | 터치를 지원합니다 (1개의 아이템) 12 | 13 | # 기부 14 | [페이팔](https://www.paypal.me/phphe) | [알리페이](https://github.com/phphe/my-alipay-wechat-qr-code/blob/master/alipay.jpg) | [위챗](https://github.com/phphe/my-alipay-wechat-qr-code/blob/master/wechat.png) 15 | 16 | # 목차 17 | * [vue-draggable-nested-tree](#vue_draggable_nested_tree) 18 | * [install](#install) 19 | * [usage](#usage) 20 | * [import](#import) 21 | * [data](#data) 22 | * [template](#template) 23 | * [template for old browsers(eg: IE)](#template_for_old_browsers) 24 | * [api](#api) 25 | * [Tree props](#tree_props) 26 | * [Noraml - Tree props](#noraml_tree_props) 27 | * [Hooks - Tree props](#hooks_tree_props) 28 | * [draggableHelperInfo: {event, options, store}](#draggable_helper_info) 29 | * [Tree properties](#tree_properties) 30 | * [Tree events](#tree_events) 31 | * [Tree methods](#tree_methods) 32 | * [node properties](#node_properties) 33 | * [node deep properties example](#node_deep_properties_example) 34 | * [other](#other) 35 | * [demo css](#demo_css) 36 | * [examples](#examples) 37 | * [draggable & droppable](#draggable_&_droppable) 38 | * [Traverse tree](#traverse_tree) 39 | * [draggable library](#draggable_library) 40 | 41 | 42 | # 설치 43 | ```sh 44 | npm i vue-draggable-nested-tree 45 | ``` 46 | 47 | # 사용 48 | 49 | ### 함수 import 50 | ```js 51 | import {DraggableTree} from 'vue-draggable-nested-tree' 52 | // vue-draggable-nested-tree contains Tree, TreeNode, DraggableTree, DraggableTreeNode 53 | // import the component and register it as global or local component 54 | ``` 55 | 56 | ### json 데이터 57 | ```js 58 | data: [ 59 | {text: 'node 1'}, 60 | {text: 'node 2'}, 61 | {text: 'node 3 undraggable', draggable: false}, 62 | {text: 'node 4'}, 63 | {text: 'node 4 undroppable', droppable: false}, 64 | {text: 'node 5', children: [ 65 | {text: 'node 1'}, 66 | {text: 'node 2', children: [ 67 | {text: 'node 3'}, 68 | {text: 'node 4'}, 69 | ]}, 70 | {text: 'node 2 undroppable', droppable: false, children: [ 71 | {text: 'node 3'}, 72 | {text: 'node 4'}, 73 | ]}, 74 | {text: 'node 2', children: [ 75 | {text: 'node 3'}, 76 | {text: 'node 4 undroppable', droppable: false}, 77 | ]}, 78 | {text: 'node 3'}, 79 | {text: 'node 4'}, 80 | {text: 'node 3'}, 81 | {text: 'node 4'}, 82 | {text: 'node 3'}, 83 | {text: 'node 4'}, 84 | {text: 'node 3'}, 85 | {text: 'node 4'}, 86 | ]}, 87 | ] 88 | ``` 89 | 90 | ### template 91 | ```pug 92 | Tree(:data="data" draggable crossTree) 93 | div(slot-scope="{data, store, vm}") 94 | //- data is node 95 | //- store is the tree 96 | //- vm is node Vue instance, you can get node level by vm.level 97 | template(v-if="!data.isDragPlaceHolder") 98 | b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}}  99 | span {{data.text}} 100 | ``` 101 | 102 | ### 오래된 브라우저를 위한 template(eg: IE) 103 | ```pug 104 | //- slot-scope="{data, store, vm}" may not work in old browsers, replace with slot-scope="slot" 105 | Tree(:data="data" draggable crossTree) 106 | div(slot-scope="slot") 107 | //- data is node 108 | //- store is the tree 109 | //- vm is node Vue instance, you can get node level by vm.level 110 | template(v-if="!slot.data.isDragPlaceHolder") 111 | b(v-if="slot.data.children && slot.data.children.length" @click="slot.store.toggleOpen(slot.data)") {{slot.data.open ? '-' : '+'}}  112 | span {{slot.data.text}} 113 | ``` 114 | 115 | # api 116 | **'store'는 tree에 있는 VM입니다.** 117 | 118 | ### Tree props 119 | 120 | ###### 기본 - Tree props 121 | ```js 122 | // base tree 123 | data: {}, // type Array 124 | indent: {default: 16}, 125 | activatedClass: {default: 'active'}, 126 | openedClass: {default: 'open'}, 127 | space: {default: 10}, // space between node, unit px 128 | // draggable tree 129 | preventSelect: {default: true}, // if to prevent drag handler text be selected when drag, excluding input and textarea 130 | getTriggerEl: {type: Function}, // get the el trigger drag, default is node self. arguments(nodeVm) 131 | draggable: {}, // is the tree draggable, default false 132 | droppable: {default: true}, // is the tree droppable, default true 133 | crossTree: {}, // can a node of the tree be dragged into other tree, or receive other tree node 134 | ``` 135 | 136 | ###### 훅 - Tree props 137 | ```js 138 | ondragstart: {type: Function}, // hook. return false to prevent drag. arguments(node, draggableHelperInfo) 139 | ondragend: {type: Function}, // hook. return false to prevent drop. arguments(node, draggableHelperInfo) 140 | ``` 141 | 142 | ###### draggableHelperInfo 143 | {event, options, store} 144 | 145 | ### Tree properties 146 | ```js 147 | // base 148 | rootData, // generated by tree 149 | // draggable 150 | dplh, // drag placeholder. globally unique. 151 | trees, // array, all trees in the app. globally unique. 152 | ``` 153 | 154 | ### Tree 이벤트 155 | ```js 156 | // store is the tree vm 157 | drag(node), // on drag start. 158 | drop(node, targetTree, oldTree), // after drop. 159 | change(node, targetTree, oldTree), // after drop, only when the node position changed 160 | nodeOpenChanged(node) // on a node is closed or open 161 | ``` 162 | * targetTree와 oldTree 는 tree vm입니다. 163 | * oldTree is 오직 cross tree에서만 사용이 가능합니다. 그 외에는 null입니다.. 164 | * 만약 cross tree를 사용중이라면 targetTree와 oldTree 는 드롭할 때 값이 바뀔것입니다. 165 | 166 | ### Tree 함수 167 | ```js 168 | pure(node, withChildren, after) 169 | /* 170 | pure 171 | return a node data without runtime properties.(!: property which starts with '_' will be removed) 172 | withChildren: optional. after: Function, optional 173 | the code about after(t is computed node data): 174 | if (after) { 175 | return after(t, node) || t 176 | } 177 | return t 178 | */ 179 | getNodeById(id) 180 | getActivated() 181 | getOpened() 182 | activeNode(node, inactiveOld) 183 | toggleActive(node, inactiveOld) 184 | openNode(node, closeOld) 185 | toggleOpen(node, closeOld) 186 | // follow methods are easy, so I paste their soure code 187 | getPureData(after) { return this.pure(this.rootData, true, after).children } // after: Function, optional 188 | deleteNode(node) { return hp.arrayRemove(node.parent.children, node) } 189 | // add node: like array. eg: node.children.push(newNodeData) 190 | // update node: just assign to the node properties directly 191 | isNodeDraggable(node) 192 | isNodeDroppable(node) 193 | ``` 194 | 195 | ### node 속성 196 | ```js 197 | // base 198 | _id 199 | _vm 200 | parent 201 | children: [], 202 | open, 203 | active: false, 204 | style: {}, 205 | class: '', 206 | innerStyle: {}, 207 | innerClass: '', 208 | innerBackStyle: {}, 209 | innerBackClass: {}, 210 | // draggable 211 | draggable // default true. Please check 'draggable & droppable' below 212 | droppable // default true. Please check 'draggable & droppable' below 213 | isDragPlaceHolder 214 | ``` 215 | 216 | #### node의 아래 속성 예제 217 | ```js 218 | node._vm // vm 219 | node._vm.level // 노드 레벨 220 | node._vm.store // 트리 221 | node.parent._vm // 부모 node vm 222 | node._vm.store 223 | ``` 224 | 225 | # 기타 226 | 227 | ### 데모 css 228 | ```css 229 | .he-tree{ 230 | border: 1px solid #ccc; 231 | padding: 20px; 232 | width: 300px; 233 | } 234 | .tree-node{ 235 | } 236 | .tree-node-inner{ 237 | padding: 5px; 238 | border: 1px solid #ccc; 239 | cursor: pointer; 240 | } 241 | .draggable-placeholder{ 242 | } 243 | .draggable-placeholder-inner{ 244 | border: 1px dashed #0088F8; 245 | box-sizing: border-box; 246 | background: rgba(0, 136, 249, 0.09); 247 | color: #0088f9; 248 | text-align: center; 249 | padding: 0; 250 | display: flex; 251 | align-items: center; 252 | } 253 | ``` 254 | 255 | ### 예제 256 | 이 패키지를 clone합니다. 그리고, 257 | ```sh 258 | npm install 259 | npm run dev 260 | ``` 261 | * [기본 예제](https://github.com/phphe/vue-draggable-nested-tree/blob/master/src/examples/Base.vue) 262 | * [트리의 최대 레벨설정](https://github.com/phphe/vue-draggable-nested-tree/blob/master/src/examples/MaxLevel.vue) 263 | 264 | 265 | ### draggable & droppable 266 | 한개의 node는 기본으로 드래그&드랍이 사용 가능합니다. 당신은 node의 속성을 드래그&드랍으로 설정 가능합니다. 다른방법으로는 'drag'이벤트를 기다리다가 모든 데이터를 드래그 혹은 드랍속성으로 세팅하는것입니다. 267 | 268 | ### Traverse tree 269 | 나의 다른 라이브러리인 [tree-helper](https://github.com/phphe/tree-helper)를 추천한다.. 그것은 2가지의 가로트리를 지원한다: depthFirstSearch, breadthFirstSearch. 270 | 271 | ### draggable library 272 | [draggable-helper](https://github.com/phphe/draggable-helper) 드래그를 위한 제가 개발한 다른 라이브러리입니다.. 그리고 그것은 이 컴포넌트를 통해 사용하고 있습니다. 드래그 함수를 사용할 때 도움이 될것입니다. 273 | -------------------------------------------------------------------------------- /UPGRADE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | ### 1.0.x -> 2.0.0 4 | 5 | #### `level` moved into node 6 | 7 | The `level` is in node vm in 1.0.*. It has been moved into node since 2.0.0. You may use it in the slot like following: 8 | ```pug 9 | Tree 10 | div(slot-scope="{data, store, level}") {{level}} 11 | ``` 12 | Change to: 13 | 14 | ```pug 15 | Tree 16 | div(slot-scope="{data, store}") {{data.level}} 17 | ``` 18 | ### 2.1.8 -> 2.2.0 19 | 20 | #### `level` moved into node vm from node. `vm` add to slot props 21 | 22 | ```pug 23 | Tree 24 | div(slot-scope="{data, store}") {{data.level}} 25 | ``` 26 | Change to: 27 | 28 | ```pug 29 | Tree 30 | div(slot-scope="{data, store, vm}") {{vm.level}} 31 | ``` 32 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /build/build-lib.js: -------------------------------------------------------------------------------- 1 | // use bili to build lib 2 | // vue-cli 3 support build as library, but don't support output as es module because of webpack 3 | const bili = require('bili'); 4 | const fs = require('fs'); 5 | 6 | bili.write({ 7 | input: './src/lib-entry.js', 8 | format: ['cjs','umd','umd-min','es'], 9 | banner: true, 10 | plugin: ['vue'], 11 | }).then(() => { 12 | console.log('Done!') 13 | }) 14 | -------------------------------------------------------------------------------- /dist/vue-draggable-nested-tree.cjs.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-draggable-nested-tree v2.3.0-beta.1 3 | * (c) 2018-present phphe 4 | * Released under the MIT License. 5 | */ 6 | 'use strict'; 7 | 8 | Object.defineProperty(exports, '__esModule', { value: true }); 9 | 10 | function _interopDefault(ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 11 | 12 | var keys = _interopDefault(require('core-js/library/fn/object/keys')); 13 | var assign = _interopDefault(require('core-js/library/fn/object/assign')); 14 | var th = require('tree-helper'); 15 | require('core-js/modules/web.dom.iterable'); 16 | require('core-js/modules/es6.number.constructor'); 17 | var hp = require('helper-js'); 18 | var defineProperty = _interopDefault(require('core-js/library/fn/object/define-property')); 19 | require('core-js/modules/es6.function.name'); 20 | var getIterator = _interopDefault(require('core-js/library/fn/get-iterator')); 21 | require('core-js/modules/es6.array.find'); 22 | var vf = require('vue-functions'); 23 | require('core-js/modules/es6.regexp.replace'); 24 | var draggableHelper = _interopDefault(require('draggable-helper')); 25 | 26 | var keys$1 = keys; 27 | 28 | var assign$1 = assign; 29 | 30 | // 31 | var script = { 32 | name: 'TreeNode', 33 | props: { 34 | data: {}, 35 | store: {}, 36 | level: { 37 | default: 0 38 | } // readonly 39 | 40 | }, 41 | data: function data() { 42 | return { 43 | vm: this 44 | }; 45 | }, 46 | computed: { 47 | childrenLevel: function childrenLevel() { 48 | return this.level + 1; 49 | }, 50 | isRoot: function isRoot() { 51 | return this.data && this.data.isRoot; 52 | }, 53 | childrenVisible: function childrenVisible() { 54 | var data = this.data; 55 | return this.isRoot || data && data.children && data.children.length && data.open; 56 | }, 57 | innerBackStyle: function innerBackStyle() { 58 | var r = { 59 | marginBottom: this.store.space + 'px' 60 | }; 61 | 62 | if (!this.isRoot && this.level > 1) { 63 | if (this.store.dir === 'rtl') { 64 | r.paddingRight = (this.level - 1) * this.store.indent + 'px'; 65 | } else { 66 | r.paddingLeft = (this.level - 1) * this.store.indent + 'px'; 67 | } 68 | } 69 | 70 | return r; 71 | } 72 | }, 73 | watch: { 74 | data: { 75 | immediate: true, 76 | handler: function handler(data) { 77 | if (data) { 78 | data._vm = this; 79 | 80 | if (!data._treeNodePropertiesCompleted && !data.isRoot) { 81 | this.store.compeleteNode(data, this.$parent.data); 82 | } 83 | } 84 | } 85 | } 86 | } // methods: {}, 87 | // created() {}, 88 | // mounted() {}, 89 | 90 | }; 91 | 92 | /* script */ 93 | const __vue_script__ = script; 94 | 95 | /* template */ 96 | 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(), _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 }, 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 } }]) }) }), 1) : _vm._e()])], 2) }; 97 | var __vue_staticRenderFns__ = []; 98 | 99 | /* style */ 100 | const __vue_inject_styles__ = undefined; 101 | /* scoped */ 102 | const __vue_scope_id__ = undefined; 103 | /* module identifier */ 104 | const __vue_module_identifier__ = undefined; 105 | /* functional template */ 106 | const __vue_is_functional_template__ = false; 107 | /* component normalizer */ 108 | function __vue_normalize__( 109 | template, style, script$$1, 110 | scope, functional, moduleIdentifier, 111 | createInjector, createInjectorSSR 112 | ) { 113 | const component = (typeof script$$1 === 'function' ? script$$1.options : script$$1) || {}; 114 | 115 | // For security concerns, we use only base name in production mode. 116 | component.__file = "TreeNode.vue"; 117 | 118 | if (!component.render) { 119 | component.render = template.render; 120 | component.staticRenderFns = template.staticRenderFns; 121 | component._compiled = true; 122 | 123 | if (functional) component.functional = true; 124 | } 125 | 126 | component._scopeId = scope; 127 | 128 | return component 129 | } 130 | /* style inject */ 131 | 132 | /* style inject SSR */ 133 | 134 | 135 | 136 | var TreeNode = __vue_normalize__( 137 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 138 | __vue_inject_styles__, 139 | __vue_script__, 140 | __vue_scope_id__, 141 | __vue_is_functional_template__, 142 | __vue_module_identifier__, 143 | undefined, 144 | undefined 145 | ); 146 | 147 | var script$1 = { 148 | props: { 149 | data: {}, 150 | idLength: { 151 | type: Number, 152 | default: 5 153 | }, 154 | indent: { 155 | type: Number, 156 | default: 16 157 | }, 158 | activatedClass: { 159 | default: 'active' 160 | }, 161 | openedClass: { 162 | default: 'open' 163 | }, 164 | space: { 165 | type: Number, 166 | default: 10 167 | }, 168 | // space between node, unit px 169 | childrenTransitionName: {}, 170 | // there are issues under draggable tree 171 | customInnerBack: {} 172 | }, 173 | components: { 174 | TreeNode: TreeNode 175 | }, 176 | data: function data() { 177 | return { 178 | store: this, 179 | rootData: null 180 | }; 181 | }, 182 | // computed: {}, 183 | watch: { 184 | data: { 185 | immediate: true, 186 | handler: function handler(data, old) { 187 | var _this = this; 188 | 189 | if (data === old) { 190 | return; 191 | } // make rootData always use a same object 192 | 193 | 194 | this.rootData = this.rootData || { 195 | isRoot: true, 196 | _id: "tree_".concat(this._uid, "_node_root"), 197 | children: [] 198 | }; 199 | th.breadthFirstSearch(data, function (node, k, parent) { 200 | _this.compeleteNode(node, parent); 201 | }); 202 | this.rootData.children = data; 203 | } 204 | } 205 | }, 206 | methods: { 207 | compeleteNode: function compeleteNode(node, parent) { 208 | var compeletedData = { 209 | open: true, 210 | children: [], 211 | active: false, 212 | style: {}, 213 | class: '', 214 | innerStyle: {}, 215 | innerClass: '', 216 | innerBackStyle: {}, 217 | innerBackClass: {} 218 | }; 219 | 220 | for (var key in compeletedData) { 221 | if (!node.hasOwnProperty(key)) { 222 | this.$set(node, key, compeletedData[key]); 223 | } 224 | } 225 | 226 | this.$set(node, 'parent', parent || this.rootData); 227 | 228 | if (!node.hasOwnProperty('_id')) { 229 | node._id = "tree_".concat(this._uid, "_node_").concat(hp.strRand(this.idLength)); 230 | } 231 | 232 | node._treeNodePropertiesCompleted = true; 233 | }, 234 | // pure node self 235 | pure: function pure(node, withChildren, after) { 236 | var _this2 = this; 237 | 238 | var t = assign$1({}, node); 239 | 240 | delete t._id; 241 | delete t.parent; 242 | delete t.children; 243 | delete t.open; 244 | delete t.active; 245 | delete t.style; 246 | delete t.class; 247 | delete t.innerStyle; 248 | delete t.innerClass; 249 | delete t.innerBackStyle; 250 | delete t.innerBackClass; 251 | 252 | var _arr = keys$1(t); 253 | 254 | for (var _i = 0; _i < _arr.length; _i++) { 255 | var key = _arr[_i]; 256 | 257 | if (key[0] === '_') { 258 | delete t[key]; 259 | } 260 | } 261 | 262 | if (withChildren && node.children) { 263 | t.children = node.children.slice(); 264 | t.children.forEach(function (v, k) { 265 | t.children[k] = _this2.pure(v, withChildren); 266 | }); 267 | } 268 | 269 | if (after) { 270 | return after(t, node) || t; 271 | } 272 | 273 | return t; 274 | }, 275 | getNodeById: function getNodeById(id) { 276 | var r; 277 | th.breadthFirstSearch(this.rootData.children, function (node) { 278 | if (node._id === id) { 279 | r = node; 280 | return false; 281 | } 282 | }); 283 | return r; 284 | }, 285 | getActivated: function getActivated() { 286 | var r = []; 287 | th.breadthFirstSearch(this.rootData.children, function (node) { 288 | if (node.active) { 289 | r.push(node); 290 | } 291 | }); 292 | return r; 293 | }, 294 | getOpened: function getOpened() { 295 | var r = []; 296 | th.breadthFirstSearch(this.rootData.children, function (node) { 297 | if (node.open) { 298 | r.push(node); 299 | } 300 | }); 301 | return r; 302 | }, 303 | activeNode: function activeNode(node, inactiveOld) { 304 | var activated = this.activated; 305 | 306 | if (inactiveOld) { 307 | this.getActivated().forEach(function (node2) { 308 | node2.active = false; 309 | }); 310 | } 311 | 312 | node.active = true; 313 | }, 314 | toggleActive: function toggleActive(node, inactiveOld) { 315 | if (node.active) { 316 | node.active = false; 317 | } else { 318 | this.activeNode(node, inactiveOld); 319 | } 320 | }, 321 | openNode: function openNode(node, closeOld) { 322 | var _this3 = this; 323 | 324 | var opened = this.opened; 325 | 326 | if (closeOld) { 327 | this.getOpened().forEach(function (node2) { 328 | node2.open = false; 329 | 330 | _this3.$emit('nodeOpenChanged', node2); 331 | }); 332 | } 333 | 334 | node.open = true; 335 | this.$emit('nodeOpenChanged', node); 336 | }, 337 | toggleOpen: function toggleOpen(node, closeOld) { 338 | if (node.open) { 339 | node.open = false; 340 | this.$emit('nodeOpenChanged', node); 341 | } else { 342 | this.openNode(node, closeOld); 343 | } 344 | }, 345 | getPureData: function getPureData(after) { 346 | return this.pure(this.rootData, true, after).children; 347 | }, 348 | deleteNode: function deleteNode(node) { 349 | return hp.arrayRemove(node.parent.children, node); 350 | } 351 | } // created() {}, 352 | // mounted() {}, 353 | 354 | }; 355 | 356 | /* script */ 357 | const __vue_script__$1 = script$1; 358 | 359 | /* template */ 360 | var __vue_render__$1 = function () { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c('div', { staticClass: "he-tree tree" }, [_c('TreeNode', { attrs: { "data": _vm.rootData, "store": _vm.store }, scopedSlots: _vm._u([{ key: "default", fn: function (props) { return [_vm._t("default", null, { data: props.data, store: _vm.store, vm: props.vm })] } }, { key: "node-inner-back", fn: function (props) { return (_vm.customInnerBack) ? [_vm._t("node-inner-back", null, { styleObj: props.styleObj, data: props.data, store: props.store, vm: props.vm })] : undefined } }]) })], 1) }; 361 | var __vue_staticRenderFns__$1 = []; 362 | 363 | /* style */ 364 | const __vue_inject_styles__$1 = undefined; 365 | /* scoped */ 366 | const __vue_scope_id__$1 = undefined; 367 | /* module identifier */ 368 | const __vue_module_identifier__$1 = undefined; 369 | /* functional template */ 370 | const __vue_is_functional_template__$1 = false; 371 | /* component normalizer */ 372 | function __vue_normalize__$1( 373 | template, style, script, 374 | scope, functional, moduleIdentifier, 375 | createInjector, createInjectorSSR 376 | ) { 377 | const component = (typeof script === 'function' ? script.options : script) || {}; 378 | 379 | // For security concerns, we use only base name in production mode. 380 | component.__file = "Tree.vue"; 381 | 382 | if (!component.render) { 383 | component.render = template.render; 384 | component.staticRenderFns = template.staticRenderFns; 385 | component._compiled = true; 386 | 387 | if (functional) component.functional = true; 388 | } 389 | 390 | component._scopeId = scope; 391 | 392 | return component 393 | } 394 | /* style inject */ 395 | 396 | /* style inject SSR */ 397 | 398 | 399 | 400 | var Tree = __vue_normalize__$1( 401 | { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 }, 402 | __vue_inject_styles__$1, 403 | __vue_script__$1, 404 | __vue_scope_id__$1, 405 | __vue_is_functional_template__$1, 406 | __vue_module_identifier__$1, 407 | undefined, 408 | undefined 409 | ); 410 | 411 | var defineProperty$1 = defineProperty; 412 | 413 | function _classCallCheck(instance, Constructor) { 414 | if (!(instance instanceof Constructor)) { 415 | throw new TypeError("Cannot call a class as a function"); 416 | } 417 | } 418 | 419 | function _defineProperties(target, props) { 420 | for (var i = 0; i < props.length; i++) { 421 | var descriptor = props[i]; 422 | descriptor.enumerable = descriptor.enumerable || false; 423 | descriptor.configurable = true; 424 | if ("value" in descriptor) descriptor.writable = true; 425 | 426 | defineProperty$1(target, descriptor.key, descriptor); 427 | } 428 | } 429 | 430 | function _createClass(Constructor, protoProps, staticProps) { 431 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 432 | if (staticProps) _defineProperties(Constructor, staticProps); 433 | return Constructor; 434 | } 435 | 436 | function _defineProperty(obj, key, value) { 437 | if (key in obj) { 438 | defineProperty$1(obj, key, { 439 | value: value, 440 | enumerable: true, 441 | configurable: true, 442 | writable: true 443 | }); 444 | } else { 445 | obj[key] = value; 446 | } 447 | 448 | return obj; 449 | } 450 | 451 | var Cache = 452 | /*#__PURE__*/ 453 | function () { 454 | function Cache() { 455 | _classCallCheck(this, Cache); 456 | 457 | _defineProperty(this, "store", {}); 458 | } 459 | 460 | _createClass(Cache, [{ 461 | key: "has", 462 | value: function has(name) { 463 | return this.store.hasOwnProperty(name); 464 | } 465 | }, { 466 | key: "remember", 467 | value: function remember(name, getter) { 468 | if (!this.has(name)) { 469 | this.store[name] = { 470 | value: getter() 471 | }; 472 | } 473 | 474 | return this.store[name].value; 475 | } 476 | }, { 477 | key: "forget", 478 | value: function forget(name) { 479 | if (name) { 480 | if (this.has(name)) { 481 | delete this.store[name]; 482 | } 483 | } else { 484 | this.store = {}; 485 | } 486 | } 487 | }]); 488 | 489 | return Cache; 490 | }(); 491 | function attachCache(obj, cache, toCache) { 492 | var _loop = function _loop(key) { 493 | defineProperty$1(obj, key, { 494 | get: function get() { 495 | var _this = this; 496 | 497 | return cache.remember(key, function () { 498 | return toCache[key].call(_this); 499 | }); 500 | } 501 | }); 502 | }; 503 | 504 | for (var key in toCache) { 505 | _loop(key); 506 | } 507 | } 508 | 509 | var getIterator$1 = getIterator; 510 | 511 | // from https://gist.github.com/iddan/54d5d9e58311b0495a91bf06de661380 512 | 513 | if (!document.elementsFromPoint) { 514 | document.elementsFromPoint = elementsFromPoint; 515 | } 516 | 517 | function elementsFromPoint(x, y) { 518 | var parents = []; 519 | var parent = void 0; 520 | 521 | do { 522 | if (parent !== document.elementFromPoint(x, y)) { 523 | parent = document.elementFromPoint(x, y); 524 | parents.push(parent); 525 | parent.style.pointerEvents = 'none'; 526 | } else { 527 | parent = false; 528 | } 529 | } while (parent); 530 | 531 | parents.forEach(function (parent) { 532 | return parent.style.pointerEvents = 'all'; 533 | }); 534 | return parents; 535 | } 536 | 537 | function getTreeByPoint(x, y, trees) { 538 | var els = document.elementsFromPoint(x, y); 539 | var treeEl; 540 | var nodeEl; 541 | var betweenEls = []; 542 | var _iteratorNormalCompletion = true; 543 | var _didIteratorError = false; 544 | var _iteratorError = undefined; 545 | 546 | try { 547 | for (var _iterator = getIterator$1(els), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 548 | var _el = _step.value; 549 | 550 | if (!nodeEl) { 551 | if (hp.hasClass(_el, 'tree-node')) { 552 | nodeEl = _el; 553 | } 554 | } else { 555 | // console.log(el); 556 | if (hp.hasClass(_el, 'tree')) { 557 | treeEl = _el; 558 | break; 559 | } 560 | 561 | betweenEls.push(_el); 562 | } 563 | } 564 | } catch (err) { 565 | _didIteratorError = true; 566 | _iteratorError = err; 567 | } finally { 568 | try { 569 | if (!_iteratorNormalCompletion && _iterator.return != null) { 570 | _iterator.return(); 571 | } 572 | } finally { 573 | if (_didIteratorError) { 574 | throw _iteratorError; 575 | } 576 | } 577 | } 578 | 579 | if (treeEl) { 580 | // is target tree is another tree, and be covered by other element, like modal, popup 581 | var covered = false; 582 | 583 | if (!isParent(nodeEl, treeEl)) { 584 | // cross tree 585 | for (var _i = 0; _i < betweenEls.length; _i++) { 586 | var el = betweenEls[_i]; 587 | 588 | if (!isParent(el, treeEl)) { 589 | covered = true; 590 | break; 591 | } 592 | } 593 | } // 594 | 595 | 596 | if (!covered) { 597 | return trees.find(function (v) { 598 | return v.$el === treeEl; 599 | }); 600 | } 601 | } 602 | } 603 | 604 | function isParent(child, parent) { 605 | var cur = child; 606 | 607 | while (cur) { 608 | cur = cur.parentNode; 609 | 610 | if (cur === parent) { 611 | return true; 612 | } 613 | } 614 | } 615 | 616 | // 对 drag placeholder进行的操作 617 | 618 | var targets = { 619 | 'nothing': function nothing(info) { }, 620 | 'after': function after(info) { 621 | insertDplhAfterTo(info.dplh, info.targetNode, info); 622 | }, 623 | 'before': function before(info) { 624 | if (isNodeDroppable(info.targetNode.parent)) { 625 | th.insertBefore(info.dplh, info.targetNode); 626 | } else { 627 | insertDplhAfterTo(info.dplh, info.targetNode.parent, info); 628 | } 629 | }, 630 | 'append': function append(info) { 631 | if (isNodeDroppable(info.targetNode)) { 632 | th.appendTo(info.dplh, info.targetNode); 633 | if (!info.targetNode.open) info.store.toggleOpen(info.targetNode); 634 | } else { 635 | insertDplhAfterTo(info.dplh, info.targetNode, info); 636 | } 637 | }, 638 | 'prepend': function prepend(info) { 639 | if (isNodeDroppable(info.targetNode)) { 640 | th.prependTo(info.dplh, info.targetNode); 641 | if (!info.targetNode.open) info.store.toggleOpen(info.targetNode); 642 | } else { 643 | insertDplhAfterTo(info.dplh, info.targetNode, info); 644 | } 645 | }, 646 | 'after target parent': function afterTargetParent(info) { 647 | insertDplhAfterTo(info.dplh, info.targetNode.parent, info); 648 | }, 649 | // append to prev sibling 650 | 'append prev': function appendPrev(info) { 651 | if (isNodeDroppable(info.targetPrev)) { 652 | th.appendTo(info.dplh, info.targetPrev); 653 | if (!info.targetPrev.open) info.store.toggleOpen(info.targetPrev); 654 | } else { 655 | insertDplhAfterTo(info.dplh, info.targetPrev, info); 656 | } 657 | }, 658 | // append to current tree 659 | 'append current tree': function appendCurrentTree(info) { 660 | if (isNodeDroppable(info.currentTree.rootData)) { 661 | th.appendTo(info.dplh, info.currentTree.rootData); 662 | } 663 | } 664 | }; 665 | 666 | function insertDplhAfterTo(dplh, targetNode, info) { 667 | if (!targetNode) { 668 | return false; 669 | } else { 670 | var closest = findParent(targetNode, function (node) { 671 | return node.parent && isNodeDroppable(node.parent); 672 | }); 673 | 674 | if (closest) { 675 | th.insertAfter(dplh, closest); 676 | } else { 677 | return false; 678 | } 679 | } 680 | 681 | return true; 682 | } 683 | 684 | function isNodeDraggable(node) { 685 | if (!draggableIds.hasOwnProperty(node._id)) { 686 | var r; 687 | 688 | if (node.hasOwnProperty('draggable')) { 689 | r = node.draggable; 690 | } else if (node.parent) { 691 | r = isNodeDraggable(node.parent); 692 | } else { 693 | r = true; 694 | } 695 | 696 | draggableIds[node._id] = r; 697 | } 698 | 699 | return draggableIds[node._id]; 700 | } 701 | function isNodeDroppable(node) { 702 | if (!droppableIds.hasOwnProperty(node._id)) { 703 | var r; 704 | 705 | if (node.hasOwnProperty('droppable')) { 706 | r = node.droppable; 707 | } else if (node.parent) { 708 | r = isNodeDroppable(node.parent); 709 | } else { 710 | r = true; 711 | } 712 | 713 | droppableIds[node._id] = r; 714 | } 715 | 716 | return droppableIds[node._id]; 717 | } // find child, excluding dragging node default 718 | 719 | function findChild(info, children, handler, reverse) { 720 | var len = children.length; 721 | 722 | if (reverse) { 723 | for (var i = len - 1; i >= 0; i--) { 724 | var item = children[i]; // excluding dragging node 725 | 726 | if (item !== info.node) { 727 | if (handler(item, i)) { 728 | return item; 729 | } 730 | } 731 | } 732 | } else { 733 | for (var _i = 0; _i < len; _i++) { 734 | var _item = children[_i]; // excluding dragging node 735 | 736 | if (_item !== info.node) { 737 | if (handler(_item, _i)) { 738 | return _item; 739 | } 740 | } 741 | } 742 | } 743 | } // start from node self 744 | 745 | 746 | function findParent(node, handle) { 747 | var current = node; 748 | 749 | while (current) { 750 | if (handle(current)) { 751 | return current; 752 | } 753 | 754 | current = current.parent; 755 | } 756 | } 757 | 758 | var rules = { 759 | // 另一节点存在 760 | 'targetNode existed': function targetNodeExisted(info) { 761 | return info.targetNode; 762 | }, 763 | // 另一节点是拖动占位节点 764 | 'targetNode is placeholder': function targetNodeIsPlaceholder(info) { 765 | return info.targetNode.isDragPlaceHolder; 766 | }, 767 | // 另一节点在最上面 768 | 'targetNode at top': function targetNodeAtTop(info) { 769 | return info.targetAtTop; 770 | }, 771 | // 另一节点在最下面 772 | 'targetNode at bottom': function targetNodeAtBottom(info) { 773 | return info.targetAtBottom; 774 | }, 775 | // 另一节点是根节点第二个子 776 | 'targetNode is the second child of root': function targetNodeIsTheSecondChildOfRoot(info) { 777 | return info.currentTreeRootSecondChildExcludingDragging === info.targetNode; 778 | }, 779 | // 拖动点坐标在任一树中, 同时, 起始树要可拖出, 当前树要可拖入 780 | 'currentTree existed': function currentTreeExisted(info) { 781 | return info.currentTree; 782 | }, 783 | // 当前树为空(不包括占位节点) 784 | 'currentTree empty': function currentTreeEmpty(info) { 785 | return !findChild(info, info.currentTree.rootData.children, function (v) { 786 | return v; 787 | }); 788 | }, 789 | // 占位节点存在 790 | 'placeholder existed': function placeholderExisted(info) { 791 | return info.dplhEl; 792 | }, 793 | // 占位节点在当前树中 794 | 'placeholder in currentTree': function placeholderInCurrentTree(info) { 795 | return info.dplhElInCurrentTree; 796 | }, 797 | // 占位节点在最上面 798 | 'placeholder at top': function placeholderAtTop(info) { 799 | return info.dplhAtTop; 800 | }, 801 | // 另一节点是打开的 802 | 'targetNode is open': function targetNodeIsOpen(info) { 803 | return info.targetNode.open; 804 | }, 805 | // 另一节点有子(不包括占位节点) 806 | 'targetNode has children excluding placeholder': function targetNodeHasChildrenExcludingPlaceholder(info) { 807 | return findChild(info, info.targetNode.children, function (v) { 808 | return v !== info.dplh; 809 | }); 810 | }, 811 | // 另一节点是第一个节点 812 | 'targetNode is 1st child': function targetNodeIs1stChild(info) { 813 | return findChild(info, info.targetNode.parent.children, function (v) { 814 | return v; 815 | }) === info.targetNode; 816 | }, 817 | // 另一节点是最后节点 818 | 'targetNode is last child': function targetNodeIsLastChild(info) { 819 | return findChild(info, info.targetNode.parent.children, function (v) { 820 | return v; 821 | }, true) === info.targetNode; 822 | }, 823 | // 当前位置在另一节点inner垂直中线上 824 | 'on targetNode middle': function onTargetNodeMiddle(info) { 825 | return info.offset.y <= info.tiMiddleY; 826 | }, 827 | // 当前位置在另一节点inner左边 828 | 'at left': function atLeft(info) { 829 | return info.offset.x < info.tiOffset.x; 830 | }, 831 | 'at right': function atRight(info) { 832 | return info.offset.x > info.tiOffset.x; 833 | }, 834 | // 当前位置在另一节点innner indent位置右边 835 | 'at indent right': function atIndentRight(info) { 836 | return info.offset.x > info.tiOffset.x + info.currentTree.indent; 837 | }, 838 | 'at indent left': function atIndentLeft(info) { 839 | return info.offset.x < info.tiOffset.x + info.currentTree.indent; 840 | } // convert rule output to Boolean 841 | 842 | }; 843 | 844 | var _arr = keys$1(rules); 845 | 846 | var _loop = function _loop() { 847 | var key = _arr[_i2]; 848 | var old = rules[key]; 849 | 850 | rules[key] = function () { 851 | return Boolean(old.apply(void 0, arguments)); 852 | }; 853 | }; 854 | 855 | for (var _i2 = 0; _i2 < _arr.length; _i2++) { 856 | _loop(); 857 | } 858 | 859 | var prevTree; 860 | var droppableIds = {}; 861 | var draggableIds = {}; // context is vm 862 | 863 | function autoMoveDragPlaceHolder(draggableHelperInfo) { 864 | var trees = this.store.trees; 865 | var dhStore = draggableHelperInfo.store; // make info 866 | 867 | var info = { 868 | event: draggableHelperInfo.event, 869 | el: dhStore.el, 870 | vm: this, 871 | node: this.data, 872 | store: this.store, 873 | dplh: this.store.dplh, 874 | draggableHelperData: { 875 | opt: draggableHelperInfo.options, 876 | store: dhStore 877 | } // 878 | 879 | }; 880 | attachCache(info, new Cache(), { 881 | // dragging node coordinate 882 | // 拖动中的节点相关坐标 883 | nodeInnerEl: function nodeInnerEl() { 884 | return this.el.querySelector('.tree-node-inner'); 885 | }, 886 | offset: function offset() { 887 | return hp.getOffset(this.nodeInnerEl); 888 | }, 889 | // left top point 890 | offset2: function offset2() { 891 | return { 892 | x: this.offset.x + this.nodeInnerEl.offsetWidth, 893 | y: this.offset.y + this.nodeInnerEl.offsetHeight 894 | }; 895 | }, 896 | // right bottom point 897 | offsetToViewPort: function offsetToViewPort() { 898 | var r = this.nodeInnerEl.getBoundingClientRect(); 899 | r.x = this.store.dir === 'rtl' ? r.right : r.left; 900 | r.y = r.top; 901 | return r; 902 | }, 903 | // tree 904 | currentTree: function currentTree() { 905 | // const currentTree = trees.find(tree => hp.isOffsetInEl(this.offset.x, this.offset.y, tree.$el)) 906 | var currentTree = getTreeByPoint(this.offsetToViewPort.x, this.offsetToViewPort.y, trees); 907 | 908 | if (currentTree) { 909 | var dragStartTree = this.store; 910 | 911 | if (prevTree == null) { 912 | prevTree = dragStartTree; 913 | } 914 | 915 | if (prevTree !== currentTree) { 916 | if (!vf.isPropTrue(dragStartTree.crossTree) || !vf.isPropTrue(currentTree.crossTree)) { 917 | return; 918 | } 919 | 920 | prevTree = currentTree; 921 | } 922 | 923 | if (!vf.isPropTrue(currentTree.droppable)) { 924 | return; 925 | } 926 | 927 | return currentTree; 928 | } 929 | }, 930 | currentTreeRootEl: function currentTreeRootEl() { 931 | return document.getElementById(this.currentTree.rootData._id); 932 | }, 933 | currentTreeRootOf4: function currentTreeRootOf4() { 934 | return getOf4(this.currentTreeRootEl, this.currentTree.space); 935 | }, 936 | // the second child of currentTree root, excluding dragging node 937 | currentTreeRootSecondChildExcludingDragging: function currentTreeRootSecondChildExcludingDragging() { 938 | var _this = this; 939 | 940 | return this.currentTree.rootData.children.slice(0, 3).filter(function (v) { 941 | return v !== _this.node; 942 | })[1]; 943 | }, 944 | // placeholder 945 | dplhEl: function dplhEl() { 946 | return document.getElementById(this.dplh._id); 947 | }, 948 | dplhElInCurrentTree: function dplhElInCurrentTree() { 949 | return Boolean(this.currentTree.$el.querySelector("#".concat(this.dplh._id))); 950 | }, 951 | dplhOf4: function dplhOf4() { 952 | return getOf4(this.dplhEl, this.currentTree.space); 953 | }, 954 | dplhAtTop: function dplhAtTop() { 955 | return Math.abs(this.dplhOf4.y - this.currentTreeRootOf4.y) < 5; 956 | }, 957 | targetAtTop: function targetAtTop() { 958 | return Math.abs(this.tiOf4.y - this.currentTreeRootOf4.y) < 5; 959 | }, 960 | targetAtBottom: function targetAtBottom() { 961 | return Math.abs(this.tiOf4.y2 - this.currentTreeRootOf4.y2) < 5; 962 | }, 963 | // most related node 964 | // 最相关的另一个节点 965 | targetNode: function targetNode() { 966 | var currentTree = this.currentTree; 967 | 968 | if (!currentTree) { 969 | throw 'no currentTree'; 970 | } // 971 | 972 | 973 | var _this$offset = this.offset, 974 | x = _this$offset.x, 975 | y = _this$offset.y; 976 | var currentNode = currentTree.rootData; 977 | 978 | while (true) { 979 | var children = currentNode.children; 980 | 981 | if (!children) { 982 | break; 983 | } 984 | 985 | if (this.node.parent === currentNode) { 986 | // dragging node is in currentNode children, remove it first 987 | children = children.slice(); 988 | children.splice(children.indexOf(this.node), 1); 989 | } 990 | 991 | if (children.length === 0) { 992 | break; 993 | } 994 | 995 | var t = hp.binarySearch(children, function (node) { 996 | var el = document.getElementById(node._id); 997 | var ty = hp.getOffset(el).y; 998 | var ty2 = ty + el.offsetHeight + currentTree.space; 999 | 1000 | if (ty2 < y) { 1001 | return -1; 1002 | } else if (ty <= y) { 1003 | return 0; 1004 | } else { 1005 | return 1; 1006 | } 1007 | }, null, null, true); 1008 | 1009 | if (t.hit) { 1010 | currentNode = t.value; 1011 | } else { 1012 | if (t.bigger) { 1013 | currentNode = children[t.index - 1]; 1014 | } else { 1015 | currentNode = t.value; 1016 | } 1017 | } 1018 | 1019 | if (!currentNode) { 1020 | currentNode = children[0]; 1021 | break; 1022 | } 1023 | 1024 | if (!currentNode) { 1025 | break; 1026 | } 1027 | 1028 | var innerEl = document.getElementById(currentNode._id).querySelector('.tree-node-inner'); 1029 | var of = getOf4(innerEl, currentTree.space); 1030 | 1031 | if (of.y <= y && y <= of.y2) { 1032 | break; 1033 | } 1034 | } 1035 | 1036 | return currentNode; 1037 | }, 1038 | targetNodeEl: function targetNodeEl() { 1039 | return document.getElementById(this.targetNode._id); 1040 | }, 1041 | // targetNodeInnerElOffset 1042 | tiInnerEl: function tiInnerEl() { 1043 | return this.targetNodeEl.querySelector('.tree-node-inner'); 1044 | }, 1045 | tiOffset: function tiOffset() { 1046 | return hp.getOffset(this.tiInnerEl); 1047 | }, 1048 | tiOf4: function tiOf4() { 1049 | return getOf4(this.tiInnerEl, this.currentTree.space); 1050 | }, 1051 | tiMiddleY: function tiMiddleY() { 1052 | return this.tiOffset.y + this.tiInnerEl.offsetHeight / 2; 1053 | }, 1054 | // 1055 | targetPrevEl: function targetPrevEl() { 1056 | // tree node 之间不要有其他元素, 否则这里会获取到错误的元素 1057 | var r = this.targetNodeEl.previousSibling; 1058 | 1059 | if (hp.hasClass(r, 'dragging')) { 1060 | r = r.previousSibling; 1061 | } 1062 | 1063 | return r; 1064 | }, 1065 | targetPrev: function targetPrev() { 1066 | var id = this.targetPrevEl.getAttribute('id'); 1067 | return this.currentTree.getNodeById(id); 1068 | } 1069 | }); // attachCache end 1070 | // decision start ================================= 1071 | 1072 | var executedRuleCache = {}; // exec rule 1073 | 1074 | var exec = function exec(ruleId) { 1075 | if (!executedRuleCache.hasOwnProperty(ruleId)) { 1076 | var r; 1077 | 1078 | try { 1079 | r = rules[ruleId](info); 1080 | } catch (e) { 1081 | r = e; 1082 | 1083 | try { 1084 | if (process.env.DEVELOPE_SELF) { 1085 | // only visible when develop its self 1086 | console.warn("failed to execute rule '".concat(ruleId, "'"), e); 1087 | } 1088 | } catch (e2) { } 1089 | } 1090 | 1091 | executedRuleCache[ruleId] = r; 1092 | } 1093 | 1094 | return executedRuleCache[ruleId]; 1095 | }; 1096 | 1097 | if (exec('currentTree existed') === true) { 1098 | if (exec('targetNode is placeholder') === false) { 1099 | if (exec('targetNode is the second child of root') === true) { 1100 | if (exec('targetNode has children excluding placeholder') === false) { 1101 | if (exec('on targetNode middle') === true) { 1102 | targets['before'](info); 1103 | } else if (exec('on targetNode middle') === false) { 1104 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1105 | targets['append'](info); 1106 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1107 | targets['after'](info); 1108 | } 1109 | } 1110 | } else if (exec('targetNode has children excluding placeholder') === true) { 1111 | targets['prepend'](info); 1112 | } 1113 | } else if (exec('targetNode is the second child of root') === false) { 1114 | if (exec('currentTree empty') === false) { 1115 | if (exec('targetNode at top') === true) { 1116 | if (exec('placeholder in currentTree') === true) { 1117 | if (exec('targetNode has children excluding placeholder') === false) { 1118 | if (exec('on targetNode middle') === false) { 1119 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1120 | targets['after'](info); 1121 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1122 | targets['append'](info); 1123 | } 1124 | } else if (exec('on targetNode middle') === true) { 1125 | targets['before'](info); 1126 | } 1127 | } else if (exec('targetNode has children excluding placeholder') === true) { 1128 | if (exec('on targetNode middle') === false) { 1129 | targets['prepend'](info); 1130 | } else if (exec('on targetNode middle') === true) { 1131 | targets['before'](info); 1132 | } 1133 | } 1134 | } else if (exec('placeholder in currentTree') === false) { 1135 | targets['before'](info); 1136 | } 1137 | } else if (exec('targetNode at top') === false) { 1138 | if (exec('targetNode at bottom') === false) { 1139 | if (exec('placeholder at top') === true) { 1140 | targets['prepend'](info); 1141 | } else if (exec('placeholder at top') === false) { 1142 | if (exec('targetNode has children excluding placeholder') === true) { 1143 | targets['prepend'](info); 1144 | } else if (exec('targetNode has children excluding placeholder') === false) { 1145 | if (exec('targetNode is 1st child') === false) { 1146 | if (exec('targetNode is last child') === false) { 1147 | if (exec('on targetNode middle') === true) { 1148 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1149 | targets['append'](info); 1150 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1151 | targets['after'](info); 1152 | } 1153 | } else if (exec('on targetNode middle') === false) { 1154 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1155 | targets['append'](info); 1156 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1157 | targets['after'](info); 1158 | } 1159 | } 1160 | } else if (exec('targetNode is last child') === true) { 1161 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1162 | targets['append'](info); 1163 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1164 | targets['after'](info); 1165 | } 1166 | } 1167 | } else if (exec('targetNode is 1st child') === true) { 1168 | if (exec('targetNode is last child') === true) { 1169 | targets['append'](info); 1170 | } else if (exec('targetNode is last child') === false) { 1171 | if (exec('on targetNode middle') === false) { 1172 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1173 | targets['after'](info); 1174 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1175 | targets['append'](info); 1176 | } 1177 | } else if (exec('on targetNode middle') === true) { 1178 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1179 | targets['after'](info); 1180 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1181 | targets['append'](info); 1182 | } 1183 | } 1184 | } 1185 | } 1186 | } 1187 | } 1188 | } else if (exec('targetNode at bottom') === true) { 1189 | if (exec('placeholder in currentTree') === true) { 1190 | if (exec('on targetNode middle') === false) { 1191 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1192 | targets['append'](info); 1193 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1194 | targets['after'](info); 1195 | } 1196 | } else if (exec('on targetNode middle') === true) { 1197 | targets['append'](info); 1198 | } 1199 | } else if (exec('placeholder in currentTree') === false) { 1200 | targets['append'](info); 1201 | } 1202 | } 1203 | } 1204 | } else if (exec('currentTree empty') === true) { 1205 | targets['append current tree'](info); 1206 | } 1207 | } 1208 | } else if (exec('targetNode is placeholder') === true) { 1209 | if (exec('targetNode at bottom') === false) { 1210 | if (exec('targetNode is the second child of root') === false) { 1211 | if (exec('targetNode is 1st child') === true) { 1212 | if (exec('targetNode is last child') === false); else if (exec('targetNode is last child') === true) { 1213 | if (exec('on targetNode middle') === false) { 1214 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1215 | targets['after target parent'](info); 1216 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); 1217 | } else if (exec('on targetNode middle') === true) { 1218 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1219 | targets['after target parent'](info); 1220 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); 1221 | } 1222 | } 1223 | } else if (exec('targetNode is 1st child') === false) { 1224 | if (exec('targetNode is last child') === true) { 1225 | if (exec('on targetNode middle') === true) { 1226 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1227 | targets['after target parent'](info); 1228 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1229 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1230 | targets['append prev'](info); 1231 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1232 | } 1233 | } else if (exec('on targetNode middle') === false) { 1234 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1235 | targets['after target parent'](info); 1236 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1237 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1238 | targets['append prev'](info); 1239 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1240 | } 1241 | } 1242 | } else if (exec('targetNode is last child') === false) { 1243 | if (exec('on targetNode middle') === true) { 1244 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true); else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1245 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1246 | targets['append prev'](info); 1247 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1248 | } 1249 | } else if (exec('on targetNode middle') === false) { 1250 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true); else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1251 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1252 | targets['append prev'](info); 1253 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1254 | } 1255 | } 1256 | } 1257 | } 1258 | } else if (exec('targetNode is the second child of root') === true) { 1259 | if (exec('on targetNode middle') === true) { 1260 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1261 | targets['append prev'](info); 1262 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1263 | } else if (exec('on targetNode middle') === false) { 1264 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1265 | targets['append prev'](info); 1266 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1267 | } 1268 | } 1269 | } else if (exec('targetNode at bottom') === true) { 1270 | if (exec('targetNode is 1st child') === true) { 1271 | if (exec('on targetNode middle') === false) { 1272 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1273 | targets['after target parent'](info); 1274 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); 1275 | } else if (exec('on targetNode middle') === true) { 1276 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1277 | targets['after target parent'](info); 1278 | } 1279 | } 1280 | } else if (exec('targetNode is 1st child') === false) { 1281 | if (exec('on targetNode middle') === false) { 1282 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1283 | targets['after target parent'](info); 1284 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1285 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1286 | targets['append prev'](info); 1287 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1288 | } 1289 | } else if (exec('on targetNode middle') === true) { 1290 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1291 | targets['after target parent'](info); 1292 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1293 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1294 | targets['append prev'](info); 1295 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1296 | } 1297 | } 1298 | } 1299 | } 1300 | } 1301 | } else if (exec('currentTree existed') === false); // decision end ================================= 1302 | // 1303 | 1304 | } 1305 | 1306 | function getOf4(el, space) { 1307 | var r = hp.getOffset(el); 1308 | r.x2 = r.x + el.offsetWidth; 1309 | r.y2 = r.y + el.offsetHeight + space; 1310 | return r; 1311 | } 1312 | 1313 | autoMoveDragPlaceHolder.dragStart = function dragStart() { }; 1314 | 1315 | autoMoveDragPlaceHolder.dragEnd = function dragEnd() { 1316 | prevTree = null; 1317 | droppableIds = {}; 1318 | draggableIds = {}; 1319 | }; 1320 | 1321 | var script$2 = { 1322 | extends: TreeNode, 1323 | name: 'TreeNode', 1324 | mounted: function mounted() { 1325 | var _this = this; 1326 | 1327 | this.store.isNodeDraggable = isNodeDraggable; 1328 | this.store.isNodeDroppable = isNodeDroppable; 1329 | 1330 | if (this.isRoot || this.data.isDragPlaceHolder) { 1331 | return; 1332 | } 1333 | 1334 | var dplh = this.store.dplh; 1335 | this.$watch('store.draggable', function (draggable) { 1336 | if (vf.isPropTrue(draggable)) { 1337 | var triggerEl = _this.store.getTriggerEl ? _this.store.getTriggerEl(_this) : _this.$el.querySelector('.tree-node-inner'); 1338 | _this._draggableDestroy = draggableHelper(triggerEl, { 1339 | preventSelect: vf.isPropTrue(_this.store.preventSelect), 1340 | // trigger el 1341 | getEl: function getEl() { 1342 | return _this.$el; 1343 | }, 1344 | minTranslate: 10, 1345 | drag: function drag(e, opt, store) { 1346 | autoMoveDragPlaceHolder.dragStart(); // this store is not tree 1347 | 1348 | var draggableHelperInfo = { 1349 | event: e, 1350 | options: opt, 1351 | store: store 1352 | }; 1353 | 1354 | if (_this.store.ondragstart && _this.store.ondragstart(_this.data, draggableHelperInfo) === false) { 1355 | return false; 1356 | } 1357 | 1358 | if (!isNodeDraggable(_this.data)) { 1359 | return false; 1360 | } 1361 | 1362 | _this.store.$emit('drag', _this.data); // record start positon 1363 | 1364 | 1365 | var siblings = _this.data.parent.children; 1366 | _this.startPosition = { 1367 | siblings: siblings, 1368 | index: siblings.indexOf(_this.data) // 1369 | 1370 | }; 1371 | dplh.innerStyle.height = store.el.offsetHeight + 'px'; 1372 | th.insertAfter(dplh, _this.data); 1373 | _this.data.class += ' dragging'; // console.log('drag start'); 1374 | }, 1375 | moving: function moving(e, opt, store) { 1376 | if (store.movedCount === 0) { 1377 | return; 1378 | } 1379 | 1380 | var draggableHelperInfo = { 1381 | event: e, 1382 | options: opt, 1383 | store: store 1384 | }; 1385 | return autoMoveDragPlaceHolder.call(_this, draggableHelperInfo); 1386 | }, 1387 | drop: function drop(e, opt, store) { 1388 | autoMoveDragPlaceHolder.dragEnd(); 1389 | var draggableHelperInfo = { 1390 | event: e, 1391 | options: opt, 1392 | store: store 1393 | }; 1394 | 1395 | if (_this.store.ondragend && _this.store.ondragend(_this.data, draggableHelperInfo) === false) { 1396 | hp.arrayRemove(dplh.parent.children, dplh); // can't drop, no change 1397 | } else { 1398 | var targetTree = dplh._vm.store; 1399 | var crossTree = targetTree !== _this.store; 1400 | var oldTree = crossTree ? _this.store : null; 1401 | th.insertAfter(_this.data, dplh); 1402 | hp.arrayRemove(dplh.parent.children, dplh); 1403 | _this.data.class = _this.data.class.replace(/(^| )dragging( |$)/g, ' '); 1404 | targetTree.$emit('drop', _this.data, targetTree, oldTree); 1405 | oldTree && oldTree.$emit('drop', _this.data, targetTree, oldTree); // emit change event if changed 1406 | 1407 | var siblings = _this.data.parent.children; 1408 | 1409 | if (siblings === _this.startPosition.siblings && siblings.indexOf(_this.data) === _this.startPosition.index); else { 1410 | _this.store.$emit('change', _this.data, targetTree, oldTree); 1411 | 1412 | oldTree && oldTree.$emit('change', _this.data, targetTree, oldTree); 1413 | } 1414 | 1415 | _this.startPosition = null; 1416 | } // console.log('drag end'); 1417 | 1418 | } 1419 | }); 1420 | } else { 1421 | if (_this._draggableDestroy) { 1422 | _this._draggableDestroy(); 1423 | 1424 | _this._draggableDestroy = null; 1425 | } 1426 | } 1427 | }, { 1428 | immediate: true 1429 | }); 1430 | } 1431 | }; 1432 | 1433 | /* script */ 1434 | const __vue_script__$2 = script$2; 1435 | 1436 | /* template */ 1437 | 1438 | /* style */ 1439 | const __vue_inject_styles__$2 = undefined; 1440 | /* scoped */ 1441 | const __vue_scope_id__$2 = undefined; 1442 | /* module identifier */ 1443 | const __vue_module_identifier__$2 = undefined; 1444 | /* functional template */ 1445 | const __vue_is_functional_template__$2 = undefined; 1446 | /* component normalizer */ 1447 | function __vue_normalize__$2( 1448 | template, style, script, 1449 | scope, functional, moduleIdentifier, 1450 | createInjector, createInjectorSSR 1451 | ) { 1452 | const component = (typeof script === 'function' ? script.options : script) || {}; 1453 | 1454 | // For security concerns, we use only base name in production mode. 1455 | component.__file = "DraggableTreeNode.vue"; 1456 | 1457 | if (!component.render) { 1458 | component.render = template.render; 1459 | component.staticRenderFns = template.staticRenderFns; 1460 | component._compiled = true; 1461 | 1462 | if (functional) component.functional = true; 1463 | } 1464 | 1465 | component._scopeId = scope; 1466 | 1467 | return component 1468 | } 1469 | /* style inject */ 1470 | 1471 | /* style inject SSR */ 1472 | 1473 | 1474 | 1475 | var DraggableTreeNode = __vue_normalize__$2( 1476 | {}, 1477 | __vue_inject_styles__$2, 1478 | __vue_script__$2, 1479 | __vue_scope_id__$2, 1480 | __vue_is_functional_template__$2, 1481 | __vue_module_identifier__$2, 1482 | undefined, 1483 | undefined 1484 | ); 1485 | 1486 | var trees = []; // for multiple trees 1487 | // DragPlaceHolder, unique 1488 | 1489 | var dplh = { 1490 | _id: 'draggable_tree_drag_placeHolder', 1491 | level: null, 1492 | droppable: false, 1493 | isDragPlaceHolder: true, 1494 | class: 'draggable-placeholder', 1495 | style: {}, 1496 | innerStyle: {}, 1497 | innerClass: 'draggable-placeholder-inner', 1498 | innerBackStyle: {}, 1499 | innerBackClass: 'draggable-placeholder-inner-back' // children: [], 1500 | 1501 | }; 1502 | var script$3 = { 1503 | extends: Tree, 1504 | props: { 1505 | getTriggerEl: { 1506 | type: Function 1507 | }, 1508 | draggable: {}, 1509 | droppable: { 1510 | default: true 1511 | }, 1512 | crossTree: {}, 1513 | ondragstart: { 1514 | type: Function 1515 | }, 1516 | ondragend: { 1517 | type: Function 1518 | }, 1519 | preventSelect: { 1520 | default: true 1521 | }, 1522 | dir: { 1523 | type: String, 1524 | default: 'ltr' 1525 | } 1526 | }, 1527 | components: { 1528 | TreeNode: DraggableTreeNode 1529 | }, 1530 | data: function data() { 1531 | return { 1532 | // DragPlaceHolder 1533 | dplh: dplh, 1534 | trees: trees 1535 | }; 1536 | }, 1537 | // computed: {}, 1538 | // watch: {}, 1539 | // methods: {}, 1540 | created: function created() { 1541 | trees.push(this); 1542 | }, 1543 | mounted: function mounted() { }, 1544 | beforeDestroy: function beforeDestroy() { 1545 | hp.arrayRemove(trees, this); 1546 | } 1547 | }; 1548 | 1549 | /* script */ 1550 | const __vue_script__$3 = script$3; 1551 | 1552 | /* template */ 1553 | 1554 | /* style */ 1555 | const __vue_inject_styles__$3 = undefined; 1556 | /* scoped */ 1557 | const __vue_scope_id__$3 = undefined; 1558 | /* module identifier */ 1559 | const __vue_module_identifier__$3 = undefined; 1560 | /* functional template */ 1561 | const __vue_is_functional_template__$3 = undefined; 1562 | /* component normalizer */ 1563 | function __vue_normalize__$3( 1564 | template, style, script, 1565 | scope, functional, moduleIdentifier, 1566 | createInjector, createInjectorSSR 1567 | ) { 1568 | const component = (typeof script === 'function' ? script.options : script) || {}; 1569 | 1570 | // For security concerns, we use only base name in production mode. 1571 | component.__file = "DraggableTree.vue"; 1572 | 1573 | if (!component.render) { 1574 | component.render = template.render; 1575 | component.staticRenderFns = template.staticRenderFns; 1576 | component._compiled = true; 1577 | 1578 | if (functional) component.functional = true; 1579 | } 1580 | 1581 | component._scopeId = scope; 1582 | 1583 | return component 1584 | } 1585 | /* style inject */ 1586 | 1587 | /* style inject SSR */ 1588 | 1589 | 1590 | 1591 | var DraggableTree = __vue_normalize__$3( 1592 | {}, 1593 | __vue_inject_styles__$3, 1594 | __vue_script__$3, 1595 | __vue_scope_id__$3, 1596 | __vue_is_functional_template__$3, 1597 | __vue_module_identifier__$3, 1598 | undefined, 1599 | undefined 1600 | ); 1601 | 1602 | exports.Tree = Tree; 1603 | exports.TreeNode = TreeNode; 1604 | exports.DraggableTree = DraggableTree; 1605 | exports.DraggableTreeNode = DraggableTreeNode; 1606 | -------------------------------------------------------------------------------- /dist/vue-draggable-nested-tree.es.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-draggable-nested-tree v2.3.0-beta.1 3 | * (c) 2018-present phphe 4 | * Released under the MIT License. 5 | */ 6 | import keys from 'core-js/library/fn/object/keys'; 7 | import assign from 'core-js/library/fn/object/assign'; 8 | import { breadthFirstSearch, insertAfter, insertBefore, appendTo, prependTo } from 'tree-helper'; 9 | import 'core-js/modules/web.dom.iterable'; 10 | import 'core-js/modules/es6.number.constructor'; 11 | import { strRand, arrayRemove, getOffset, binarySearch, hasClass } from 'helper-js'; 12 | import defineProperty from 'core-js/library/fn/object/define-property'; 13 | import 'core-js/modules/es6.function.name'; 14 | import getIterator from 'core-js/library/fn/get-iterator'; 15 | import 'core-js/modules/es6.array.find'; 16 | import { isPropTrue } from 'vue-functions'; 17 | import 'core-js/modules/es6.regexp.replace'; 18 | import draggableHelper from 'draggable-helper'; 19 | 20 | var keys$1 = keys; 21 | 22 | var assign$1 = assign; 23 | 24 | // 25 | var script = { 26 | name: 'TreeNode', 27 | props: { 28 | data: {}, 29 | store: {}, 30 | level: { 31 | default: 0 32 | } // readonly 33 | 34 | }, 35 | data: function data() { 36 | return { 37 | vm: this 38 | }; 39 | }, 40 | computed: { 41 | childrenLevel: function childrenLevel() { 42 | return this.level + 1; 43 | }, 44 | isRoot: function isRoot() { 45 | return this.data && this.data.isRoot; 46 | }, 47 | childrenVisible: function childrenVisible() { 48 | var data = this.data; 49 | return this.isRoot || data && data.children && data.children.length && data.open; 50 | }, 51 | innerBackStyle: function innerBackStyle() { 52 | var r = { 53 | marginBottom: this.store.space + 'px' 54 | }; 55 | 56 | if (!this.isRoot && this.level > 1) { 57 | if (this.store.dir === 'rtl') { 58 | r.paddingRight = (this.level - 1) * this.store.indent + 'px'; 59 | } else { 60 | r.paddingLeft = (this.level - 1) * this.store.indent + 'px'; 61 | } 62 | } 63 | 64 | return r; 65 | } 66 | }, 67 | watch: { 68 | data: { 69 | immediate: true, 70 | handler: function handler(data) { 71 | if (data) { 72 | data._vm = this; 73 | 74 | if (!data._treeNodePropertiesCompleted && !data.isRoot) { 75 | this.store.compeleteNode(data, this.$parent.data); 76 | } 77 | } 78 | } 79 | } 80 | } // methods: {}, 81 | // created() {}, 82 | // mounted() {}, 83 | 84 | }; 85 | 86 | /* script */ 87 | const __vue_script__ = script; 88 | 89 | /* template */ 90 | 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(), _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 }, 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 } }]) }) }), 1) : _vm._e()])], 2) }; 91 | var __vue_staticRenderFns__ = []; 92 | 93 | /* style */ 94 | const __vue_inject_styles__ = undefined; 95 | /* scoped */ 96 | const __vue_scope_id__ = undefined; 97 | /* module identifier */ 98 | const __vue_module_identifier__ = undefined; 99 | /* functional template */ 100 | const __vue_is_functional_template__ = false; 101 | /* component normalizer */ 102 | function __vue_normalize__( 103 | template, style, script$$1, 104 | scope, functional, moduleIdentifier, 105 | createInjector, createInjectorSSR 106 | ) { 107 | const component = (typeof script$$1 === 'function' ? script$$1.options : script$$1) || {}; 108 | 109 | // For security concerns, we use only base name in production mode. 110 | component.__file = "TreeNode.vue"; 111 | 112 | if (!component.render) { 113 | component.render = template.render; 114 | component.staticRenderFns = template.staticRenderFns; 115 | component._compiled = true; 116 | 117 | if (functional) component.functional = true; 118 | } 119 | 120 | component._scopeId = scope; 121 | 122 | return component 123 | } 124 | /* style inject */ 125 | 126 | /* style inject SSR */ 127 | 128 | 129 | 130 | var TreeNode = __vue_normalize__( 131 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 132 | __vue_inject_styles__, 133 | __vue_script__, 134 | __vue_scope_id__, 135 | __vue_is_functional_template__, 136 | __vue_module_identifier__, 137 | undefined, 138 | undefined 139 | ); 140 | 141 | var script$1 = { 142 | props: { 143 | data: {}, 144 | idLength: { 145 | type: Number, 146 | default: 5 147 | }, 148 | indent: { 149 | type: Number, 150 | default: 16 151 | }, 152 | activatedClass: { 153 | default: 'active' 154 | }, 155 | openedClass: { 156 | default: 'open' 157 | }, 158 | space: { 159 | type: Number, 160 | default: 10 161 | }, 162 | // space between node, unit px 163 | childrenTransitionName: {}, 164 | // there are issues under draggable tree 165 | customInnerBack: {} 166 | }, 167 | components: { 168 | TreeNode: TreeNode 169 | }, 170 | data: function data() { 171 | return { 172 | store: this, 173 | rootData: null 174 | }; 175 | }, 176 | // computed: {}, 177 | watch: { 178 | data: { 179 | immediate: true, 180 | handler: function handler(data, old) { 181 | var _this = this; 182 | 183 | if (data === old) { 184 | return; 185 | } // make rootData always use a same object 186 | 187 | 188 | this.rootData = this.rootData || { 189 | isRoot: true, 190 | _id: "tree_".concat(this._uid, "_node_root"), 191 | children: [] 192 | }; 193 | breadthFirstSearch(data, function (node, k, parent) { 194 | _this.compeleteNode(node, parent); 195 | }); 196 | this.rootData.children = data; 197 | } 198 | } 199 | }, 200 | methods: { 201 | compeleteNode: function compeleteNode(node, parent) { 202 | var compeletedData = { 203 | open: true, 204 | children: [], 205 | active: false, 206 | style: {}, 207 | class: '', 208 | innerStyle: {}, 209 | innerClass: '', 210 | innerBackStyle: {}, 211 | innerBackClass: {} 212 | }; 213 | 214 | for (var key in compeletedData) { 215 | if (!node.hasOwnProperty(key)) { 216 | this.$set(node, key, compeletedData[key]); 217 | } 218 | } 219 | 220 | this.$set(node, 'parent', parent || this.rootData); 221 | 222 | if (!node.hasOwnProperty('_id')) { 223 | node._id = "tree_".concat(this._uid, "_node_").concat(strRand(this.idLength)); 224 | } 225 | 226 | node._treeNodePropertiesCompleted = true; 227 | }, 228 | // pure node self 229 | pure: function pure(node, withChildren, after) { 230 | var _this2 = this; 231 | 232 | var t = assign$1({}, node); 233 | 234 | delete t._id; 235 | delete t.parent; 236 | delete t.children; 237 | delete t.open; 238 | delete t.active; 239 | delete t.style; 240 | delete t.class; 241 | delete t.innerStyle; 242 | delete t.innerClass; 243 | delete t.innerBackStyle; 244 | delete t.innerBackClass; 245 | 246 | var _arr = keys$1(t); 247 | 248 | for (var _i = 0; _i < _arr.length; _i++) { 249 | var key = _arr[_i]; 250 | 251 | if (key[0] === '_') { 252 | delete t[key]; 253 | } 254 | } 255 | 256 | if (withChildren && node.children) { 257 | t.children = node.children.slice(); 258 | t.children.forEach(function (v, k) { 259 | t.children[k] = _this2.pure(v, withChildren); 260 | }); 261 | } 262 | 263 | if (after) { 264 | return after(t, node) || t; 265 | } 266 | 267 | return t; 268 | }, 269 | getNodeById: function getNodeById(id) { 270 | var r; 271 | breadthFirstSearch(this.rootData.children, function (node) { 272 | if (node._id === id) { 273 | r = node; 274 | return false; 275 | } 276 | }); 277 | return r; 278 | }, 279 | getActivated: function getActivated() { 280 | var r = []; 281 | breadthFirstSearch(this.rootData.children, function (node) { 282 | if (node.active) { 283 | r.push(node); 284 | } 285 | }); 286 | return r; 287 | }, 288 | getOpened: function getOpened() { 289 | var r = []; 290 | breadthFirstSearch(this.rootData.children, function (node) { 291 | if (node.open) { 292 | r.push(node); 293 | } 294 | }); 295 | return r; 296 | }, 297 | activeNode: function activeNode(node, inactiveOld) { 298 | var activated = this.activated; 299 | 300 | if (inactiveOld) { 301 | this.getActivated().forEach(function (node2) { 302 | node2.active = false; 303 | }); 304 | } 305 | 306 | node.active = true; 307 | }, 308 | toggleActive: function toggleActive(node, inactiveOld) { 309 | if (node.active) { 310 | node.active = false; 311 | } else { 312 | this.activeNode(node, inactiveOld); 313 | } 314 | }, 315 | openNode: function openNode(node, closeOld) { 316 | var _this3 = this; 317 | 318 | var opened = this.opened; 319 | 320 | if (closeOld) { 321 | this.getOpened().forEach(function (node2) { 322 | node2.open = false; 323 | 324 | _this3.$emit('nodeOpenChanged', node2); 325 | }); 326 | } 327 | 328 | node.open = true; 329 | this.$emit('nodeOpenChanged', node); 330 | }, 331 | toggleOpen: function toggleOpen(node, closeOld) { 332 | if (node.open) { 333 | node.open = false; 334 | this.$emit('nodeOpenChanged', node); 335 | } else { 336 | this.openNode(node, closeOld); 337 | } 338 | }, 339 | getPureData: function getPureData(after) { 340 | return this.pure(this.rootData, true, after).children; 341 | }, 342 | deleteNode: function deleteNode(node) { 343 | return arrayRemove(node.parent.children, node); 344 | } 345 | } // created() {}, 346 | // mounted() {}, 347 | 348 | }; 349 | 350 | /* script */ 351 | const __vue_script__$1 = script$1; 352 | 353 | /* template */ 354 | var __vue_render__$1 = function () { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c('div', { staticClass: "he-tree tree" }, [_c('TreeNode', { attrs: { "data": _vm.rootData, "store": _vm.store }, scopedSlots: _vm._u([{ key: "default", fn: function (props) { return [_vm._t("default", null, { data: props.data, store: _vm.store, vm: props.vm })] } }, { key: "node-inner-back", fn: function (props) { return (_vm.customInnerBack) ? [_vm._t("node-inner-back", null, { styleObj: props.styleObj, data: props.data, store: props.store, vm: props.vm })] : undefined } }]) })], 1) }; 355 | var __vue_staticRenderFns__$1 = []; 356 | 357 | /* style */ 358 | const __vue_inject_styles__$1 = undefined; 359 | /* scoped */ 360 | const __vue_scope_id__$1 = undefined; 361 | /* module identifier */ 362 | const __vue_module_identifier__$1 = undefined; 363 | /* functional template */ 364 | const __vue_is_functional_template__$1 = false; 365 | /* component normalizer */ 366 | function __vue_normalize__$1( 367 | template, style, script, 368 | scope, functional, moduleIdentifier, 369 | createInjector, createInjectorSSR 370 | ) { 371 | const component = (typeof script === 'function' ? script.options : script) || {}; 372 | 373 | // For security concerns, we use only base name in production mode. 374 | component.__file = "Tree.vue"; 375 | 376 | if (!component.render) { 377 | component.render = template.render; 378 | component.staticRenderFns = template.staticRenderFns; 379 | component._compiled = true; 380 | 381 | if (functional) component.functional = true; 382 | } 383 | 384 | component._scopeId = scope; 385 | 386 | return component 387 | } 388 | /* style inject */ 389 | 390 | /* style inject SSR */ 391 | 392 | 393 | 394 | var Tree = __vue_normalize__$1( 395 | { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 }, 396 | __vue_inject_styles__$1, 397 | __vue_script__$1, 398 | __vue_scope_id__$1, 399 | __vue_is_functional_template__$1, 400 | __vue_module_identifier__$1, 401 | undefined, 402 | undefined 403 | ); 404 | 405 | var defineProperty$1 = defineProperty; 406 | 407 | function _classCallCheck(instance, Constructor) { 408 | if (!(instance instanceof Constructor)) { 409 | throw new TypeError("Cannot call a class as a function"); 410 | } 411 | } 412 | 413 | function _defineProperties(target, props) { 414 | for (var i = 0; i < props.length; i++) { 415 | var descriptor = props[i]; 416 | descriptor.enumerable = descriptor.enumerable || false; 417 | descriptor.configurable = true; 418 | if ("value" in descriptor) descriptor.writable = true; 419 | 420 | defineProperty$1(target, descriptor.key, descriptor); 421 | } 422 | } 423 | 424 | function _createClass(Constructor, protoProps, staticProps) { 425 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 426 | if (staticProps) _defineProperties(Constructor, staticProps); 427 | return Constructor; 428 | } 429 | 430 | function _defineProperty(obj, key, value) { 431 | if (key in obj) { 432 | defineProperty$1(obj, key, { 433 | value: value, 434 | enumerable: true, 435 | configurable: true, 436 | writable: true 437 | }); 438 | } else { 439 | obj[key] = value; 440 | } 441 | 442 | return obj; 443 | } 444 | 445 | var Cache = 446 | /*#__PURE__*/ 447 | function () { 448 | function Cache() { 449 | _classCallCheck(this, Cache); 450 | 451 | _defineProperty(this, "store", {}); 452 | } 453 | 454 | _createClass(Cache, [{ 455 | key: "has", 456 | value: function has(name) { 457 | return this.store.hasOwnProperty(name); 458 | } 459 | }, { 460 | key: "remember", 461 | value: function remember(name, getter) { 462 | if (!this.has(name)) { 463 | this.store[name] = { 464 | value: getter() 465 | }; 466 | } 467 | 468 | return this.store[name].value; 469 | } 470 | }, { 471 | key: "forget", 472 | value: function forget(name) { 473 | if (name) { 474 | if (this.has(name)) { 475 | delete this.store[name]; 476 | } 477 | } else { 478 | this.store = {}; 479 | } 480 | } 481 | }]); 482 | 483 | return Cache; 484 | }(); 485 | function attachCache(obj, cache, toCache) { 486 | var _loop = function _loop(key) { 487 | defineProperty$1(obj, key, { 488 | get: function get() { 489 | var _this = this; 490 | 491 | return cache.remember(key, function () { 492 | return toCache[key].call(_this); 493 | }); 494 | } 495 | }); 496 | }; 497 | 498 | for (var key in toCache) { 499 | _loop(key); 500 | } 501 | } 502 | 503 | var getIterator$1 = getIterator; 504 | 505 | // from https://gist.github.com/iddan/54d5d9e58311b0495a91bf06de661380 506 | 507 | if (!document.elementsFromPoint) { 508 | document.elementsFromPoint = elementsFromPoint; 509 | } 510 | 511 | function elementsFromPoint(x, y) { 512 | var parents = []; 513 | var parent = void 0; 514 | 515 | do { 516 | if (parent !== document.elementFromPoint(x, y)) { 517 | parent = document.elementFromPoint(x, y); 518 | parents.push(parent); 519 | parent.style.pointerEvents = 'none'; 520 | } else { 521 | parent = false; 522 | } 523 | } while (parent); 524 | 525 | parents.forEach(function (parent) { 526 | return parent.style.pointerEvents = 'all'; 527 | }); 528 | return parents; 529 | } 530 | 531 | function getTreeByPoint(x, y, trees) { 532 | var els = document.elementsFromPoint(x, y); 533 | var treeEl; 534 | var nodeEl; 535 | var betweenEls = []; 536 | var _iteratorNormalCompletion = true; 537 | var _didIteratorError = false; 538 | var _iteratorError = undefined; 539 | 540 | try { 541 | for (var _iterator = getIterator$1(els), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 542 | var _el = _step.value; 543 | 544 | if (!nodeEl) { 545 | if (hasClass(_el, 'tree-node')) { 546 | nodeEl = _el; 547 | } 548 | } else { 549 | // console.log(el); 550 | if (hasClass(_el, 'tree')) { 551 | treeEl = _el; 552 | break; 553 | } 554 | 555 | betweenEls.push(_el); 556 | } 557 | } 558 | } catch (err) { 559 | _didIteratorError = true; 560 | _iteratorError = err; 561 | } finally { 562 | try { 563 | if (!_iteratorNormalCompletion && _iterator.return != null) { 564 | _iterator.return(); 565 | } 566 | } finally { 567 | if (_didIteratorError) { 568 | throw _iteratorError; 569 | } 570 | } 571 | } 572 | 573 | if (treeEl) { 574 | // is target tree is another tree, and be covered by other element, like modal, popup 575 | var covered = false; 576 | 577 | if (!isParent(nodeEl, treeEl)) { 578 | // cross tree 579 | for (var _i = 0; _i < betweenEls.length; _i++) { 580 | var el = betweenEls[_i]; 581 | 582 | if (!isParent(el, treeEl)) { 583 | covered = true; 584 | break; 585 | } 586 | } 587 | } // 588 | 589 | 590 | if (!covered) { 591 | return trees.find(function (v) { 592 | return v.$el === treeEl; 593 | }); 594 | } 595 | } 596 | } 597 | 598 | function isParent(child, parent) { 599 | var cur = child; 600 | 601 | while (cur) { 602 | cur = cur.parentNode; 603 | 604 | if (cur === parent) { 605 | return true; 606 | } 607 | } 608 | } 609 | 610 | // 对 drag placeholder进行的操作 611 | 612 | var targets = { 613 | 'nothing': function nothing(info) { }, 614 | 'after': function after(info) { 615 | insertDplhAfterTo(info.dplh, info.targetNode, info); 616 | }, 617 | 'before': function before(info) { 618 | if (isNodeDroppable(info.targetNode.parent)) { 619 | insertBefore(info.dplh, info.targetNode); 620 | } else { 621 | insertDplhAfterTo(info.dplh, info.targetNode.parent, info); 622 | } 623 | }, 624 | 'append': function append(info) { 625 | if (isNodeDroppable(info.targetNode)) { 626 | appendTo(info.dplh, info.targetNode); 627 | if (!info.targetNode.open) info.store.toggleOpen(info.targetNode); 628 | } else { 629 | insertDplhAfterTo(info.dplh, info.targetNode, info); 630 | } 631 | }, 632 | 'prepend': function prepend(info) { 633 | if (isNodeDroppable(info.targetNode)) { 634 | prependTo(info.dplh, info.targetNode); 635 | if (!info.targetNode.open) info.store.toggleOpen(info.targetNode); 636 | } else { 637 | insertDplhAfterTo(info.dplh, info.targetNode, info); 638 | } 639 | }, 640 | 'after target parent': function afterTargetParent(info) { 641 | insertDplhAfterTo(info.dplh, info.targetNode.parent, info); 642 | }, 643 | // append to prev sibling 644 | 'append prev': function appendPrev(info) { 645 | if (isNodeDroppable(info.targetPrev)) { 646 | appendTo(info.dplh, info.targetPrev); 647 | if (!info.targetPrev.open) info.store.toggleOpen(info.targetPrev); 648 | } else { 649 | insertDplhAfterTo(info.dplh, info.targetPrev, info); 650 | } 651 | }, 652 | // append to current tree 653 | 'append current tree': function appendCurrentTree(info) { 654 | if (isNodeDroppable(info.currentTree.rootData)) { 655 | appendTo(info.dplh, info.currentTree.rootData); 656 | } 657 | } 658 | }; 659 | 660 | function insertDplhAfterTo(dplh, targetNode, info) { 661 | if (!targetNode) { 662 | return false; 663 | } else { 664 | var closest = findParent(targetNode, function (node) { 665 | return node.parent && isNodeDroppable(node.parent); 666 | }); 667 | 668 | if (closest) { 669 | insertAfter(dplh, closest); 670 | } else { 671 | return false; 672 | } 673 | } 674 | 675 | return true; 676 | } 677 | 678 | function isNodeDraggable(node) { 679 | if (!draggableIds.hasOwnProperty(node._id)) { 680 | var r; 681 | 682 | if (node.hasOwnProperty('draggable')) { 683 | r = node.draggable; 684 | } else if (node.parent) { 685 | r = isNodeDraggable(node.parent); 686 | } else { 687 | r = true; 688 | } 689 | 690 | draggableIds[node._id] = r; 691 | } 692 | 693 | return draggableIds[node._id]; 694 | } 695 | function isNodeDroppable(node) { 696 | if (!droppableIds.hasOwnProperty(node._id)) { 697 | var r; 698 | 699 | if (node.hasOwnProperty('droppable')) { 700 | r = node.droppable; 701 | } else if (node.parent) { 702 | r = isNodeDroppable(node.parent); 703 | } else { 704 | r = true; 705 | } 706 | 707 | droppableIds[node._id] = r; 708 | } 709 | 710 | return droppableIds[node._id]; 711 | } // find child, excluding dragging node default 712 | 713 | function findChild(info, children, handler, reverse) { 714 | var len = children.length; 715 | 716 | if (reverse) { 717 | for (var i = len - 1; i >= 0; i--) { 718 | var item = children[i]; // excluding dragging node 719 | 720 | if (item !== info.node) { 721 | if (handler(item, i)) { 722 | return item; 723 | } 724 | } 725 | } 726 | } else { 727 | for (var _i = 0; _i < len; _i++) { 728 | var _item = children[_i]; // excluding dragging node 729 | 730 | if (_item !== info.node) { 731 | if (handler(_item, _i)) { 732 | return _item; 733 | } 734 | } 735 | } 736 | } 737 | } // start from node self 738 | 739 | 740 | function findParent(node, handle) { 741 | var current = node; 742 | 743 | while (current) { 744 | if (handle(current)) { 745 | return current; 746 | } 747 | 748 | current = current.parent; 749 | } 750 | } 751 | 752 | var rules = { 753 | // 另一节点存在 754 | 'targetNode existed': function targetNodeExisted(info) { 755 | return info.targetNode; 756 | }, 757 | // 另一节点是拖动占位节点 758 | 'targetNode is placeholder': function targetNodeIsPlaceholder(info) { 759 | return info.targetNode.isDragPlaceHolder; 760 | }, 761 | // 另一节点在最上面 762 | 'targetNode at top': function targetNodeAtTop(info) { 763 | return info.targetAtTop; 764 | }, 765 | // 另一节点在最下面 766 | 'targetNode at bottom': function targetNodeAtBottom(info) { 767 | return info.targetAtBottom; 768 | }, 769 | // 另一节点是根节点第二个子 770 | 'targetNode is the second child of root': function targetNodeIsTheSecondChildOfRoot(info) { 771 | return info.currentTreeRootSecondChildExcludingDragging === info.targetNode; 772 | }, 773 | // 拖动点坐标在任一树中, 同时, 起始树要可拖出, 当前树要可拖入 774 | 'currentTree existed': function currentTreeExisted(info) { 775 | return info.currentTree; 776 | }, 777 | // 当前树为空(不包括占位节点) 778 | 'currentTree empty': function currentTreeEmpty(info) { 779 | return !findChild(info, info.currentTree.rootData.children, function (v) { 780 | return v; 781 | }); 782 | }, 783 | // 占位节点存在 784 | 'placeholder existed': function placeholderExisted(info) { 785 | return info.dplhEl; 786 | }, 787 | // 占位节点在当前树中 788 | 'placeholder in currentTree': function placeholderInCurrentTree(info) { 789 | return info.dplhElInCurrentTree; 790 | }, 791 | // 占位节点在最上面 792 | 'placeholder at top': function placeholderAtTop(info) { 793 | return info.dplhAtTop; 794 | }, 795 | // 另一节点是打开的 796 | 'targetNode is open': function targetNodeIsOpen(info) { 797 | return info.targetNode.open; 798 | }, 799 | // 另一节点有子(不包括占位节点) 800 | 'targetNode has children excluding placeholder': function targetNodeHasChildrenExcludingPlaceholder(info) { 801 | return findChild(info, info.targetNode.children, function (v) { 802 | return v !== info.dplh; 803 | }); 804 | }, 805 | // 另一节点是第一个节点 806 | 'targetNode is 1st child': function targetNodeIs1stChild(info) { 807 | return findChild(info, info.targetNode.parent.children, function (v) { 808 | return v; 809 | }) === info.targetNode; 810 | }, 811 | // 另一节点是最后节点 812 | 'targetNode is last child': function targetNodeIsLastChild(info) { 813 | return findChild(info, info.targetNode.parent.children, function (v) { 814 | return v; 815 | }, true) === info.targetNode; 816 | }, 817 | // 当前位置在另一节点inner垂直中线上 818 | 'on targetNode middle': function onTargetNodeMiddle(info) { 819 | return info.offset.y <= info.tiMiddleY; 820 | }, 821 | // 当前位置在另一节点inner左边 822 | 'at left': function atLeft(info) { 823 | return info.offset.x < info.tiOffset.x; 824 | }, 825 | 'at right': function atRight(info) { 826 | return info.offset.x > info.tiOffset.x; 827 | }, 828 | // 当前位置在另一节点innner indent位置右边 829 | 'at indent right': function atIndentRight(info) { 830 | return info.offset.x > info.tiOffset.x + info.currentTree.indent; 831 | }, 832 | 'at indent left': function atIndentLeft(info) { 833 | return info.offset.x < info.tiOffset.x + info.currentTree.indent; 834 | } // convert rule output to Boolean 835 | 836 | }; 837 | 838 | var _arr = keys$1(rules); 839 | 840 | var _loop = function _loop() { 841 | var key = _arr[_i2]; 842 | var old = rules[key]; 843 | 844 | rules[key] = function () { 845 | return Boolean(old.apply(void 0, arguments)); 846 | }; 847 | }; 848 | 849 | for (var _i2 = 0; _i2 < _arr.length; _i2++) { 850 | _loop(); 851 | } 852 | 853 | var prevTree; 854 | var droppableIds = {}; 855 | var draggableIds = {}; // context is vm 856 | 857 | function autoMoveDragPlaceHolder(draggableHelperInfo) { 858 | var trees = this.store.trees; 859 | var dhStore = draggableHelperInfo.store; // make info 860 | 861 | var info = { 862 | event: draggableHelperInfo.event, 863 | el: dhStore.el, 864 | vm: this, 865 | node: this.data, 866 | store: this.store, 867 | dplh: this.store.dplh, 868 | draggableHelperData: { 869 | opt: draggableHelperInfo.options, 870 | store: dhStore 871 | } // 872 | 873 | }; 874 | attachCache(info, new Cache(), { 875 | // dragging node coordinate 876 | // 拖动中的节点相关坐标 877 | nodeInnerEl: function nodeInnerEl() { 878 | return this.el.querySelector('.tree-node-inner'); 879 | }, 880 | offset: function offset() { 881 | return getOffset(this.nodeInnerEl); 882 | }, 883 | // left top point 884 | offset2: function offset2() { 885 | return { 886 | x: this.offset.x + this.nodeInnerEl.offsetWidth, 887 | y: this.offset.y + this.nodeInnerEl.offsetHeight 888 | }; 889 | }, 890 | // right bottom point 891 | offsetToViewPort: function offsetToViewPort() { 892 | var r = this.nodeInnerEl.getBoundingClientRect(); 893 | r.x = this.store.dir === 'rtl' ? r.right : r.left; 894 | r.y = r.top; 895 | return r; 896 | }, 897 | // tree 898 | currentTree: function currentTree() { 899 | // const currentTree = trees.find(tree => hp.isOffsetInEl(this.offset.x, this.offset.y, tree.$el)) 900 | var currentTree = getTreeByPoint(this.offsetToViewPort.x, this.offsetToViewPort.y, trees); 901 | 902 | if (currentTree) { 903 | var dragStartTree = this.store; 904 | 905 | if (prevTree == null) { 906 | prevTree = dragStartTree; 907 | } 908 | 909 | if (prevTree !== currentTree) { 910 | if (!isPropTrue(dragStartTree.crossTree) || !isPropTrue(currentTree.crossTree)) { 911 | return; 912 | } 913 | 914 | prevTree = currentTree; 915 | } 916 | 917 | if (!isPropTrue(currentTree.droppable)) { 918 | return; 919 | } 920 | 921 | return currentTree; 922 | } 923 | }, 924 | currentTreeRootEl: function currentTreeRootEl() { 925 | return document.getElementById(this.currentTree.rootData._id); 926 | }, 927 | currentTreeRootOf4: function currentTreeRootOf4() { 928 | return getOf4(this.currentTreeRootEl, this.currentTree.space); 929 | }, 930 | // the second child of currentTree root, excluding dragging node 931 | currentTreeRootSecondChildExcludingDragging: function currentTreeRootSecondChildExcludingDragging() { 932 | var _this = this; 933 | 934 | return this.currentTree.rootData.children.slice(0, 3).filter(function (v) { 935 | return v !== _this.node; 936 | })[1]; 937 | }, 938 | // placeholder 939 | dplhEl: function dplhEl() { 940 | return document.getElementById(this.dplh._id); 941 | }, 942 | dplhElInCurrentTree: function dplhElInCurrentTree() { 943 | return Boolean(this.currentTree.$el.querySelector("#".concat(this.dplh._id))); 944 | }, 945 | dplhOf4: function dplhOf4() { 946 | return getOf4(this.dplhEl, this.currentTree.space); 947 | }, 948 | dplhAtTop: function dplhAtTop() { 949 | return Math.abs(this.dplhOf4.y - this.currentTreeRootOf4.y) < 5; 950 | }, 951 | targetAtTop: function targetAtTop() { 952 | return Math.abs(this.tiOf4.y - this.currentTreeRootOf4.y) < 5; 953 | }, 954 | targetAtBottom: function targetAtBottom() { 955 | return Math.abs(this.tiOf4.y2 - this.currentTreeRootOf4.y2) < 5; 956 | }, 957 | // most related node 958 | // 最相关的另一个节点 959 | targetNode: function targetNode() { 960 | var currentTree = this.currentTree; 961 | 962 | if (!currentTree) { 963 | throw 'no currentTree'; 964 | } // 965 | 966 | 967 | var _this$offset = this.offset, 968 | x = _this$offset.x, 969 | y = _this$offset.y; 970 | var currentNode = currentTree.rootData; 971 | 972 | while (true) { 973 | var children = currentNode.children; 974 | 975 | if (!children) { 976 | break; 977 | } 978 | 979 | if (this.node.parent === currentNode) { 980 | // dragging node is in currentNode children, remove it first 981 | children = children.slice(); 982 | children.splice(children.indexOf(this.node), 1); 983 | } 984 | 985 | if (children.length === 0) { 986 | break; 987 | } 988 | 989 | var t = binarySearch(children, function (node) { 990 | var el = document.getElementById(node._id); 991 | var ty = getOffset(el).y; 992 | var ty2 = ty + el.offsetHeight + currentTree.space; 993 | 994 | if (ty2 < y) { 995 | return -1; 996 | } else if (ty <= y) { 997 | return 0; 998 | } else { 999 | return 1; 1000 | } 1001 | }, null, null, true); 1002 | 1003 | if (t.hit) { 1004 | currentNode = t.value; 1005 | } else { 1006 | if (t.bigger) { 1007 | currentNode = children[t.index - 1]; 1008 | } else { 1009 | currentNode = t.value; 1010 | } 1011 | } 1012 | 1013 | if (!currentNode) { 1014 | currentNode = children[0]; 1015 | break; 1016 | } 1017 | 1018 | if (!currentNode) { 1019 | break; 1020 | } 1021 | 1022 | var innerEl = document.getElementById(currentNode._id).querySelector('.tree-node-inner'); 1023 | var of = getOf4(innerEl, currentTree.space); 1024 | 1025 | if (of.y <= y && y <= of.y2) { 1026 | break; 1027 | } 1028 | } 1029 | 1030 | return currentNode; 1031 | }, 1032 | targetNodeEl: function targetNodeEl() { 1033 | return document.getElementById(this.targetNode._id); 1034 | }, 1035 | // targetNodeInnerElOffset 1036 | tiInnerEl: function tiInnerEl() { 1037 | return this.targetNodeEl.querySelector('.tree-node-inner'); 1038 | }, 1039 | tiOffset: function tiOffset() { 1040 | return getOffset(this.tiInnerEl); 1041 | }, 1042 | tiOf4: function tiOf4() { 1043 | return getOf4(this.tiInnerEl, this.currentTree.space); 1044 | }, 1045 | tiMiddleY: function tiMiddleY() { 1046 | return this.tiOffset.y + this.tiInnerEl.offsetHeight / 2; 1047 | }, 1048 | // 1049 | targetPrevEl: function targetPrevEl() { 1050 | // tree node 之间不要有其他元素, 否则这里会获取到错误的元素 1051 | var r = this.targetNodeEl.previousSibling; 1052 | 1053 | if (hasClass(r, 'dragging')) { 1054 | r = r.previousSibling; 1055 | } 1056 | 1057 | return r; 1058 | }, 1059 | targetPrev: function targetPrev() { 1060 | var id = this.targetPrevEl.getAttribute('id'); 1061 | return this.currentTree.getNodeById(id); 1062 | } 1063 | }); // attachCache end 1064 | // decision start ================================= 1065 | 1066 | var executedRuleCache = {}; // exec rule 1067 | 1068 | var exec = function exec(ruleId) { 1069 | if (!executedRuleCache.hasOwnProperty(ruleId)) { 1070 | var r; 1071 | 1072 | try { 1073 | r = rules[ruleId](info); 1074 | } catch (e) { 1075 | r = e; 1076 | 1077 | try { 1078 | if (process.env.DEVELOPE_SELF) { 1079 | // only visible when develop its self 1080 | console.warn("failed to execute rule '".concat(ruleId, "'"), e); 1081 | } 1082 | } catch (e2) { } 1083 | } 1084 | 1085 | executedRuleCache[ruleId] = r; 1086 | } 1087 | 1088 | return executedRuleCache[ruleId]; 1089 | }; 1090 | 1091 | if (exec('currentTree existed') === true) { 1092 | if (exec('targetNode is placeholder') === false) { 1093 | if (exec('targetNode is the second child of root') === true) { 1094 | if (exec('targetNode has children excluding placeholder') === false) { 1095 | if (exec('on targetNode middle') === true) { 1096 | targets['before'](info); 1097 | } else if (exec('on targetNode middle') === false) { 1098 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1099 | targets['append'](info); 1100 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1101 | targets['after'](info); 1102 | } 1103 | } 1104 | } else if (exec('targetNode has children excluding placeholder') === true) { 1105 | targets['prepend'](info); 1106 | } 1107 | } else if (exec('targetNode is the second child of root') === false) { 1108 | if (exec('currentTree empty') === false) { 1109 | if (exec('targetNode at top') === true) { 1110 | if (exec('placeholder in currentTree') === true) { 1111 | if (exec('targetNode has children excluding placeholder') === false) { 1112 | if (exec('on targetNode middle') === false) { 1113 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1114 | targets['after'](info); 1115 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1116 | targets['append'](info); 1117 | } 1118 | } else if (exec('on targetNode middle') === true) { 1119 | targets['before'](info); 1120 | } 1121 | } else if (exec('targetNode has children excluding placeholder') === true) { 1122 | if (exec('on targetNode middle') === false) { 1123 | targets['prepend'](info); 1124 | } else if (exec('on targetNode middle') === true) { 1125 | targets['before'](info); 1126 | } 1127 | } 1128 | } else if (exec('placeholder in currentTree') === false) { 1129 | targets['before'](info); 1130 | } 1131 | } else if (exec('targetNode at top') === false) { 1132 | if (exec('targetNode at bottom') === false) { 1133 | if (exec('placeholder at top') === true) { 1134 | targets['prepend'](info); 1135 | } else if (exec('placeholder at top') === false) { 1136 | if (exec('targetNode has children excluding placeholder') === true) { 1137 | targets['prepend'](info); 1138 | } else if (exec('targetNode has children excluding placeholder') === false) { 1139 | if (exec('targetNode is 1st child') === false) { 1140 | if (exec('targetNode is last child') === false) { 1141 | if (exec('on targetNode middle') === true) { 1142 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1143 | targets['append'](info); 1144 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1145 | targets['after'](info); 1146 | } 1147 | } else if (exec('on targetNode middle') === false) { 1148 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1149 | targets['append'](info); 1150 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1151 | targets['after'](info); 1152 | } 1153 | } 1154 | } else if (exec('targetNode is last child') === true) { 1155 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1156 | targets['append'](info); 1157 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1158 | targets['after'](info); 1159 | } 1160 | } 1161 | } else if (exec('targetNode is 1st child') === true) { 1162 | if (exec('targetNode is last child') === true) { 1163 | targets['append'](info); 1164 | } else if (exec('targetNode is last child') === false) { 1165 | if (exec('on targetNode middle') === false) { 1166 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1167 | targets['after'](info); 1168 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1169 | targets['append'](info); 1170 | } 1171 | } else if (exec('on targetNode middle') === true) { 1172 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1173 | targets['after'](info); 1174 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1175 | targets['append'](info); 1176 | } 1177 | } 1178 | } 1179 | } 1180 | } 1181 | } 1182 | } else if (exec('targetNode at bottom') === true) { 1183 | if (exec('placeholder in currentTree') === true) { 1184 | if (exec('on targetNode middle') === false) { 1185 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1186 | targets['append'](info); 1187 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false) { 1188 | targets['after'](info); 1189 | } 1190 | } else if (exec('on targetNode middle') === true) { 1191 | targets['append'](info); 1192 | } 1193 | } else if (exec('placeholder in currentTree') === false) { 1194 | targets['append'](info); 1195 | } 1196 | } 1197 | } 1198 | } else if (exec('currentTree empty') === true) { 1199 | targets['append current tree'](info); 1200 | } 1201 | } 1202 | } else if (exec('targetNode is placeholder') === true) { 1203 | if (exec('targetNode at bottom') === false) { 1204 | if (exec('targetNode is the second child of root') === false) { 1205 | if (exec('targetNode is 1st child') === true) { 1206 | if (exec('targetNode is last child') === false); else if (exec('targetNode is last child') === true) { 1207 | if (exec('on targetNode middle') === false) { 1208 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1209 | targets['after target parent'](info); 1210 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); 1211 | } else if (exec('on targetNode middle') === true) { 1212 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1213 | targets['after target parent'](info); 1214 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); 1215 | } 1216 | } 1217 | } else if (exec('targetNode is 1st child') === false) { 1218 | if (exec('targetNode is last child') === true) { 1219 | if (exec('on targetNode middle') === true) { 1220 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1221 | targets['after target parent'](info); 1222 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1223 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1224 | targets['append prev'](info); 1225 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1226 | } 1227 | } else if (exec('on targetNode middle') === false) { 1228 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1229 | targets['after target parent'](info); 1230 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1231 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1232 | targets['append prev'](info); 1233 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1234 | } 1235 | } 1236 | } else if (exec('targetNode is last child') === false) { 1237 | if (exec('on targetNode middle') === true) { 1238 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true); else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1239 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1240 | targets['append prev'](info); 1241 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1242 | } 1243 | } else if (exec('on targetNode middle') === false) { 1244 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true); else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1245 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1246 | targets['append prev'](info); 1247 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1248 | } 1249 | } 1250 | } 1251 | } 1252 | } else if (exec('targetNode is the second child of root') === true) { 1253 | if (exec('on targetNode middle') === true) { 1254 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1255 | targets['append prev'](info); 1256 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1257 | } else if (exec('on targetNode middle') === false) { 1258 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1259 | targets['append prev'](info); 1260 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1261 | } 1262 | } 1263 | } else if (exec('targetNode at bottom') === true) { 1264 | if (exec('targetNode is 1st child') === true) { 1265 | if (exec('on targetNode middle') === false) { 1266 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1267 | targets['after target parent'](info); 1268 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); 1269 | } else if (exec('on targetNode middle') === true) { 1270 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false); else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1271 | targets['after target parent'](info); 1272 | } 1273 | } 1274 | } else if (exec('targetNode is 1st child') === false) { 1275 | if (exec('on targetNode middle') === false) { 1276 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1277 | targets['after target parent'](info); 1278 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1279 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1280 | targets['append prev'](info); 1281 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1282 | } 1283 | } else if (exec('on targetNode middle') === true) { 1284 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true) { 1285 | targets['after target parent'](info); 1286 | } else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false) { 1287 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true) { 1288 | targets['append prev'](info); 1289 | } else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false); 1290 | } 1291 | } 1292 | } 1293 | } 1294 | } 1295 | } else if (exec('currentTree existed') === false); // decision end ================================= 1296 | // 1297 | 1298 | } 1299 | 1300 | function getOf4(el, space) { 1301 | var r = getOffset(el); 1302 | r.x2 = r.x + el.offsetWidth; 1303 | r.y2 = r.y + el.offsetHeight + space; 1304 | return r; 1305 | } 1306 | 1307 | autoMoveDragPlaceHolder.dragStart = function dragStart() { }; 1308 | 1309 | autoMoveDragPlaceHolder.dragEnd = function dragEnd() { 1310 | prevTree = null; 1311 | droppableIds = {}; 1312 | draggableIds = {}; 1313 | }; 1314 | 1315 | var script$2 = { 1316 | extends: TreeNode, 1317 | name: 'TreeNode', 1318 | mounted: function mounted() { 1319 | var _this = this; 1320 | 1321 | this.store.isNodeDraggable = isNodeDraggable; 1322 | this.store.isNodeDroppable = isNodeDroppable; 1323 | 1324 | if (this.isRoot || this.data.isDragPlaceHolder) { 1325 | return; 1326 | } 1327 | 1328 | var dplh = this.store.dplh; 1329 | this.$watch('store.draggable', function (draggable) { 1330 | if (isPropTrue(draggable)) { 1331 | var triggerEl = _this.store.getTriggerEl ? _this.store.getTriggerEl(_this) : _this.$el.querySelector('.tree-node-inner'); 1332 | _this._draggableDestroy = draggableHelper(triggerEl, { 1333 | preventSelect: isPropTrue(_this.store.preventSelect), 1334 | // trigger el 1335 | getEl: function getEl() { 1336 | return _this.$el; 1337 | }, 1338 | minTranslate: 10, 1339 | drag: function drag(e, opt, store) { 1340 | autoMoveDragPlaceHolder.dragStart(); // this store is not tree 1341 | 1342 | var draggableHelperInfo = { 1343 | event: e, 1344 | options: opt, 1345 | store: store 1346 | }; 1347 | 1348 | if (_this.store.ondragstart && _this.store.ondragstart(_this.data, draggableHelperInfo) === false) { 1349 | return false; 1350 | } 1351 | 1352 | if (!isNodeDraggable(_this.data)) { 1353 | return false; 1354 | } 1355 | 1356 | _this.store.$emit('drag', _this.data); // record start positon 1357 | 1358 | 1359 | var siblings = _this.data.parent.children; 1360 | _this.startPosition = { 1361 | siblings: siblings, 1362 | index: siblings.indexOf(_this.data) // 1363 | 1364 | }; 1365 | dplh.innerStyle.height = store.el.offsetHeight + 'px'; 1366 | insertAfter(dplh, _this.data); 1367 | _this.data.class += ' dragging'; // console.log('drag start'); 1368 | }, 1369 | moving: function moving(e, opt, store) { 1370 | if (store.movedCount === 0) { 1371 | return; 1372 | } 1373 | 1374 | var draggableHelperInfo = { 1375 | event: e, 1376 | options: opt, 1377 | store: store 1378 | }; 1379 | return autoMoveDragPlaceHolder.call(_this, draggableHelperInfo); 1380 | }, 1381 | drop: function drop(e, opt, store) { 1382 | autoMoveDragPlaceHolder.dragEnd(); 1383 | var draggableHelperInfo = { 1384 | event: e, 1385 | options: opt, 1386 | store: store 1387 | }; 1388 | 1389 | if (_this.store.ondragend && _this.store.ondragend(_this.data, draggableHelperInfo) === false) { 1390 | arrayRemove(dplh.parent.children, dplh); // can't drop, no change 1391 | } else { 1392 | var targetTree = dplh._vm.store; 1393 | var crossTree = targetTree !== _this.store; 1394 | var oldTree = crossTree ? _this.store : null; 1395 | insertAfter(_this.data, dplh); 1396 | arrayRemove(dplh.parent.children, dplh); 1397 | _this.data.class = _this.data.class.replace(/(^| )dragging( |$)/g, ' '); 1398 | targetTree.$emit('drop', _this.data, targetTree, oldTree); 1399 | oldTree && oldTree.$emit('drop', _this.data, targetTree, oldTree); // emit change event if changed 1400 | 1401 | var siblings = _this.data.parent.children; 1402 | 1403 | if (siblings === _this.startPosition.siblings && siblings.indexOf(_this.data) === _this.startPosition.index); else { 1404 | _this.store.$emit('change', _this.data, targetTree, oldTree); 1405 | 1406 | oldTree && oldTree.$emit('change', _this.data, targetTree, oldTree); 1407 | } 1408 | 1409 | _this.startPosition = null; 1410 | } // console.log('drag end'); 1411 | 1412 | } 1413 | }); 1414 | } else { 1415 | if (_this._draggableDestroy) { 1416 | _this._draggableDestroy(); 1417 | 1418 | _this._draggableDestroy = null; 1419 | } 1420 | } 1421 | }, { 1422 | immediate: true 1423 | }); 1424 | } 1425 | }; 1426 | 1427 | /* script */ 1428 | const __vue_script__$2 = script$2; 1429 | 1430 | /* template */ 1431 | 1432 | /* style */ 1433 | const __vue_inject_styles__$2 = undefined; 1434 | /* scoped */ 1435 | const __vue_scope_id__$2 = undefined; 1436 | /* module identifier */ 1437 | const __vue_module_identifier__$2 = undefined; 1438 | /* functional template */ 1439 | const __vue_is_functional_template__$2 = undefined; 1440 | /* component normalizer */ 1441 | function __vue_normalize__$2( 1442 | template, style, script, 1443 | scope, functional, moduleIdentifier, 1444 | createInjector, createInjectorSSR 1445 | ) { 1446 | const component = (typeof script === 'function' ? script.options : script) || {}; 1447 | 1448 | // For security concerns, we use only base name in production mode. 1449 | component.__file = "DraggableTreeNode.vue"; 1450 | 1451 | if (!component.render) { 1452 | component.render = template.render; 1453 | component.staticRenderFns = template.staticRenderFns; 1454 | component._compiled = true; 1455 | 1456 | if (functional) component.functional = true; 1457 | } 1458 | 1459 | component._scopeId = scope; 1460 | 1461 | return component 1462 | } 1463 | /* style inject */ 1464 | 1465 | /* style inject SSR */ 1466 | 1467 | 1468 | 1469 | var DraggableTreeNode = __vue_normalize__$2( 1470 | {}, 1471 | __vue_inject_styles__$2, 1472 | __vue_script__$2, 1473 | __vue_scope_id__$2, 1474 | __vue_is_functional_template__$2, 1475 | __vue_module_identifier__$2, 1476 | undefined, 1477 | undefined 1478 | ); 1479 | 1480 | var trees = []; // for multiple trees 1481 | // DragPlaceHolder, unique 1482 | 1483 | var dplh = { 1484 | _id: 'draggable_tree_drag_placeHolder', 1485 | level: null, 1486 | droppable: false, 1487 | isDragPlaceHolder: true, 1488 | class: 'draggable-placeholder', 1489 | style: {}, 1490 | innerStyle: {}, 1491 | innerClass: 'draggable-placeholder-inner', 1492 | innerBackStyle: {}, 1493 | innerBackClass: 'draggable-placeholder-inner-back' // children: [], 1494 | 1495 | }; 1496 | var script$3 = { 1497 | extends: Tree, 1498 | props: { 1499 | getTriggerEl: { 1500 | type: Function 1501 | }, 1502 | draggable: {}, 1503 | droppable: { 1504 | default: true 1505 | }, 1506 | crossTree: {}, 1507 | ondragstart: { 1508 | type: Function 1509 | }, 1510 | ondragend: { 1511 | type: Function 1512 | }, 1513 | preventSelect: { 1514 | default: true 1515 | }, 1516 | dir: { 1517 | type: String, 1518 | default: 'ltr' 1519 | } 1520 | }, 1521 | components: { 1522 | TreeNode: DraggableTreeNode 1523 | }, 1524 | data: function data() { 1525 | return { 1526 | // DragPlaceHolder 1527 | dplh: dplh, 1528 | trees: trees 1529 | }; 1530 | }, 1531 | // computed: {}, 1532 | // watch: {}, 1533 | // methods: {}, 1534 | created: function created() { 1535 | trees.push(this); 1536 | }, 1537 | mounted: function mounted() { }, 1538 | beforeDestroy: function beforeDestroy() { 1539 | arrayRemove(trees, this); 1540 | } 1541 | }; 1542 | 1543 | /* script */ 1544 | const __vue_script__$3 = script$3; 1545 | 1546 | /* template */ 1547 | 1548 | /* style */ 1549 | const __vue_inject_styles__$3 = undefined; 1550 | /* scoped */ 1551 | const __vue_scope_id__$3 = undefined; 1552 | /* module identifier */ 1553 | const __vue_module_identifier__$3 = undefined; 1554 | /* functional template */ 1555 | const __vue_is_functional_template__$3 = undefined; 1556 | /* component normalizer */ 1557 | function __vue_normalize__$3( 1558 | template, style, script, 1559 | scope, functional, moduleIdentifier, 1560 | createInjector, createInjectorSSR 1561 | ) { 1562 | const component = (typeof script === 'function' ? script.options : script) || {}; 1563 | 1564 | // For security concerns, we use only base name in production mode. 1565 | component.__file = "DraggableTree.vue"; 1566 | 1567 | if (!component.render) { 1568 | component.render = template.render; 1569 | component.staticRenderFns = template.staticRenderFns; 1570 | component._compiled = true; 1571 | 1572 | if (functional) component.functional = true; 1573 | } 1574 | 1575 | component._scopeId = scope; 1576 | 1577 | return component 1578 | } 1579 | /* style inject */ 1580 | 1581 | /* style inject SSR */ 1582 | 1583 | 1584 | 1585 | var DraggableTree = __vue_normalize__$3( 1586 | {}, 1587 | __vue_inject_styles__$3, 1588 | __vue_script__$3, 1589 | __vue_scope_id__$3, 1590 | __vue_is_functional_template__$3, 1591 | __vue_module_identifier__$3, 1592 | undefined, 1593 | undefined 1594 | ); 1595 | 1596 | export { Tree, TreeNode, DraggableTree, DraggableTreeNode }; 1597 | -------------------------------------------------------------------------------- /ie11-example/README.txt: -------------------------------------------------------------------------------- 1 | A Pen created at CodePen.io. You can find this one at https://codepen.io/phphe/pen/KRapQm. 2 | 3 | -------------------------------------------------------------------------------- /ie11-example/css/style.css: -------------------------------------------------------------------------------- 1 | #app { 2 | padding: 50px; 3 | } 4 | 5 | .he-tree { 6 | border: 1px solid #ccc; 7 | padding: 20px; 8 | } 9 | 10 | .tree-node-inner { 11 | padding: 5px; 12 | border: 1px solid #ccc; 13 | cursor: pointer; 14 | } 15 | 16 | .draggable-placeholder-inner { 17 | border: 1px dashed #0088F8; 18 | box-sizing: border-box; 19 | background: rgba(0, 136, 249, 0.09); 20 | color: #0088f9; 21 | text-align: center; 22 | padding: 0; 23 | display: flex; 24 | align-items: center; 25 | } 26 | 27 | .tree3 .tree-node-inner { 28 | border: none; 29 | padding: 0px; 30 | } 31 | .tree3 .tree-node-inner-back:hover { 32 | background: #ddd; 33 | } 34 | 35 | .tree4 .tree-node-inner { 36 | border: none; 37 | border-bottom: 1px solid #ccc; 38 | padding: 5px 10px; 39 | backgroud: #ccc; 40 | } 41 | .tree4 .draggable-placeholder-inner { 42 | background-color: orangered; 43 | } 44 | -------------------------------------------------------------------------------- /ie11-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | vue-draggable-nested-tree demo 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 | 41 |
42 | 43 |
44 |
45 |
46 | 70 |
71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /ie11-example/index.pug: -------------------------------------------------------------------------------- 1 | #app 2 | .row 3 | .col-3 4 | .mb-2 5 | button.btn.btn-primary(@click="collapseAll") collapse all 6 | button.btn.btn-primary.ml-1(@click="expandAll") expand all 7 | Tree(:data="tree1data" draggable cross-tree) 8 | div(slot-scope="{data, store}") 9 | template(v-if="!data.isDragPlaceHolder") 10 | b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}}  11 | span {{data.text}} 12 | .col-3 13 | Tree(:data="tree2data" draggable cross-tree) 14 | div(slot-scope="{data, store}") 15 | template(v-if="!data.isDragPlaceHolder") 16 | b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}}  17 | span {{data.text}} 18 | .mt-2 19 | button.btn.btn-primary(@click="addChild") Add child 20 | .col-3 21 | Tree.tree3(:data="tree3data" draggable cross-tree) 22 | div(slot-scope="{data, store}") 23 | template(v-if="!data.isDragPlaceHolder") 24 | b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}}  25 | span {{data.text}} 26 | .col-3 27 | Tree.tree4(:data="tree4data" draggable cross-tree :indent="30" :space="0") 28 | div(slot-scope="{data, store}") 29 | template(v-if="!data.isDragPlaceHolder") 30 | b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}}  31 | span {{data.text}} 32 | -------------------------------------------------------------------------------- /ie11-example/js/index.js: -------------------------------------------------------------------------------- 1 | th = treeHelper; 2 | app = new Vue({ 3 | el: '#app', 4 | components: { 5 | Tree: vueDraggableNestedTree.DraggableTree 6 | }, 7 | data: { 8 | tree1data: [{ 9 | text: 'node 1' 10 | }, { 11 | text: 'node 2' 12 | }, { 13 | text: 'node 3 undraggable', 14 | draggable: false 15 | }, { 16 | text: 'node 4' 17 | }, { 18 | text: 'node 4 undroppable', 19 | droppable: false 20 | }, { 21 | text: 'node 5', 22 | children: [{ 23 | text: 'node 1' 24 | }, { 25 | text: 'node 2', 26 | children: [{ 27 | text: 'node 3' 28 | }, { 29 | text: 'node 4' 30 | }] 31 | }, { 32 | text: 'node 2 undroppable', 33 | droppable: false, 34 | children: [{ 35 | text: 'node 3' 36 | }, { 37 | text: 'node 4' 38 | }] 39 | }, { 40 | text: 'node 2', 41 | children: [{ 42 | text: 'node 3' 43 | }, { 44 | text: 'node 4 undroppable', 45 | droppable: false 46 | }] 47 | }, { 48 | text: 'node 3' 49 | }, { 50 | text: 'node 4' 51 | }, { 52 | text: 'node 3' 53 | }, { 54 | text: 'node 4' 55 | }, { 56 | text: 'node 3' 57 | }, { 58 | text: 'node 4' 59 | }, { 60 | text: 'node 3' 61 | }, { 62 | text: 'node 4' 63 | }] 64 | }], 65 | tree2data: [{ 66 | text: 'node 1' 67 | }, { 68 | text: 'node 2' 69 | }, { 70 | text: 'node 3' 71 | }, { 72 | text: 'node 4' 73 | }], 74 | tree3data: [{ 75 | text: 'node 1' 76 | }, { 77 | text: 'node 2' 78 | }, { 79 | text: 'node 3 undraggable', 80 | draggable: false 81 | }, { 82 | text: 'node 4' 83 | }, { 84 | text: 'node 4 undroppable', 85 | droppable: false 86 | }, { 87 | text: 'node 5', 88 | children: [{ 89 | text: 'node 1' 90 | }, { 91 | text: 'node 2', 92 | children: [{ 93 | text: 'node 3' 94 | }, { 95 | text: 'node 4' 96 | }] 97 | }, { 98 | text: 'node 2 undroppable', 99 | droppable: false, 100 | children: [{ 101 | text: 'node 3' 102 | }, { 103 | text: 'node 4' 104 | }] 105 | }, { 106 | text: 'node 2', 107 | children: [{ 108 | text: 'node 3' 109 | }, { 110 | text: 'node 4 undroppable', 111 | droppable: false 112 | }] 113 | }, { 114 | text: 'node 3' 115 | }, { 116 | text: 'node 4' 117 | }, { 118 | text: 'node 3' 119 | }, { 120 | text: 'node 4' 121 | }, { 122 | text: 'node 3' 123 | }, { 124 | text: 'node 4' 125 | }, { 126 | text: 'node 3' 127 | }, { 128 | text: 'node 4' 129 | }] 130 | }], 131 | tree4data: [{ 132 | text: 'node 1' 133 | }, { 134 | text: 'node 2' 135 | }, { 136 | text: 'node 3 undraggable', 137 | draggable: false 138 | }, { 139 | text: 'node 4' 140 | }, { 141 | text: 'node 4 undroppable', 142 | droppable: false 143 | }, { 144 | text: 'node 5', 145 | children: [{ 146 | text: 'node 1' 147 | }, { 148 | text: 'node 2', 149 | children: [{ 150 | text: 'node 3' 151 | }, { 152 | text: 'node 4' 153 | }] 154 | }, { 155 | text: 'node 2 undroppable', 156 | droppable: false, 157 | children: [{ 158 | text: 'node 3' 159 | }, { 160 | text: 'node 4' 161 | }] 162 | }, { 163 | text: 'node 2', 164 | children: [{ 165 | text: 'node 3' 166 | }, { 167 | text: 'node 4 undroppable', 168 | droppable: false 169 | }] 170 | }, { 171 | text: 'node 3' 172 | }, { 173 | text: 'node 4' 174 | }, { 175 | text: 'node 3' 176 | }, { 177 | text: 'node 4' 178 | }, { 179 | text: 'node 3' 180 | }, { 181 | text: 'node 4' 182 | }, { 183 | text: 'node 3' 184 | }, { 185 | text: 'node 4' 186 | }] 187 | }] 188 | }, 189 | methods: { 190 | // add child to tree2 191 | addChild: function addChild() { 192 | this.tree2data[0].children.push({ 193 | text: 'child' 194 | }); 195 | }, 196 | expandAll: function expandAll() { 197 | th.breadthFirstSearch(this.tree1data, function (node) { 198 | node.open = true; 199 | }); 200 | }, 201 | collapseAll: function collapseAll() { 202 | th.breadthFirstSearch(this.tree1data, function (node) { 203 | node.open = false; 204 | }); 205 | } 206 | }, 207 | created: function created() { 208 | } 209 | }); 210 | -------------------------------------------------------------------------------- /ie11-example/license.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | -------------------------------------------------------------------------------- /ie11-example/scss/style.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | padding: 50px; 3 | } 4 | 5 | .he-tree{ 6 | border: 1px solid #ccc; 7 | padding: 20px; 8 | } 9 | .tree-node{ 10 | } 11 | .tree-node-inner{ 12 | padding: 5px; 13 | border: 1px solid #ccc; 14 | cursor: pointer; 15 | } 16 | .draggable-placeholder{ 17 | } 18 | .draggable-placeholder-inner{ 19 | border: 1px dashed #0088F8; 20 | box-sizing: border-box; 21 | background: rgba(0, 136, 249, 0.09); 22 | color: #0088f9; 23 | text-align: center; 24 | padding: 0; 25 | display: flex; 26 | align-items: center; 27 | } 28 | .tree3{ 29 | .tree-node-inner{ 30 | border: none; 31 | padding: 0px; 32 | } 33 | .tree-node-inner-back:hover{ 34 | background: #ddd; 35 | } 36 | } 37 | 38 | .tree4{ 39 | .tree-node-inner{ 40 | border: none; 41 | border-bottom: 1px solid #ccc; 42 | padding: 5px 10px; 43 | backgroud: #ccc; 44 | } 45 | .draggable-placeholder-inner{ 46 | background-color: orangered; 47 | } 48 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-draggable-nested-tree", 3 | "version": "2.3.0-beta.1", 4 | "description": "A draggable tree view component for vue2", 5 | "main": "dist/vue-draggable-nested-tree.cjs.js", 6 | "module": "dist/vue-draggable-nested-tree.es.js", 7 | "author": "phphe ", 8 | "license": "MIT", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/phphe/vue-draggable-nested-tree.git" 12 | }, 13 | "scripts": { 14 | "dev": "vue-cli-service serve", 15 | "build-vue": "vue-cli-service build", 16 | "build": "node build/build-lib.js", 17 | "lint": "vue-cli-service lint", 18 | "postinstall": "node warn.js" 19 | }, 20 | "dependencies": { 21 | "draggable-helper": "1.0.20", 22 | "helper-js": "^1.3.7", 23 | "tree-helper": "^1.0.5", 24 | "vue": "^2.5.21", 25 | "vue-functions": "^1.0.3" 26 | }, 27 | "devDependencies": { 28 | "@vue/cli-plugin-babel": "^3.0.1", 29 | "@vue/cli-plugin-eslint": "^3.0.1", 30 | "@vue/cli-service": "^3.0.1", 31 | "babel-eslint": "^10.0.1", 32 | "eslint": "^5.8.0", 33 | "eslint-plugin-vue": "^5.0.0", 34 | "vue-template-compiler": "^2.5.21", 35 | "pug": "^2.0.3", 36 | "pug-plain-loader": "^1.0.0", 37 | "bili": "^3.4.2", 38 | "rollup-plugin-vue": "^4.3.2" 39 | }, 40 | "eslintConfig": { 41 | "root": true, 42 | "env": { 43 | "node": true 44 | }, 45 | "extends": [ 46 | "plugin:vue/essential", 47 | "eslint:recommended" 48 | ], 49 | "rules": {}, 50 | "parserOptions": { 51 | "parser": "babel-eslint" 52 | } 53 | }, 54 | "postcss": { 55 | "plugins": { 56 | "autoprefixer": {} 57 | } 58 | }, 59 | "browserslist": [ 60 | "> 1%", 61 | "last 2 versions", 62 | "not ie <= 8" 63 | ] 64 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-draggable-nested-tree 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 30 | 31 | 64 | -------------------------------------------------------------------------------- /src/components/DraggableTree.vue: -------------------------------------------------------------------------------- 1 | 59 | -------------------------------------------------------------------------------- /src/components/DraggableTreeNode.vue: -------------------------------------------------------------------------------- 1 | 93 | -------------------------------------------------------------------------------- /src/components/Tree.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 178 | -------------------------------------------------------------------------------- /src/components/TreeNode.vue: -------------------------------------------------------------------------------- 1 | 21 | 77 | -------------------------------------------------------------------------------- /src/components/autoMoveDragPlaceHolder.js: -------------------------------------------------------------------------------- 1 | import * as hp from 'helper-js' 2 | import * as th from 'tree-helper' 3 | import Cache, { 4 | attachCache 5 | } from '../plugins/Cache' 6 | import * as vf from 'vue-functions' 7 | import getTreeByPoint from './temporarily-fix-overlapping-tree-issue' 8 | 9 | // actions for drag placeholder 10 | // 对 drag placeholder进行的操作 11 | const targets = { 12 | 'nothing': info => {}, 13 | 'after': (info) => { 14 | insertDplhAfterTo(info.dplh, info.targetNode, info) 15 | }, 16 | 'before': (info) => { 17 | if (isNodeDroppable(info.targetNode.parent)) { 18 | th.insertBefore(info.dplh, info.targetNode) 19 | } else { 20 | insertDplhAfterTo(info.dplh, info.targetNode.parent, info) 21 | } 22 | }, 23 | 'append': (info) => { 24 | if (isNodeDroppable(info.targetNode)) { 25 | th.appendTo(info.dplh, info.targetNode) 26 | if (!info.targetNode.open) info.store.toggleOpen(info.targetNode) 27 | } else { 28 | insertDplhAfterTo(info.dplh, info.targetNode, info) 29 | } 30 | }, 31 | 'prepend': (info) => { 32 | if (isNodeDroppable(info.targetNode)) { 33 | th.prependTo(info.dplh, info.targetNode) 34 | if (!info.targetNode.open) info.store.toggleOpen(info.targetNode) 35 | } else { 36 | insertDplhAfterTo(info.dplh, info.targetNode, info) 37 | } 38 | }, 39 | 'after target parent': (info) => { 40 | insertDplhAfterTo(info.dplh, info.targetNode.parent, info) 41 | }, 42 | // append to prev sibling 43 | 'append prev': (info) => { 44 | if (isNodeDroppable(info.targetPrev)) { 45 | th.appendTo(info.dplh, info.targetPrev) 46 | if (!info.targetPrev.open) info.store.toggleOpen(info.targetPrev) 47 | } else { 48 | insertDplhAfterTo(info.dplh, info.targetPrev, info) 49 | } 50 | }, 51 | // append to current tree 52 | 'append current tree': (info) => { 53 | if (isNodeDroppable(info.currentTree.rootData)) { 54 | th.appendTo(info.dplh, info.currentTree.rootData) 55 | } 56 | }, 57 | } 58 | 59 | function insertDplhAfterTo(dplh, targetNode, info) { 60 | if (!targetNode) { 61 | return false 62 | } else { 63 | const closest = findParent(targetNode, node => node.parent && isNodeDroppable(node.parent)) 64 | if (closest) { 65 | th.insertAfter(dplh, closest) 66 | } else { 67 | return false 68 | } 69 | } 70 | return true 71 | } 72 | 73 | export function isNodeDraggable(node) { 74 | if (!draggableIds.hasOwnProperty(node._id)) { 75 | let r 76 | if (node.hasOwnProperty('draggable')) { 77 | r = node.draggable 78 | } else if (node.parent) { 79 | r = isNodeDraggable(node.parent) 80 | } else { 81 | r = true 82 | } 83 | draggableIds[node._id] = r 84 | } 85 | return draggableIds[node._id] 86 | } 87 | 88 | export function isNodeDroppable(node) { 89 | if (!droppableIds.hasOwnProperty(node._id)) { 90 | let r 91 | if (node.hasOwnProperty('droppable')) { 92 | r = node.droppable 93 | } else if (node.parent) { 94 | r = isNodeDroppable(node.parent) 95 | } else { 96 | r = true 97 | } 98 | droppableIds[node._id] = r 99 | } 100 | return droppableIds[node._id] 101 | } 102 | 103 | // find child, excluding dragging node default 104 | function findChild(info, children, handler, reverse) { 105 | const len = children.length 106 | if (reverse) { 107 | for (let i = len - 1; i >= 0; i--) { 108 | const item = children[i] 109 | // excluding dragging node 110 | if (item !== info.node) { 111 | if (handler(item, i)) { 112 | return item 113 | } 114 | } 115 | } 116 | } else { 117 | for (let i = 0; i < len; i++) { 118 | const item = children[i] 119 | // excluding dragging node 120 | if (item !== info.node) { 121 | if (handler(item, i)) { 122 | return item 123 | } 124 | } 125 | } 126 | } 127 | } 128 | // start from node self 129 | function findParent(node, handle) { 130 | let current = node 131 | while (current) { 132 | if (handle(current)) { 133 | return current 134 | } 135 | current = current.parent 136 | } 137 | } 138 | const rules = { 139 | // 另一节点存在 140 | 'targetNode existed': info => info.targetNode, 141 | // 另一节点是拖动占位节点 142 | 'targetNode is placeholder': info => info.targetNode.isDragPlaceHolder, 143 | // 另一节点在最上面 144 | 'targetNode at top': info => info.targetAtTop, 145 | // 另一节点在最下面 146 | 'targetNode at bottom': info => info.targetAtBottom, 147 | // 另一节点是根节点第二个子 148 | 'targetNode is the second child of root': info => info.currentTreeRootSecondChildExcludingDragging === info.targetNode, 149 | // 拖动点坐标在任一树中, 同时, 起始树要可拖出, 当前树要可拖入 150 | 'currentTree existed': info => info.currentTree, 151 | // 当前树为空(不包括占位节点) 152 | 'currentTree empty': info => !findChild(info, info.currentTree.rootData.children, v => v), 153 | // 占位节点存在 154 | 'placeholder existed': info => info.dplhEl, 155 | // 占位节点在当前树中 156 | 'placeholder in currentTree': info => info.dplhElInCurrentTree, 157 | // 占位节点在最上面 158 | 'placeholder at top': info => info.dplhAtTop, 159 | // 另一节点是打开的 160 | 'targetNode is open': info => info.targetNode.open, 161 | // 另一节点有子(不包括占位节点) 162 | 'targetNode has children excluding placeholder': info => findChild(info, info.targetNode.children, v => v !== info.dplh), 163 | // 另一节点是第一个节点 164 | 'targetNode is 1st child': info => findChild(info, info.targetNode.parent.children, v => v) === info.targetNode, 165 | // 另一节点是最后节点 166 | 'targetNode is last child': info => findChild(info, info.targetNode.parent.children, v => v, true) === info.targetNode, 167 | // 当前位置在另一节点inner垂直中线上 168 | 'on targetNode middle': info => info.offset.y <= info.tiMiddleY, 169 | // 当前位置在另一节点inner左边 170 | 'at left': info => info.offset.x < info.tiOffset.x, 171 | 'at right': info => info.offset.x > info.tiOffset.x, 172 | // 当前位置在另一节点innner indent位置右边 173 | 'at indent right': info => info.offset.x > info.tiOffset.x + info.currentTree.indent, 174 | 'at indent left': info => info.offset.x < info.tiOffset.x + info.currentTree.indent, 175 | } 176 | 177 | // convert rule output to Boolean 178 | for (const key of Object.keys(rules)) { 179 | const old = rules[key] 180 | rules[key] = (...args) => Boolean(old(...args)) 181 | } 182 | 183 | let prevTree 184 | let droppableIds = {} 185 | let draggableIds = {} 186 | // context is vm 187 | export default function autoMoveDragPlaceHolder(draggableHelperInfo) { 188 | const trees = this.store.trees 189 | const dhStore = draggableHelperInfo.store 190 | // make info 191 | const info = { 192 | event: draggableHelperInfo.event, 193 | el: dhStore.el, 194 | vm: this, 195 | node: this.data, 196 | store: this.store, 197 | dplh: this.store.dplh, 198 | draggableHelperData: { 199 | opt: draggableHelperInfo.options, 200 | store: dhStore, 201 | }, 202 | } 203 | // 204 | attachCache(info, new Cache(), { 205 | // dragging node coordinate 206 | // 拖动中的节点相关坐标 207 | nodeInnerEl() { 208 | return this.el.querySelector('.tree-node-inner') 209 | }, 210 | offset() { 211 | return hp.getOffset(this.nodeInnerEl) 212 | }, // left top point 213 | offset2() { 214 | return { 215 | x: this.offset.x + this.nodeInnerEl.offsetWidth, 216 | y: this.offset.y + this.nodeInnerEl.offsetHeight 217 | } 218 | }, // right bottom point 219 | offsetToViewPort() { 220 | const r = this.nodeInnerEl.getBoundingClientRect() 221 | r.x = this.store.dir === 'rtl' ? r.right : r.left 222 | r.y = r.top 223 | return r 224 | }, 225 | // tree 226 | currentTree() { 227 | // const currentTree = trees.find(tree => hp.isOffsetInEl(this.offset.x, this.offset.y, tree.$el)) 228 | const currentTree = getTreeByPoint(this.offsetToViewPort.x, this.offsetToViewPort.y, trees) 229 | if (currentTree) { 230 | const dragStartTree = this.store 231 | let treeChanged 232 | if (prevTree == null) { 233 | prevTree = dragStartTree 234 | treeChanged = true 235 | } 236 | if (prevTree !== currentTree) { 237 | if (!vf.isPropTrue(dragStartTree.crossTree) || !vf.isPropTrue(currentTree.crossTree)) { 238 | return 239 | } 240 | prevTree = currentTree 241 | treeChanged = true 242 | } 243 | if (!vf.isPropTrue(currentTree.droppable)) { 244 | return 245 | } 246 | return currentTree 247 | } 248 | }, 249 | currentTreeRootEl() { 250 | return document.getElementById(this.currentTree.rootData._id) 251 | }, 252 | currentTreeRootOf4() { 253 | return getOf4(this.currentTreeRootEl, this.currentTree.space) 254 | }, 255 | // the second child of currentTree root, excluding dragging node 256 | currentTreeRootSecondChildExcludingDragging() { 257 | return this.currentTree.rootData.children.slice(0, 3).filter(v => v !== this.node)[1] 258 | }, 259 | // placeholder 260 | dplhEl() { 261 | return document.getElementById(this.dplh._id) 262 | }, 263 | dplhElInCurrentTree() { 264 | return Boolean(this.currentTree.$el.querySelector(`#${this.dplh._id}`)) 265 | }, 266 | dplhOf4() { 267 | return getOf4(this.dplhEl, this.currentTree.space) 268 | }, 269 | dplhAtTop() { 270 | return Math.abs(this.dplhOf4.y - this.currentTreeRootOf4.y) < 5 271 | }, 272 | targetAtTop() { 273 | return Math.abs(this.tiOf4.y - this.currentTreeRootOf4.y) < 5 274 | }, 275 | targetAtBottom() { 276 | return Math.abs(this.tiOf4.y2 - this.currentTreeRootOf4.y2) < 5 277 | }, 278 | // most related node 279 | // 最相关的另一个节点 280 | targetNode() { 281 | const { 282 | currentTree 283 | } = this 284 | if (!currentTree) { 285 | throw 'no currentTree' 286 | } 287 | // 288 | const { 289 | x, 290 | y 291 | } = this.offset 292 | let currentNode = currentTree.rootData 293 | while (true) { 294 | let children = currentNode.children 295 | if (!children) { 296 | break 297 | } 298 | if (this.node.parent === currentNode) { 299 | // dragging node is in currentNode children, remove it first 300 | children = children.slice() 301 | children.splice(children.indexOf(this.node), 1) 302 | } 303 | if (children.length === 0) { 304 | break 305 | } 306 | const t = hp.binarySearch(children, (node) => { 307 | const el = document.getElementById(node._id) 308 | const ty = hp.getOffset(el).y 309 | const ty2 = ty + el.offsetHeight + currentTree.space 310 | if (ty2 < y) { 311 | return -1 312 | } else if (ty <= y) { 313 | return 0 314 | } else { 315 | return 1 316 | } 317 | }, null, null, true) 318 | if (t.hit) { 319 | currentNode = t.value 320 | } else { 321 | if (t.bigger) { 322 | currentNode = children[t.index - 1] 323 | } else { 324 | currentNode = t.value 325 | } 326 | } 327 | if (!currentNode) { 328 | currentNode = children[0] 329 | break 330 | } 331 | if (!currentNode) { 332 | break 333 | } 334 | const innerEl = document.getElementById(currentNode._id).querySelector('.tree-node-inner') 335 | const of = getOf4(innerEl, currentTree.space) 336 | if ( of .y <= y && y <= of .y2) { 337 | break 338 | } 339 | } 340 | return currentNode 341 | }, 342 | targetNodeEl() { 343 | return document.getElementById(this.targetNode._id) 344 | }, 345 | // targetNodeInnerElOffset 346 | tiInnerEl() { 347 | return this.targetNodeEl.querySelector('.tree-node-inner') 348 | }, 349 | tiOffset() { 350 | return hp.getOffset(this.tiInnerEl) 351 | }, 352 | tiOf4() { 353 | return getOf4(this.tiInnerEl, this.currentTree.space) 354 | }, 355 | tiMiddleY() { 356 | return this.tiOffset.y + this.tiInnerEl.offsetHeight / 2 357 | }, 358 | // 359 | targetPrevEl() { 360 | // tree node 之间不要有其他元素, 否则这里会获取到错误的元素 361 | let r = this.targetNodeEl.previousSibling 362 | if (hp.hasClass(r, 'dragging')) { 363 | r = r.previousSibling 364 | } 365 | return r 366 | }, 367 | targetPrev() { 368 | const id = this.targetPrevEl.getAttribute('id') 369 | return this.currentTree.getNodeById(id) 370 | }, 371 | }) 372 | // attachCache end 373 | 374 | // decision start ================================= 375 | const executedRuleCache = {} 376 | // exec rule 377 | const exec = (ruleId) => { 378 | if (!executedRuleCache.hasOwnProperty(ruleId)) { 379 | let r 380 | try { 381 | r = rules[ruleId](info) 382 | } catch (e) { 383 | r = e 384 | try { 385 | if (process.env.DEVELOPE_SELF) { 386 | // only visible when develop its self 387 | console.warn(`failed to execute rule '${ruleId}'`, e); 388 | } 389 | } catch (e2) {} 390 | } 391 | executedRuleCache[ruleId] = r 392 | } 393 | return executedRuleCache[ruleId] 394 | } 395 | if (exec('currentTree existed') === true){ 396 | if (exec('targetNode is placeholder') === false){ 397 | if (exec('targetNode is the second child of root') === true){ 398 | if (exec('targetNode has children excluding placeholder') === false){ 399 | if (exec('on targetNode middle') === true){ 400 | targets['before'](info) 401 | } 402 | else if (exec('on targetNode middle') === false){ 403 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 404 | targets['append'](info) 405 | } 406 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 407 | targets['after'](info) 408 | } 409 | } 410 | } 411 | else if (exec('targetNode has children excluding placeholder') === true){ 412 | targets['prepend'](info) 413 | } 414 | } 415 | else if (exec('targetNode is the second child of root') === false){ 416 | if (exec('currentTree empty') === false){ 417 | if (exec('targetNode at top') === true){ 418 | if (exec('placeholder in currentTree') === true){ 419 | if (exec('targetNode has children excluding placeholder') === false){ 420 | if (exec('on targetNode middle') === false){ 421 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 422 | targets['after'](info) 423 | } 424 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 425 | targets['append'](info) 426 | } 427 | } 428 | else if (exec('on targetNode middle') === true){ 429 | targets['before'](info) 430 | } 431 | } 432 | else if (exec('targetNode has children excluding placeholder') === true){ 433 | if (exec('on targetNode middle') === false){ 434 | targets['prepend'](info) 435 | } 436 | else if (exec('on targetNode middle') === true){ 437 | targets['before'](info) 438 | } 439 | } 440 | } 441 | else if (exec('placeholder in currentTree') === false){ 442 | targets['before'](info) 443 | } 444 | } 445 | else if (exec('targetNode at top') === false){ 446 | if (exec('targetNode at bottom') === false){ 447 | if (exec('placeholder at top') === true){ 448 | targets['prepend'](info) 449 | } 450 | else if (exec('placeholder at top') === false){ 451 | if (exec('targetNode has children excluding placeholder') === true){ 452 | targets['prepend'](info) 453 | } 454 | else if (exec('targetNode has children excluding placeholder') === false){ 455 | if (exec('targetNode is 1st child') === false){ 456 | if (exec('targetNode is last child') === false){ 457 | if (exec('on targetNode middle') === true){ 458 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 459 | targets['append'](info) 460 | } 461 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 462 | targets['after'](info) 463 | } 464 | } 465 | else if (exec('on targetNode middle') === false){ 466 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 467 | targets['append'](info) 468 | } 469 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 470 | targets['after'](info) 471 | } 472 | } 473 | } 474 | else if (exec('targetNode is last child') === true){ 475 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 476 | targets['append'](info) 477 | } 478 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 479 | targets['after'](info) 480 | } 481 | } 482 | } 483 | else if (exec('targetNode is 1st child') === true){ 484 | if (exec('targetNode is last child') === true){ 485 | targets['append'](info) 486 | } 487 | else if (exec('targetNode is last child') === false){ 488 | if (exec('on targetNode middle') === false){ 489 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 490 | targets['after'](info) 491 | } 492 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 493 | targets['append'](info) 494 | } 495 | } 496 | else if (exec('on targetNode middle') === true){ 497 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 498 | targets['after'](info) 499 | } 500 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 501 | targets['append'](info) 502 | } 503 | } 504 | } 505 | } 506 | } 507 | } 508 | } 509 | else if (exec('targetNode at bottom') === true){ 510 | if (exec('placeholder in currentTree') === true){ 511 | if (exec('on targetNode middle') === false){ 512 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 513 | targets['append'](info) 514 | } 515 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 516 | targets['after'](info) 517 | } 518 | } 519 | else if (exec('on targetNode middle') === true){ 520 | targets['append'](info) 521 | } 522 | } 523 | else if (exec('placeholder in currentTree') === false){ 524 | targets['append'](info) 525 | } 526 | } 527 | } 528 | } 529 | else if (exec('currentTree empty') === true){ 530 | targets['append current tree'](info) 531 | } 532 | } 533 | } 534 | else if (exec('targetNode is placeholder') === true){ 535 | if (exec('targetNode at bottom') === false){ 536 | if (exec('targetNode is the second child of root') === false){ 537 | if (exec('targetNode is 1st child') === true){ 538 | if (exec('targetNode is last child') === false){ 539 | targets['nothing'](info) 540 | } 541 | else if (exec('targetNode is last child') === true){ 542 | if (exec('on targetNode middle') === false){ 543 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 544 | targets['after target parent'](info) 545 | } 546 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 547 | targets['nothing'](info) 548 | } 549 | } 550 | else if (exec('on targetNode middle') === true){ 551 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 552 | targets['after target parent'](info) 553 | } 554 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 555 | targets['nothing'](info) 556 | } 557 | } 558 | } 559 | } 560 | else if (exec('targetNode is 1st child') === false){ 561 | if (exec('targetNode is last child') === true){ 562 | if (exec('on targetNode middle') === true){ 563 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 564 | targets['after target parent'](info) 565 | } 566 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 567 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 568 | targets['append prev'](info) 569 | } 570 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 571 | targets['nothing'](info) 572 | } 573 | } 574 | } 575 | else if (exec('on targetNode middle') === false){ 576 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 577 | targets['after target parent'](info) 578 | } 579 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 580 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 581 | targets['append prev'](info) 582 | } 583 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 584 | targets['nothing'](info) 585 | } 586 | } 587 | } 588 | } 589 | else if (exec('targetNode is last child') === false){ 590 | if (exec('on targetNode middle') === true){ 591 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 592 | targets['nothing'](info) 593 | } 594 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 595 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 596 | targets['append prev'](info) 597 | } 598 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 599 | targets['nothing'](info) 600 | } 601 | } 602 | } 603 | else if (exec('on targetNode middle') === false){ 604 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 605 | targets['nothing'](info) 606 | } 607 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 608 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 609 | targets['append prev'](info) 610 | } 611 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 612 | targets['nothing'](info) 613 | } 614 | } 615 | } 616 | } 617 | } 618 | } 619 | else if (exec('targetNode is the second child of root') === true){ 620 | if (exec('on targetNode middle') === true){ 621 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 622 | targets['append prev'](info) 623 | } 624 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 625 | targets['nothing'](info) 626 | } 627 | } 628 | else if (exec('on targetNode middle') === false){ 629 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 630 | targets['append prev'](info) 631 | } 632 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 633 | targets['nothing'](info) 634 | } 635 | } 636 | } 637 | } 638 | else if (exec('targetNode at bottom') === true){ 639 | if (exec('targetNode is 1st child') === true){ 640 | if (exec('on targetNode middle') === false){ 641 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 642 | targets['after target parent'](info) 643 | } 644 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 645 | targets['nothing'](info) 646 | } 647 | } 648 | else if (exec('on targetNode middle') === true){ 649 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 650 | targets['nothing'](info) 651 | } 652 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 653 | targets['after target parent'](info) 654 | } 655 | } 656 | } 657 | else if (exec('targetNode is 1st child') === false){ 658 | if (exec('on targetNode middle') === false){ 659 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 660 | targets['after target parent'](info) 661 | } 662 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 663 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 664 | targets['append prev'](info) 665 | } 666 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 667 | targets['nothing'](info) 668 | } 669 | } 670 | } 671 | else if (exec('on targetNode middle') === true){ 672 | if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === true){ 673 | targets['after target parent'](info) 674 | } 675 | else if (exec(this.store.dir === 'rtl' ? 'at right' : 'at left') === false){ 676 | if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === true){ 677 | targets['append prev'](info) 678 | } 679 | else if (exec(this.store.dir === 'rtl' ? 'at indent left' : 'at indent right') === false){ 680 | targets['nothing'](info) 681 | } 682 | } 683 | } 684 | } 685 | } 686 | } 687 | } 688 | else if (exec('currentTree existed') === false){ 689 | targets['nothing'](info) 690 | } 691 | // decision end ================================= 692 | 693 | // 694 | } 695 | 696 | function getOf4(el, space) { 697 | const r = hp.getOffset(el) 698 | r.x2 = r.x + el.offsetWidth 699 | r.y2 = r.y + el.offsetHeight + space 700 | return r 701 | } 702 | 703 | autoMoveDragPlaceHolder.dragStart = function dragStart() {} 704 | autoMoveDragPlaceHolder.dragEnd = function dragEnd() { 705 | prevTree = null 706 | droppableIds = {} 707 | draggableIds = {} 708 | } 709 | -------------------------------------------------------------------------------- /src/components/temporarily-fix-overlapping-tree-issue.js: -------------------------------------------------------------------------------- 1 | import * as hp from 'helper-js' 2 | 3 | // document.elementsFromPoint Polyfill 4 | // from https://gist.github.com/iddan/54d5d9e58311b0495a91bf06de661380 5 | if (!document.elementsFromPoint) { 6 | document.elementsFromPoint = elementsFromPoint; 7 | } 8 | 9 | function elementsFromPoint(x, y) { 10 | var parents = []; 11 | var parent = void 0; 12 | do { 13 | if (parent !== document.elementFromPoint(x, y)) { 14 | parent = document.elementFromPoint(x, y); 15 | parents.push(parent); 16 | parent.style.pointerEvents = 'none'; 17 | } else { 18 | parent = false; 19 | } 20 | } while (parent); 21 | parents.forEach(function (parent) { 22 | return parent.style.pointerEvents = 'all'; 23 | }); 24 | return parents; 25 | } 26 | 27 | export default function getTreeByPoint(x, y, trees) { 28 | const els = document.elementsFromPoint(x, y) 29 | let treeEl 30 | let nodeEl 31 | const betweenEls = [] 32 | for (const el of els) { 33 | if (!nodeEl) { 34 | if (hp.hasClass(el, 'tree-node')) { 35 | nodeEl = el 36 | } 37 | } else { 38 | // console.log(el); 39 | if (hp.hasClass(el, 'tree')) { 40 | treeEl = el 41 | break 42 | } 43 | betweenEls.push(el) 44 | } 45 | } 46 | if (treeEl) { 47 | // is target tree is another tree, and be covered by other element, like modal, popup 48 | let covered = false 49 | if (!isParent(nodeEl, treeEl)) { 50 | // cross tree 51 | for (const el of betweenEls) { 52 | if (!isParent(el, treeEl)) { 53 | covered = true 54 | break 55 | } 56 | } 57 | } 58 | // 59 | if (!covered) { 60 | return trees.find(v => v.$el === treeEl) 61 | } 62 | } 63 | } 64 | 65 | function isParent(child, parent) { 66 | let cur = child 67 | while (cur) { 68 | cur = cur.parentNode 69 | if (cur === parent) { 70 | return true 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/examples/Base.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 61 | 62 | 64 | -------------------------------------------------------------------------------- /src/examples/CollapsingAnimation.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 84 | 85 | 87 | -------------------------------------------------------------------------------- /src/examples/CustomTreeNode.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 28 | 29 | 37 | -------------------------------------------------------------------------------- /src/examples/Empty.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 27 | 28 | 30 | -------------------------------------------------------------------------------- /src/examples/MaxLevel.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 82 | 83 | 85 | -------------------------------------------------------------------------------- /src/examples/RTL.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 63 | 64 | 66 | -------------------------------------------------------------------------------- /src/lib-entry.js: -------------------------------------------------------------------------------- 1 | export {default as Tree} from './components/Tree.vue' 2 | export {default as TreeNode} from './components/TreeNode.vue' 3 | export {default as DraggableTree} from './components/DraggableTree.vue' 4 | export {default as DraggableTreeNode} from './components/DraggableTreeNode.vue' 5 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | 6 | Vue.config.productionTip = false 7 | 8 | /* eslint-disable no-new */ 9 | new Vue({ 10 | render: h => h(App), 11 | }).$mount('#app') 12 | -------------------------------------------------------------------------------- /src/plugins/Cache.js: -------------------------------------------------------------------------------- 1 | export default class Cache { 2 | store = {}; 3 | has(name) { 4 | return this.store.hasOwnProperty(name) 5 | } 6 | remember(name, getter) { 7 | if (!this.has(name)) { 8 | this.store[name] = { 9 | value: getter() 10 | } 11 | } 12 | return this.store[name].value 13 | } 14 | forget(name) { 15 | if (name) { 16 | if (this.has(name)) { 17 | delete this.store[name] 18 | } 19 | } else { 20 | this.store = {} 21 | } 22 | } 23 | } 24 | export function attachCache(obj, cache, toCache) { 25 | for (const key in toCache) { 26 | Object.defineProperty(obj, key, { 27 | get() { 28 | return cache.remember(key, () => toCache[key].call(this)) 29 | }, 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/plugins/utils.js: -------------------------------------------------------------------------------- 1 | export function isPropTrue(v) { 2 | return v || v === '' 3 | } 4 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x8bc8e039d54CdD22af1c8123D01246A044B7EE42' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node './version-plus.js' 4 | npm run build 5 | git add . 6 | git commit -m "$1" 7 | git push origin master 8 | npm publish 9 | -------------------------------------------------------------------------------- /version-plus.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | const pkg = require('./package.json') 4 | const t = pkg.version.split('.') 5 | t[2] = parseInt(t[2]) + 1 6 | pkg.version = t.join('.') 7 | fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2)) 8 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | function resolve (dir) { 3 | return path.join(__dirname, dir) 4 | } 5 | 6 | module.exports = { 7 | lintOnSave: false, 8 | } 9 | -------------------------------------------------------------------------------- /warn.js: -------------------------------------------------------------------------------- 1 | console.warn('\x1b[35m', 'Please use he-tree-vue, vue-draggable-nested-tree will no longer be maintained.'); 2 | console.warn('\x1b[33m', 'Please use he-tree-vue, vue-draggable-nested-tree will no longer be maintained.'); 3 | console.warn('\x1b[31m', 'Please use he-tree-vue, vue-draggable-nested-tree will no longer be maintained.'); 4 | --------------------------------------------------------------------------------