@thisux/sveltednd
Version:
A lightweight, flexible drag and drop library for Svelte 5 applications.
111 lines (110 loc) • 4.32 kB
JavaScript
import { dndState } from '../stores/dnd.svelte.js';
const DEFAULT_DRAG_OVER_CLASS = 'drag-over';
export function droppable(node, options) {
const dragOverClass = (options.attributes?.draggingClass || DEFAULT_DRAG_OVER_CLASS).split(' ');
let dragEnterCounter = 0; // Initialize the counter
function handleDragEnter(event) {
if (options.disabled)
return;
event.preventDefault();
dragEnterCounter++;
dndState.targetContainer = options.container;
dndState.targetElement = event.target;
if (dragEnterCounter === 0)
return;
node.classList.add(...dragOverClass);
options.callbacks?.onDragEnter?.(dndState);
}
function handleDragLeave(event) {
if (options.disabled)
return;
dragEnterCounter--;
// check if element is still being dragged over
if (dragEnterCounter > 0)
return;
node.classList.remove(...dragOverClass);
options.callbacks?.onDragLeave?.(dndState);
if (dndState.targetContainer === options.container && dndState.targetElement === event.target) {
dndState.targetContainer = null;
dndState.targetElement = null;
}
}
function handleDragOver(event) {
if (options.disabled)
return;
event.preventDefault();
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move';
}
options.callbacks?.onDragOver?.(dndState);
}
async function handleDrop(event) {
if (options.disabled)
return;
event.preventDefault();
dragEnterCounter = 0; // Reset the counter
node.classList.remove(...dragOverClass);
try {
if (event.dataTransfer) {
const dragData = JSON.parse(event.dataTransfer.getData('text/plain'));
dndState.draggedItem = dragData;
}
await options.callbacks?.onDrop?.(dndState);
}
catch (error) {
console.error('Drop handling failed:', error);
}
}
function handleDragStartOnContainer(event) {
if (options.disabled)
return;
// Reset the counter and remove the class
dragEnterCounter = 0;
node.classList.remove(...dragOverClass);
}
function handlePointerOver(event) {
if (options.disabled || !dndState.isDragging)
return;
dndState.targetContainer = options.container;
node.classList.add(...dragOverClass);
options.callbacks?.onDragEnter?.(dndState);
}
function handlePointerOut(event) {
if (options.disabled || !dndState.isDragging)
return;
if (dndState.targetContainer === options.container) {
dndState.targetContainer = null;
}
node.classList.remove(...dragOverClass);
options.callbacks?.onDragLeave?.(dndState);
}
function handlePointerUp(event) {
if (options.disabled || !dndState.isDragging)
return;
node.classList.remove(...dragOverClass);
options.callbacks?.onDrop?.(dndState);
}
node.addEventListener('dragenter', handleDragEnter);
node.addEventListener('dragleave', handleDragLeave);
node.addEventListener('dragover', handleDragOver);
node.addEventListener('drop', handleDrop);
node.addEventListener('dragstart-on-container', handleDragStartOnContainer);
node.addEventListener('pointerover', handlePointerOver);
node.addEventListener('pointerout', handlePointerOut);
node.addEventListener('pointerup', handlePointerUp);
return {
update(newOptions) {
options = newOptions;
},
destroy() {
node.removeEventListener('dragenter', handleDragEnter);
node.removeEventListener('dragleave', handleDragLeave);
node.removeEventListener('dragover', handleDragOver);
node.removeEventListener('drop', handleDrop);
node.removeEventListener('dragstart-on-container', handleDragStartOnContainer);
node.removeEventListener('pointerover', handlePointerOver);
node.removeEventListener('pointerout', handlePointerOut);
node.removeEventListener('pointerup', handlePointerUp);
}
};
}