From cf4f62e42c6ba25ee8fb3c6ceb28015df9d39472 Mon Sep 17 00:00:00 2001 From: colecrouter Date: Tue, 26 Nov 2024 09:46:12 -0600 Subject: [PATCH] feat: add custom dragstart event and improve drag enter/leave handling --- src/lib/actions/draggable.ts | 4 ++++ src/lib/actions/droppable.ts | 40 +++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/lib/actions/draggable.ts b/src/lib/actions/draggable.ts index ad370ef..019bd02 100644 --- a/src/lib/actions/draggable.ts +++ b/src/lib/actions/draggable.ts @@ -21,6 +21,10 @@ export function draggable(node: HTMLElement, options: DragDropOptions) { node.classList.add(...draggingClass); options.callbacks?.onDragStart?.(dndState as DragDropState); + + // **Dispatch the custom event that bubbles up to the container** + const customEvent = new CustomEvent('dragstart-on-container', { bubbles: true }); + node.dispatchEvent(customEvent); } function handleDragEnd() { diff --git a/src/lib/actions/droppable.ts b/src/lib/actions/droppable.ts index 9511600..c7f928e 100644 --- a/src/lib/actions/droppable.ts +++ b/src/lib/actions/droppable.ts @@ -5,34 +5,35 @@ const DEFAULT_DRAG_OVER_CLASS = 'drag-over'; export function droppable(node: HTMLElement, options: DragDropOptions) { const dragOverClass = (options.attributes?.draggingClass || DEFAULT_DRAG_OVER_CLASS).split(' '); + let dragEnterCounter = 0; // Initialize the counter function handleDragEnter(event: DragEvent) { if (options.disabled) return; event.preventDefault(); - const target = event.target as HTMLElement; + dragEnterCounter++; dndState.targetContainer = options.container; - dndState.targetElement = target; + dndState.targetElement = event.target as HTMLElement; - node.classList.add(...dragOverClass); - options.callbacks?.onDragEnter?.(dndState as DragDropState); - } + if (dragEnterCounter === 1) { + node.classList.add(...dragOverClass); + options.callbacks?.onDragEnter?.(dndState as DragDropState); + } + } function handleDragLeave(event: DragEvent) { if (options.disabled) return; - const target = event.target as HTMLElement; - - // check if element is still being dragged over - if (!dndState.targetElement?.isSameNode(target)) return; - - node.classList.remove(...dragOverClass); + dragEnterCounter--; - options.callbacks?.onDragLeave?.(dndState as DragDropState); + if (dragEnterCounter === 0) { + node.classList.remove(...dragOverClass); + options.callbacks?.onDragLeave?.(dndState as DragDropState); - dndState.targetContainer = null; - dndState.targetElement = null; + dndState.targetContainer = null; + dndState.targetElement = null; + } } function handleDragOver(event: DragEvent) { @@ -50,6 +51,7 @@ export function droppable(node: HTMLElement, options: DragDropOptions) { if (options.disabled) return; event.preventDefault(); + dragEnterCounter = 0; // Reset the counter node.classList.remove(...dragOverClass); try { @@ -64,6 +66,14 @@ export function droppable(node: HTMLElement, options: DragDropOptions) { } } + function handleDragStartOnContainer(event: Event) { + if (options.disabled) return; + + // Reset the counter and remove the class + dragEnterCounter = 0; + node.classList.remove(...dragOverClass); + } + function handlePointerOver(event: PointerEvent) { if (options.disabled || !dndState.isDragging) return; @@ -91,6 +101,7 @@ export function droppable(node: HTMLElement, options: DragDropOptions) { 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); @@ -106,6 +117,7 @@ export function droppable(node: HTMLElement, options: DragDropOptions) { 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);