Skip to content

Instantly share code, notes, and snippets.

@PuruVJ
Created August 30, 2025 10:02
Show Gist options
  • Save PuruVJ/b58e046cecebad918672d6e6fefc1578 to your computer and use it in GitHub Desktop.
Save PuruVJ/b58e046cecebad918672d6e6fefc1578 to your computer and use it in GitHub Desktop.
<script lang="ts">
import { draggable, ghost } from '@neodrag/svelte';
import { droppable } from '@neodrag/svelte/drop';
let tasks = $state([
{ id: '1', title: 'Task 1', column: 'todo' },
{ id: '2', title: 'Task 2', column: 'todo' },
{ id: '3', title: 'Task 3', column: 'done' },
]);
let draggedTask = $state(null);
function getTasksForColumn(columnId) {
return tasks.filter((task) => task.column === columnId);
}
function startDrag(task) {
draggedTask = task;
console.log('DRAG START:', task.id);
}
function endDrag() {
// Delay clearing draggedTask to allow onDrop to process first
setTimeout(() => {
draggedTask = null;
console.log('DRAG END');
}, 0);
}
function dropInColumn(columnId) {
console.log('DROP ATTEMPT:', draggedTask?.id, 'to', columnId);
if (draggedTask && draggedTask.column !== columnId) {
console.log('ACTUAL DROP:', draggedTask.id, 'from', draggedTask.column, 'to', columnId);
// Direct mutation works in Svelte 5
const task = tasks.find((t) => t.id === draggedTask!.id);
if (task) {
task.column = columnId;
console.log('UPDATED TASK:', task);
}
} else {
console.log('SAME COLUMN DROP - NO CHANGE NEEDED');
}
}
</script>
<h1>Bare Kanban</h1>
<div style="display: flex; gap: 20px;">
<div
style="border: 1px solid black; width: 200px; min-height: 300px; padding: 10px;"
{@attach droppable([
{
name: 'todo-drop',
onDrop() {
dropInColumn('todo');
},
},
])}
>
<h2>TODO</h2>
{#each getTasksForColumn('todo') as task (task.id)}
<div
style="border: 1px solid red; padding: 5px; margin: 5px 0; background: white;"
{@attach draggable([
ghost({ opacity: 0.2 }),
{
name: 'task-drag',
start() {
startDrag(task);
},
end() {
endDrag();
},
},
])}
>
{task.title}
</div>
{/each}
</div>
<div
style="border: 1px solid black; width: 200px; min-height: 300px; padding: 10px;"
{@attach droppable([
{
name: 'done-drop',
onDrop() {
console.log(2);
dropInColumn('done');
},
},
])}
>
<h2>DONE</h2>
{#each getTasksForColumn('done') as task (task.id)}
<div
style="border: 1px solid green; padding: 5px; margin: 5px 0; background: white;"
{@attach draggable([
ghost({ opacity: 0.2 }),
{
name: 'task-drag',
start() {
startDrag(task);
},
end() {
endDrag();
},
},
])}
>
{task.title}
</div>
{/each}
</div>
</div>
<p>Dragging: {draggedTask?.title || 'none'}</p>
<p>Tasks: {JSON.stringify(tasks)}</p>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment