Minimal drag and drop vue template

related: Vue

There are native browser APIs. Gotcha: It needs to be explicitly draggable="true". draggable is an enum, not a boolean.

<script lang="ts" setup>
const items = ref([
  {
    id: 0,
    title: 'Item A',
    list: 1,
  },
  {
    id: 1,
    title: 'Item B',
    list: 1,
  },
  {
    id: 2,
    title: 'Item C',
    list: 2,
  },
])
 
const startDrag = (evt, item) => {
  evt.dataTransfer.dropEffect = 'move'
  evt.dataTransfer.effectAllowed = 'move'
  evt.dataTransfer.setData('itemID', item.id)
}
 
const onDrop = (evt, list) => {
  console.log("ondrop", evt.dataTransfer.getData('itemID'), list)
  const itemID = evt.dataTransfer.getData('itemID')
  const item = items.value.find((item) => item.id == itemID)
  if (!item) {
    console.log("couldn't find item")
    return
  }
  item.list = list
}
 
const listOne = computed(() => {
  return items.value.filter((item) => item.list === 1)
})
 
const listTwo = computed(() => {
  return items.value.filter((item) => item.list === 2)
})
 
</script>
 
<template lang="pug">
div
  .drop-zone(@drop="onDrop($event, 1)", @dragover.prevent, @dragenter.prevent)
    .drag-el(v-for='item in listOne', :key='item.title', draggable="true", @dragstart="startDrag($event, item)") {{ item.title }}
 
  .drop-zone(@drop="onDrop($event, 2)", @dragover.prevent, @dragenter.prevent)
    .drag-el(v-for='item in listTwo', :key='item.title', draggable="true", @dragstart="startDrag($event, item)") {{ item.title }}
</template>
 
<style scoped>
.drop-zone {
  @apply bg-neutral-800;
  margin-bottom: 10px;
  padding: 10px;
}
 
.drag-el {
  background-color: #fff;
  margin-bottom: 10px;
  padding: 5px;
}
</style>

Resources