mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-27 02:04:09 +00:00
## 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)
183 lines
5.2 KiB
TypeScript
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
|
|
}
|
|
}
|