Draggable component
HTML
<div id="vue">
<draggable header="Drag me">Hello</draggable>
<draggable header="Drag me">World</draggable>
<draggable header="Drag me">
<draggable header="Drag me">Nested!<draggable header="Drag me">World</draggable></draggable>
</draggable>
</div>
SCSS
body{
overflow-x:hidden;
}
.draggable{
position:relative;
background:white;
box-shadow:0px 1px 1px rgba(0,0,0,0.5);
transition:box-shadow 0.2s;
margin-bottom:10px;
&.dragging{
box-shadow:0px 3px 8px rgba(0,0,0,0.5);
z-index:200;
}
.header{
background:#444;
color:white;
padding:10px;
cursor:move;
user-select:none;
}
.content{
padding:20px;
}
}
JavaScript
let z = 1 //This shared variable is necessary so that the last dragged component is always on top
Vue.component('draggable', {
template:`
<div class="draggable" :class="{dragging}" :style="{top:top + 'px', left:left + 'px', zIndex:z}" @mousedown="updateZ">
<div class="header" @mousedown="dragStart">
{{header}}
</div>
<div class="content">
<slot></slot>
</div>
</div>
`,
props:['header'],
data(){
return {
top:0,
left:0,
z:1,
dragging:false
}
},
methods:{
move(e){
//prevent component from being dragged out of view
if(e.pageY > 0 && e.pageY < document.body.clientHeight){
this.top += e.movementY
}
if(e.pageX > 0 && e.pageX < document.body.clientWidth){
this.left += e.movementX
}
},
dragStart(){
this.dragging = true
//Need to add listeners to the document rather than the component, otherwise there will be problems if the mouse moves too fast
document.addEventListener('mousemove', this.move)
document.addEventListener('mouseup', this.dragStop)
document.addEventListener('mouseleave', this.dragStop)
},
dragStop(){
this.dragging = false
document.removeEventListener('mousemove', this.move)
document.addEventListener('mouseup', this.dragStop)
document.removeEventListener('mouseleave', this.dragStop)
},
updateZ(){
this.z = z += 1
}
}
})
new Vue({
el:'#vue'
})