mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-23 22:25:05 +00:00
fix: firefox can give invalid drag coordinates causing incorrect drop position (https://bugzilla.mozilla.org/show_bug.cgi?id=1773886) - change to track during drag events
This commit is contained in:
@@ -456,4 +456,90 @@ describe('useNodeDragToCanvas', () => {
|
||||
expect(dispatchPointerDown(600, 250)).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('native drag position tracking', () => {
|
||||
beforeEach(() => {
|
||||
mockCanvas.canvas.getBoundingClientRect.mockReturnValue({
|
||||
left: 0,
|
||||
right: 500,
|
||||
top: 0,
|
||||
bottom: 500
|
||||
})
|
||||
mockConvertEventToCanvasOffset.mockReturnValue([300, 300])
|
||||
})
|
||||
|
||||
// happy-dom has no DragEvent constructor; MouseEvent works since the
|
||||
// handler only reads clientX/clientY.
|
||||
function fireDrag(x: number, y: number) {
|
||||
document.dispatchEvent(
|
||||
new MouseEvent('dragover', { clientX: x, clientY: y, bubbles: true })
|
||||
)
|
||||
}
|
||||
|
||||
it('should prefer tracked drag position over dragend coordinates', () => {
|
||||
const { startDrag, setupGlobalListeners, handleNativeDrop } =
|
||||
useNodeDragToCanvas()
|
||||
setupGlobalListeners()
|
||||
startDrag(mockNodeDef, 'native')
|
||||
|
||||
fireDrag(250, 250)
|
||||
// dragend supplies a bad position (the Firefox bug); the tracked one
|
||||
// from the last drag event should win.
|
||||
handleNativeDrop(1505, 102)
|
||||
|
||||
expect(mockConvertEventToCanvasOffset).toHaveBeenCalledWith({
|
||||
clientX: 250,
|
||||
clientY: 250
|
||||
})
|
||||
})
|
||||
|
||||
it('should ignore drag events with (0, 0)', () => {
|
||||
const { startDrag, setupGlobalListeners, handleNativeDrop } =
|
||||
useNodeDragToCanvas()
|
||||
setupGlobalListeners()
|
||||
startDrag(mockNodeDef, 'native')
|
||||
|
||||
fireDrag(250, 250)
|
||||
fireDrag(0, 0)
|
||||
handleNativeDrop(1505, 102)
|
||||
|
||||
expect(mockConvertEventToCanvasOffset).toHaveBeenCalledWith({
|
||||
clientX: 250,
|
||||
clientY: 250
|
||||
})
|
||||
})
|
||||
|
||||
it('should fall back to dragend coordinates when no drag fired', () => {
|
||||
const { startDrag, setupGlobalListeners, handleNativeDrop } =
|
||||
useNodeDragToCanvas()
|
||||
setupGlobalListeners()
|
||||
startDrag(mockNodeDef, 'native')
|
||||
|
||||
handleNativeDrop(250, 250)
|
||||
|
||||
expect(mockConvertEventToCanvasOffset).toHaveBeenCalledWith({
|
||||
clientX: 250,
|
||||
clientY: 250
|
||||
})
|
||||
})
|
||||
|
||||
it('should clear tracked position between drags', () => {
|
||||
const { startDrag, setupGlobalListeners, handleNativeDrop } =
|
||||
useNodeDragToCanvas()
|
||||
setupGlobalListeners()
|
||||
|
||||
startDrag(mockNodeDef, 'native')
|
||||
fireDrag(250, 250)
|
||||
handleNativeDrop(1505, 102)
|
||||
|
||||
// Second drag - no drag events, so we should fall back to args.
|
||||
startDrag(mockNodeDef, 'native')
|
||||
handleNativeDrop(300, 300)
|
||||
|
||||
expect(mockConvertEventToCanvasOffset).toHaveBeenLastCalledWith({
|
||||
clientX: 300,
|
||||
clientY: 300
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,16 +10,26 @@ const isDragging = ref(false)
|
||||
const draggedNode = shallowRef<ComfyNodeDefImpl | null>(null)
|
||||
const cursorPosition = ref({ x: 0, y: 0 })
|
||||
const dragMode = ref<DragMode>('click')
|
||||
const lastNativeDragPosition = shallowRef<{ x: number; y: number } | null>(null)
|
||||
let listenersSetup = false
|
||||
|
||||
function updatePosition(e: PointerEvent) {
|
||||
cursorPosition.value = { x: e.clientX, y: e.clientY }
|
||||
}
|
||||
|
||||
// Firefox dragend can report stale clientX/Y and `drag` can fire with
|
||||
// (0, 0). dragover on the target reliably reports real client coords.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1773886
|
||||
function trackNativeDragPosition(e: DragEvent) {
|
||||
if (e.clientX === 0 && e.clientY === 0) return
|
||||
lastNativeDragPosition.value = { x: e.clientX, y: e.clientY }
|
||||
}
|
||||
|
||||
function cancelDrag() {
|
||||
isDragging.value = false
|
||||
draggedNode.value = null
|
||||
dragMode.value = 'click'
|
||||
lastNativeDragPosition.value = null
|
||||
}
|
||||
|
||||
function isOverCanvas(clientX: number, clientY: number): boolean {
|
||||
@@ -81,6 +91,7 @@ function setupGlobalListeners() {
|
||||
document.addEventListener('pointerdown', blockCommitPointerDown, true)
|
||||
document.addEventListener('pointerup', endDrag, true)
|
||||
document.addEventListener('keydown', handleKeydown)
|
||||
document.addEventListener('dragover', trackNativeDragPosition)
|
||||
}
|
||||
|
||||
function cleanupGlobalListeners() {
|
||||
@@ -91,6 +102,7 @@ function cleanupGlobalListeners() {
|
||||
document.removeEventListener('pointerdown', blockCommitPointerDown, true)
|
||||
document.removeEventListener('pointerup', endDrag, true)
|
||||
document.removeEventListener('keydown', handleKeydown)
|
||||
document.removeEventListener('dragover', trackNativeDragPosition)
|
||||
|
||||
if (isDragging.value && dragMode.value === 'click') {
|
||||
cancelDrag()
|
||||
@@ -106,8 +118,9 @@ export function useNodeDragToCanvas() {
|
||||
|
||||
function handleNativeDrop(clientX: number, clientY: number) {
|
||||
if (dragMode.value !== 'native') return
|
||||
const tracked = lastNativeDragPosition.value
|
||||
try {
|
||||
addNodeAtPosition(clientX, clientY)
|
||||
addNodeAtPosition(tracked?.x ?? clientX, tracked?.y ?? clientY)
|
||||
} finally {
|
||||
cancelDrag()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user