import type { Ref } from 'vue' import type { CommonProps } from './useCommon' type DragAndDropProps = Pick< CommonProps, 'dragEnterFunc' | 'dragOverFunc' | 'dragLeaveFunc' | 'dropFunc' > interface DragAndDropOptions { targetRef: Ref value: T resetValue: T type: string scope: any } interface QueuedDragTarget { frame: number value: T } const queuedDragTargets = new WeakMap, QueuedDragTarget>() function requestFrame(callback: FrameRequestCallback): number | undefined { return typeof globalThis.requestAnimationFrame === 'function' ? globalThis.requestAnimationFrame(callback) : undefined } function cancelFrame(frame: number): void { if (typeof globalThis.cancelAnimationFrame === 'function') { globalThis.cancelAnimationFrame(frame) } } function updateDragTarget(targetRef: Ref, value: T): void { if (!Object.is(targetRef.value, value)) { targetRef.value = value } } function cancelQueuedDragTarget(targetRef: Ref): void { const queued = queuedDragTargets.get(targetRef as Ref) if (queued !== undefined) { cancelFrame(queued.frame) queuedDragTargets.delete(targetRef as Ref) } } function queueDragTargetUpdate(targetRef: Ref, value: T): void { const queued = queuedDragTargets.get(targetRef as Ref) as QueuedDragTarget | undefined if (queued !== undefined) { queued.value = value return } const frame = requestFrame(() => { const queued = queuedDragTargets.get(targetRef as Ref) as | QueuedDragTarget | undefined if (queued !== undefined) { queuedDragTargets.delete(targetRef as Ref) updateDragTarget(targetRef, queued.value) } }) if (frame === undefined) { updateDragTarget(targetRef, value) return } queuedDragTargets.set(targetRef as Ref, { frame, value }) } function runDragCallback( callback: ((_event: Event, _type: string, _scope: any) => boolean) | undefined, event: DragEvent, { targetRef, value, resetValue, type, scope }: DragAndDropOptions, immediate = false, ): void { if (immediate === true) { cancelQueuedDragTarget(targetRef) } if (typeof callback !== 'function') return const nextValue = callback(event, type, { scope }) === true ? value : resetValue if (immediate === true) { updateDragTarget(targetRef, nextValue) } else { queueDragTargetUpdate(targetRef, nextValue) } } export function getDragEventHandlers( props: DragAndDropProps, options: DragAndDropOptions, ): Record void> { return { onDragenter: (event: DragEvent): void => { runDragCallback(props.dragEnterFunc, event, options) }, onDragover: (event: DragEvent): void => { runDragCallback(props.dragOverFunc, event, options) }, onDragleave: (event: DragEvent): void => { runDragCallback(props.dragLeaveFunc, event, options) }, onDrop: (event: DragEvent): void => { runDragCallback(props.dropFunc, event, options, true) }, } }