Last active
December 8, 2017 00:36
-
-
Save martinandersen3d/3ae79ab03cd7a43b4dfe31cd56f2bf14 to your computer and use it in GitHub Desktop.
vue draggable nested tree [Solution & Working, vue 2.x]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PARENT ----------------------------------------------------- | |
<template> | |
<div> | |
<devinfo :code='$data' name='treeParent' ></devinfo> | |
<b-field label="Add Page"> | |
<input ref='pageTextField' @keyup.enter="addPageFromTextfield" v-model="textfieldvalue"> | |
</b-field> | |
<treeChild :data="data"></treeChild> | |
</div> | |
</template> | |
<script> | |
export default { | |
methods:{ | |
addPageFromTextfield () { | |
// var node = new TreeNode(this.textfieldvalue, false, Math.floor(Math.random() * (99999999 - 11111111) ) + 11111111) | |
// if (!this.data.children) this.data.children = [] | |
// node['funky']="asda" | |
// console.log(node); | |
// this.data.addChildren(node) | |
// this.textfieldvalue ="" | |
this.addPage(this.textfieldvalue) | |
this.$refs.pageTextField.select() | |
}, | |
addPage(newPageName){ | |
this.data.push({ | |
name: newPageName, | |
show:true, | |
type:'page', | |
children: [] | |
}) | |
}// addPage | |
}, | |
data() { | |
return { | |
textfieldvalue:"", | |
data: [ | |
{ | |
name: "FolderA", | |
show:true, | |
freeze:true, | |
type:'page', | |
children: [{ | |
name: "Alpha-Folder", | |
show:false, | |
freeze:true, | |
children: [{ | |
name: "Sub-Alpha1" | |
}, | |
{ | |
name: "Sub-Alpha2" | |
}, | |
{ | |
name: "Sub-Alpha3", | |
freeze:true | |
}, | |
{ | |
name: "Sub-Alpha4" | |
}, | |
{ | |
name: "Sub-Alpha5" | |
} | |
] | |
}, | |
{ | |
name: "Alpha2" | |
}, | |
{ | |
name: "Alpha3" | |
}, | |
{ | |
name: "Alpha4" | |
}, | |
{ | |
name: "Alpha5" | |
} | |
] | |
}, | |
{ | |
name: "FolderB", | |
show:true, | |
type:'page', | |
children: [{ | |
name: "Bravo1" | |
}, | |
{ | |
name: "Bravo2" | |
}, | |
{ | |
name: "Bravo3" | |
}, | |
{ | |
name: "Bravo4" | |
}, | |
{ | |
name: "Bravo5" | |
} | |
] | |
}, | |
{ | |
name: "FolderC", | |
type:'page', | |
freeze:true, | |
show:false, | |
children:[] | |
}, | |
{ | |
name: "FolderD", | |
type:'page', | |
show:false, | |
children:[] | |
} | |
] | |
} | |
}, //data | |
} | |
</script> | |
// CHILD ----------------------------------------------------- | |
<template> | |
<draggable :list="data" :options="folderOptions" id="treeWrapper" @start="dragStart" @end="dragEnd" :move="checkMove"> | |
<template v-for="(node, index) in data" > | |
<ul v-if="node.children" class="node" id="asdasdasd"> | |
<div class=" treeItem "> | |
<span v-if="node.type == 'page'" @click="node.show =! node.show"><ico :i=" iconFolder( node.show ) " o='' :styleDiv=" 'display: inline-block' "></ico> </span> | |
<span v-else @click="node.show =! node.show"><ico :i=" iconPlus( node.show ) " o='' :styleDiv=" 'display: inline-block' "></ico> </span> | |
<span @dblclick="renameItem(node)" @click="setActivePage(_uid +'-' + index)" :class=" { treeItemFrozen : node.freeze}">{{ node.name }}</span> | |
</div> | |
<traverser v-if="node.show" :data="node.children" :options="nodeOptions" ></traverser> | |
<!-- Empy items while dragging --> | |
<traverser v-if="node.children && node.children.length == 0" id="innerTreeWrapper" :data="node.children" :options="nodeOptions" :class=" [ 'dragAreaInActive', {dragAreaActive : isDragging } ] "></traverser> | |
</ul> | |
<li v-else class="leaf treeItem" @mouseover="setActivePage(_uid +'-' + index)" @mouseout="fakeMouseOverId=''"> | |
<span v-if="node.type == 'page'" @click="node.show =! node.show" ><ico :i=" iconFolder( node.show ) " o='' :styleDiv=" 'display: inline-block' "></ico> {{ node.name }} </span> | |
<span else @dblclick="renameItem(node)" @click="setActivePage(_uid +'-' + index)" :class=" { treeItemFrozen : node.freeze}">{{ node.name }}</span> | |
<span v-if="fakeMouseOverId == _uid +'-' + index"> | |
<span @click="deleteItem(data, index)" class='iconRight'><ico i=" fa-trash-o " o='' :styleDiv=" 'display: inline-block' " ></ico></span> | |
<span @click="renameItem(node)" class='iconRight'><ico i=" fa-pencil-square-o " o='' :styleDiv=" 'display: inline-block' " ></ico></span> | |
</span> | |
<traverser v-if="node.children && node.children.length == 0" id="innerTreeWrapper" :data="node.children" :options="nodeOptions" :class=" [ 'dragAreaInActive', { dragAreaActive : isDragging } ] "></traverser> | |
</li> | |
</template> | |
</draggable> | |
</template> | |
<script> | |
import draggable from 'vuedraggable'; | |
export default { | |
name: 'traverser', | |
data () { | |
return { | |
folderOptions:{group:'folders', animation:100}, | |
nodeOptions:{group:'people', animation:100}, | |
isDragging: false, | |
isActivePage:'', | |
fakeMouseOverId:'' // index+_uid temporary id | |
}; | |
}, | |
props: { | |
data: {}, | |
options: {}, | |
},//data | |
methods:{ | |
setActivePage(e){ | |
this.fakeMouseOverId = e | |
}, | |
checkMove(evt){ | |
var itemMoved = evt.draggedContext.element.name // String | Returnere navnet på den component jeg trækker. Det navn den returnere her er navnet//!=='apple' | |
var fromIndex = evt.draggedContext.index // Int | from-index på componenten, hvor den bliver trukket fra. | |
var toIndex = evt.draggedContext.futureIndex // int | target-index på der component der bliver trukket til. | |
var idOfTarget = evt.to.id // int | target-id på der component der bliver trukket til. | |
var targetHtml = evt.to // int | target id navneet på target html objektet. | |
var relatedContext = evt.relatedContext // int | target id navneet på target html objektet. | |
// console.log('---------------------------------------------'); | |
// console.log('checkMove:', itemMoved) | |
// console.log('checkMove - fromIndex:', fromIndex) | |
// console.log('checkMove - toIndex:', toIndex) | |
// console.log('checkMove - idOfTarget:', idOfTarget) | |
// console.log('checkMove - targetHtml:', targetHtml) | |
// console.log('checkMove - relatedContext:', relatedContext) | |
// console.log('evt:', evt) | |
// console.log('path:', evt.path) | |
var arr = evt.to; | |
function parentHasClass(val) { | |
var result = val.hasAttribute('class') && val.className == "node"; | |
return result; | |
} | |
// var res = arr.find(parentHasClass); // 130 | |
// console.log(res); | |
return (evt.draggedContext.element.freeze!==true); | |
}, | |
renameItem(node){ | |
var person = prompt("Rename - Please choose a new name:", node.name); | |
if (person != null) { | |
node.name = person | |
} | |
}, //renameItem end | |
deleteItem(data, index){ | |
var theName = data[index].name | |
var r = confirm("Are you shure you want to delete this item: \n \n" + theName); | |
if (r == true) { | |
data.splice(index,1) | |
} | |
}, //deleteItem end | |
iconFolder(val){ | |
if (val === true){ return 'folder-open' } else { return 'folder' } | |
}, //iconPlus end | |
iconPlus(val){ | |
if (val === true){ return 'minus-square-o' } else { return 'plus-square-o' } | |
}, //iconPlus end | |
dragStart() { | |
this.isDragging = true | |
}, | |
dragEnd(evt) { | |
this.isDragging = false | |
console.log('end:', evt.path); | |
}, | |
} | |
} // export | |
</script> | |
<style scoped> | |
.leaf{ | |
padding-left:22px; | |
list-style-type: none; | |
font-size:14px; | |
} | |
.node{ | |
padding-left:20px; | |
font-size:16px; | |
} | |
.treeItem{ | |
cursor: pointer; | |
} | |
.treeItem:hover{ | |
color: #2196f3; | |
background-color: rgba(0,0,0,0.05) | |
} | |
.treeItemFrozen{ | |
color: darkgray; | |
} | |
.iconContainer{ | |
display: inline-block !important; | |
} | |
.dragAreaActive { | |
min-height: 15px !important; | |
display: block; | |
background:green !important; | |
} | |
.dragAreaInActive { | |
min-height: 10px; | |
display: block; | |
background:red; | |
} | |
.iconRight{ | |
float: right; | |
margin-right: 10px; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment