📢 Nuovo Corso Laravel API disponibile!

Drag and Drop

Traccia

L’obiettivo di oggi è creare una Kanban con dei task trascinabili. Per Kanban si intende una lavagna divisa in colonne che mostrano lo stato di avanzamento dei lavori. Nell’esempio di oggi avremo 4 colonne per task (compiti) da svolgere in coda, aperti, in revisione e completati.

La parte fondamentale dell’esercizio è inserire la logica di drag and drop per trascinare i task da una colonna all’altra.

Parte opzionale: gestite non solo la parte grafica del drag and drop, ma anche i dati che effettivamente vengono spostati tra colonne. Potete gestirlo come un array di oggetti (colonne) al cui interno c’è un array di oggetti (task). Trascinando il task su un’altra colonna bisogna spostare il task anche all’interno degli array corretti.

Parte opzionale extra (tutto compreso nella soluzione): salvate la posizione dei task in modo permanente utilizzando il localstorage del browser ed al caricamento della pagina generate i task nella loro posizione corrente.

Se il risultato è corretto, ogni soluzione è valida.

Soluzione

Pagina HTML

<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div class="flex flex-row p-10 m-10 h-[700px] gap-10">
<div
class="colonna rounded-xl bg-slate-200 p-10 w-80 flex flex-col gap-4 justify-start"
data-column="0">
<h2 class="text-2xl text-center font-bold mb-4">In Coda</h2>
<!-- <div class="task p-4 rounded-xl shadow-xl bg-white" draggable="true" data-task="0">
Rispondere alle email
</div>
<div class="task p-4 rounded-xl shadow-xl bg-white" draggable="true" data-task="1">
Allenare gambe
</div>
<div class="task p-4 rounded-xl shadow-xl bg-white" draggable="true" data-task="2">
Ricaricare credito telefono
</div>
<div class="task p-4 rounded-xl shadow-xl bg-white" draggable="true" data-task="3">
Prenotare visita dentista
</div> -->
</div>
<div
class="colonna rounded-xl bg-slate-200 p-10 w-80 flex flex-col gap-4 justify-start"
data-column="1">
<h2 class="text-2xl text-center font-bold mb-4">Aperto</h2>
</div>
<div
class="colonna rounded-xl bg-slate-200 p-10 w-80 flex flex-col gap-4 justify-start"
data-column="2">
<h2 class="text-2xl text-center font-bold mb-4">In Revisione</h2>
</div>
<div
class="colonna rounded-xl bg-slate-200 p-10 w-80 flex flex-col gap-4 justify-start"
data-column="3">
<h2 class="text-2xl text-center font-bold mb-4">Completato</h2>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

Pagina Script.js

Dati di placeholder iniziali e dati presi dal localStorage

const placeholderData = [
{
id: 0,
name: "In Coda",
tasks: [
{ id: 0, name: "Rispondere alle email" },
{ id: 1, name: "Allenare gambe" },
{ id: 2, name: "Ricaricare credito telefono" },
{ id: 3, name: "Prenotare visita dentista" },
],
},
{
id: 1,
name: "Aperto",
tasks: [],
},
{
id: 2,
name: "In Revisione",
tasks: [],
},
{
id: 3,
name: "Completato",
tasks: [],
},
];
let data = localStorage.getItem("data")
? JSON.parse(localStorage.getItem("data"))
: placeholderData;

Codice da eseguire al caricamento della pagina

generateTasks();
const tasks = document.querySelectorAll(".task");
const colonne = document.querySelectorAll(".colonna");
let dragItem = null;
let dragData = null;
tasks.forEach((task) => {
task.addEventListener("dragstart", dragStart);
task.addEventListener("dragend", dragEnd);
});
colonne.forEach((colonna) => {
colonna.addEventListener("dragover", dragOver);
colonna.addEventListener("dragenter", dragEnter);
colonna.addEventListener("dragleave", dragLeave);
colonna.addEventListener("drop", dragDrop);
});

Varie funzioni del drag and drop

function dragStart() {
console.log("dragStart");
setTimeout(() => this.classList.add("hidden"), 0);
dragItem = this;
const indexColonna = data.findIndex((colonna) => {
return colonna.id == this.parentElement.getAttribute("data-column");
});
const indexTask = data[indexColonna].tasks.findIndex((task) => {
return task.id == this.getAttribute("data-task");
});
dragData = data[indexColonna].tasks.splice(indexTask, 1)[0];
console.log("splicing", dragData);
localStorage.setItem("data", JSON.stringify(data));
}
function dragEnd() {
this.classList.remove("hidden");
console.log("dragEnd");
dragItem = null;
data[this.parentElement.getAttribute("data-column")].tasks.push(dragData);
console.log(data);
localStorage.setItem("data", JSON.stringify(data));
}
function dragOver(e) {
e.preventDefault();
console.log("dragOver");
}
function dragEnter() {
console.log("dragEnter");
}
function dragLeave() {
console.log("dragLeave");
}
function dragDrop() {
console.log("dragDrop");
this.append(dragItem);
}

Funzione per generare i task

function generateTasks() {
data.forEach((colonna) => {
const targetColumn = document.querySelector(
`[data-column='${colonna.id}']`
);
colonna.tasks.forEach((task) => {
const element = document.createElement("div");
element.className = "task p-4 rounded-xl shadow-xl bg-white";
element.setAttribute("draggable", true);
element.setAttribute("data-task", task.id);
const text = document.createTextNode(task.name);
element.appendChild(text);
targetColumn.appendChild(element);
});
});
}