From badc2526a8b2fd3973dc4dc0ed4b9ea047c22296 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 | 22 ++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 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..b95f40e 100644 --- a/src/lib/actions/droppable.ts +++ b/src/lib/actions/droppable.ts @@ -5,15 +5,18 @@ 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; + + if (dragEnterCounter === 0) return; node.classList.add(...dragOverClass); options.callbacks?.onDragEnter?.(dndState as DragDropState); @@ -22,10 +25,10 @@ export function droppable(node: HTMLElement, options: DragDropOptions) { function handleDragLeave(event: DragEvent) { if (options.disabled) return; - const target = event.target as HTMLElement; + dragEnterCounter--; // check if element is still being dragged over - if (!dndState.targetElement?.isSameNode(target)) return; + if (dragEnterCounter > 0) return; node.classList.remove(...dragOverClass); @@ -50,6 +53,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 +68,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 +103,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 +119,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);