<div id="app">
  <div v-dragdrop="chapters" order="order">
    <div v-for="chapter in chapters" :key="chapter.id">
      {{ chapter.title }} 
      <strong>(order: {{ chapter.order }})</strong>
    </div>
  </div>
</div>
* {
  margin: 0;
  padding: 0;
}

html, body, #app {
  height:100%;
  font-family: system, helvetica;
}

#app {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #F4FAFA;
  background: url('http://res.cloudinary.com/lorisleiva/image/upload/v1519826829/backgrounds/1.jpg') center;
  background-size: cover;
  
}

#app > div {
  padding: 1em;
  border-radius: 1em;
  background-color: white;
  cursor: move;
  box-shadow: 2px 2px 5px rgba(0,0,0,0.05)
}

#app > div > div {
  padding: .5em 1em;
  border-radius: .5em;
}


#app > div > div:hover {
  background-color: #455;
  color: white;
}
/**
 * Dragdrop directive.
 */

let arrays = [];
let drakes = [];

Vue.directive('dragdrop', {
    bind (container, binding, vnode) {
        let orderProperty = vnode.data.attrs ? vnode.data.attrs.order : undefined;
        let items = binding.value || [];
        let dragIndex;

        let drake = dragula([container])
            .on('drag', (el, source) => {
                dragIndex = findDomIndex(source, el);
            })
            .on('drop', (el, target) => {
                move(items, dragIndex, findDomIndex(target, el));
                if (orderProperty) reorder(items, orderProperty);
            });
      
        addDrake(items, drake);
    },

    unbind (container, binding, vnode) {
        drake.destroy();
    }
});

/**
 * Find the index of an DOM element within a given container.
 */
function findDomIndex(container, el) {
    return Array.prototype.indexOf.call(container.children, el);
}

/**
 * Move an array item from one index to another.
 * The given array is transformed, not returned.
 */
function move(array, fromIndex, toIndex) {
    array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
}

/**
 * Reorder the items of an array from 0 to `array.length`.
 * The new order is stored on the given `orderProperty`.
 * The given array is transformed, not returned.
 */
function reorder(array, orderProperty) {
    let newOrder = 0;
    array.forEach(item => { _.set(item, orderProperty, newOrder++) })
}

/**
 * Register a drake instance based on the reference of the given array.
 */
function addDrake(array, drake) {
    if (arrays.indexOf(array) >= 0) return;
    arrays.push(array);
    drakes.push(drake);
}

/**
 * Retrieve a drake instance based on the reference of the given array.
 */
function getDrake(array) {
    let drakeIndex = arrays.indexOf(array);
    if (drakeIndex >= 0) return drakes[drakeIndex];
}

/**
 * Start the VueJS application.
 */
new Vue({
  el: '#app',
  data: {
    chapters: [
      { id: 1, title: 'The Boy Who Lived', order: 0 }, 
      { id: 2, title: 'The Vanishing Glass', order: 1 }, 
      { id: 3, title: 'The Letters from No One', order: 2 }, 
      { id: 4, title: 'The Keeper of the Keys', order: 3 }, 
      { id: 5, title: 'Diagon Alley', order: 4 }, 
    ]
  }
});
View Compiled
Rerun