@thisux/sveltednd
Version:
A lightweight, flexible drag and drop library for Svelte 5 applications.
94 lines (93 loc) • 3.77 kB
JavaScript
import { dndState } from '../stores/dnd.svelte.js';
const DEFAULT_DRAGGING_CLASS = 'dragging';
export function draggable(node, options) {
const draggingClass = (options.attributes?.draggingClass || DEFAULT_DRAGGING_CLASS).split(' ');
let initialX;
let initialY;
function isInteractiveElement(target) {
if (!options.interactive)
return false;
// Check if the target or its parents match any of the interactive selectors
return options.interactive.some((selector) => target.matches(selector) || target.closest(selector));
}
function handleDragStart(event) {
if (options.disabled)
return;
dndState.isDragging = true;
dndState.draggedItem = options.dragData;
dndState.sourceContainer = options.container;
dndState.targetContainer = null;
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', JSON.stringify(options.dragData));
}
node.classList.add(...draggingClass);
options.callbacks?.onDragStart?.(dndState);
// **Dispatch the custom event that bubbles up to the container**
const customEvent = new CustomEvent('dragstart-on-container', { bubbles: true });
node.dispatchEvent(customEvent);
}
function handleDragEnd() {
node.classList.remove(...draggingClass);
options.callbacks?.onDragEnd?.(dndState);
// Reset state
dndState.isDragging = false;
dndState.draggedItem = null;
dndState.sourceContainer = '';
dndState.targetContainer = null;
}
function handlePointerDown(event) {
if (options.disabled)
return;
// If the target is an interactive element, don't start dragging
if (isInteractiveElement(event.target)) {
return;
}
// Store initial pointer position
initialX = event.clientX;
initialY = event.clientY;
dndState.isDragging = true;
dndState.draggedItem = options.dragData;
dndState.sourceContainer = options.container;
dndState.targetContainer = null;
node.setPointerCapture(event.pointerId);
node.classList.add(...draggingClass);
options.callbacks?.onDragStart?.(dndState);
}
function handlePointerMove(event) {
if (!dndState.isDragging)
return;
// Optional: Update visual feedback or position
}
function handlePointerUp(event) {
if (!dndState.isDragging)
return;
node.releasePointerCapture(event.pointerId);
node.classList.remove(...draggingClass);
options.callbacks?.onDragEnd?.(dndState);
// Reset state
dndState.isDragging = false;
dndState.draggedItem = null;
dndState.sourceContainer = '';
dndState.targetContainer = null;
}
node.draggable = !options.disabled;
node.addEventListener('dragstart', handleDragStart);
node.addEventListener('dragend', handleDragEnd);
node.addEventListener('pointerdown', handlePointerDown);
node.addEventListener('pointermove', handlePointerMove);
node.addEventListener('pointerup', handlePointerUp);
return {
update(newOptions) {
options = newOptions;
node.draggable = !options.disabled;
},
destroy() {
node.removeEventListener('dragstart', handleDragStart);
node.removeEventListener('dragend', handleDragEnd);
node.removeEventListener('pointerdown', handlePointerDown);
node.removeEventListener('pointermove', handlePointerMove);
node.removeEventListener('pointerup', handlePointerUp);
}
};
}