UNPKG

@thisux/sveltednd

Version:

A lightweight, flexible drag and drop library for Svelte 5 applications.

111 lines (110 loc) 4.32 kB
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); } }; }