Files
ComfyUI_frontend/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts
Christian Byrne c8146ffc64 Revert "fix dragging video/image components on Vue nodes triggers node drag (#5922)" (#6148)
## Summary

This PR reverts #5922 which fixed pointer capture behavior on video and
image preview components to prevent unintended node dragging.

## Changes

- Removes `data-capture-node="true"` attribute from `VideoPreview.vue`
and `ImagePreview.vue` components
- Removes pointer event delegation logic from
`useNodePointerInteractions.ts` composable
- Restores previous drag behavior where dragging on preview components
triggers node drag

## Reason for Revert

This changes the behavior from original Litegraph and is generally
annoying. Users would rather be able to drag the node than be able to
drag an image/video out from a node.

Reverts #5922

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6148-Revert-fix-dragging-video-image-components-on-Vue-nodes-triggers-node-drag-5922-2916d73d365081398bb5c20384e26bb8)
by [Unito](https://www.unito.io)
2025-10-19 12:28:40 -07:00

183 lines
5.2 KiB
TypeScript

import { computed, onUnmounted, ref, toValue } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
import { isMiddlePointerInput } from '@/base/pointerUtils'
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
import { useNodeLayout } from '@/renderer/extensions/vueNodes/layout/useNodeLayout'
export function useNodePointerInteractions(
nodeDataMaybe: MaybeRefOrGetter<VueNodeData | null>,
onNodeSelect: (event: PointerEvent, nodeData: VueNodeData) => void
) {
const nodeData = computed(() => {
const value = toValue(nodeDataMaybe)
if (!value) {
console.warn(
'useNodePointerInteractions: nodeDataMaybe resolved to null/undefined'
)
return null
}
return value
})
// Avoid potential null access during component initialization
const nodeIdComputed = computed(() => nodeData.value?.id ?? '')
const { startDrag, endDrag, handleDrag } = useNodeLayout(nodeIdComputed)
// Use canvas interactions for proper wheel event handling and pointer event capture control
const { forwardEventToCanvas, shouldHandleNodePointerEvents } =
useCanvasInteractions()
const forwardMiddlePointerIfNeeded = (event: PointerEvent) => {
if (!isMiddlePointerInput(event)) return false
forwardEventToCanvas(event)
return true
}
// Drag state for styling
const isDragging = ref(false)
const dragStyle = computed(() => {
if (nodeData.value?.flags?.pinned) {
return { cursor: 'default' }
}
return { cursor: isDragging.value ? 'grabbing' : 'grab' }
})
const startPosition = ref({ x: 0, y: 0 })
const handlePointerDown = (event: PointerEvent) => {
if (!nodeData.value) {
console.warn(
'LGraphNode: nodeData is null/undefined in handlePointerDown'
)
return
}
if (forwardMiddlePointerIfNeeded(event)) return
// Only start drag on left-click (button 0)
if (event.button !== 0) {
return
}
// Don't handle pointer events when canvas is in panning mode - forward to canvas instead
if (!shouldHandleNodePointerEvents.value) {
forwardEventToCanvas(event)
return
}
// Record position for drag threshold calculation
startPosition.value = { x: event.clientX, y: event.clientY }
onNodeSelect(event, nodeData.value)
if (nodeData.value.flags?.pinned) {
return
}
// Start drag using layout system
isDragging.value = true
// Set Vue node dragging state for selection toolbox
layoutStore.isDraggingVueNodes.value = true
startDrag(event)
}
const handlePointerMove = (event: PointerEvent) => {
if (forwardMiddlePointerIfNeeded(event)) return
if (isDragging.value) {
void handleDrag(event)
}
}
/**
* Centralized cleanup function for drag state
* Ensures consistent cleanup across all drag termination scenarios
*/
const cleanupDragState = () => {
isDragging.value = false
layoutStore.isDraggingVueNodes.value = false
}
/**
* Safely ends drag operation with proper error handling
* @param event - PointerEvent to end the drag with
*/
const safeDragEnd = async (event: PointerEvent): Promise<void> => {
try {
await endDrag(event)
} catch (error) {
console.error('Error during endDrag:', error)
} finally {
cleanupDragState()
}
}
/**
* Common drag termination handler with fallback cleanup
*/
const handleDragTermination = (event: PointerEvent, errorContext: string) => {
safeDragEnd(event).catch((error) => {
console.error(`Failed to complete ${errorContext}:`, error)
cleanupDragState() // Fallback cleanup
})
}
const handlePointerUp = (event: PointerEvent) => {
if (forwardMiddlePointerIfNeeded(event)) return
if (isDragging.value) {
handleDragTermination(event, 'drag end')
}
// Don't handle pointer events when canvas is in panning mode - forward to canvas instead
if (!shouldHandleNodePointerEvents.value) {
forwardEventToCanvas(event)
return
}
}
/**
* Handles pointer cancellation events (e.g., touch cancelled by browser)
* Ensures drag state is properly cleaned up when pointer interaction is interrupted
*/
const handlePointerCancel = (event: PointerEvent) => {
if (!isDragging.value) return
handleDragTermination(event, 'drag cancellation')
}
/**
* Handles right-click during drag operations
* Cancels the current drag to prevent context menu from appearing while dragging
*/
const handleContextMenu = (event: MouseEvent) => {
if (!isDragging.value) return
event.preventDefault()
// Simply cleanup state without calling endDrag to avoid synthetic event creation
cleanupDragState()
}
// Cleanup on unmount to prevent resource leaks
onUnmounted(() => {
if (!isDragging.value) return
cleanupDragState()
})
const pointerHandlers = {
onPointerdown: handlePointerDown,
onPointermove: handlePointerMove,
onPointerup: handlePointerUp,
onPointercancel: handlePointerCancel,
onContextmenu: handleContextMenu
}
return {
isDragging,
dragStyle,
pointerHandlers
}
}