Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): check if dragEnd event is UseDrag or MouseTouch event #1680

Merged
merged 4 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/early-walls-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vue-flow/core": patch
---

check if event on drag end is mouse/touch event or a usedrag event
22 changes: 12 additions & 10 deletions packages/core/src/components/Nodes/NodeWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import {
elementSelectionKeys,
getXYZPos,
handleNodeClick,
snapPosition,
} from '../../utils'
import { NodeId, NodeRef, Slots } from '../../context'
import { isInputDOMNode, useDrag, useNode, useNodeHooks, useUpdateNodePositions, useVueFlow } from '../../composables'
import type { NodeComponent } from '../../types'
import type { MouseTouchEvent, NodeComponent } from '../../types'

interface Props {
id: string
Expand Down Expand Up @@ -321,14 +322,15 @@ const NodeWrapper = defineComponent({
}
/** this re-calculates the current position, necessary for clamping by a node's extent */
function clampPosition() {
const nextPos = node.computedPosition

if (snapToGrid.value) {
nextPos.x = snapGrid.value[0] * Math.round(nextPos.x / snapGrid.value[0])
nextPos.y = snapGrid.value[1] * Math.round(nextPos.y / snapGrid.value[1])
}

const { computedPosition, position } = calcNextPosition(node, nextPos, emits.error, nodeExtent.value, parentNode.value)
const nextPosition = node.computedPosition

const { computedPosition, position } = calcNextPosition(
node,
snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition,
emits.error,
nodeExtent.value,
parentNode.value,
)

// only overwrite positions if there are changes when clamping
if (node.computedPosition.x !== computedPosition.x || node.computedPosition.y !== computedPosition.y) {
Expand Down Expand Up @@ -372,7 +374,7 @@ const NodeWrapper = defineComponent({
return emit.doubleClick({ event, node })
}

function onSelectNode(event: MouseEvent) {
function onSelectNode(event: MouseTouchEvent) {
if (isSelectable.value && (!selectNodesOnDrag.value || !isDraggable.value || nodeDragThreshold.value > 0)) {
handleNodeClick(
node,
Expand Down
27 changes: 13 additions & 14 deletions packages/core/src/composables/useDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { drag } from 'd3-drag'
import { select } from 'd3-selection'
import type { MaybeRefOrGetter, Ref } from 'vue'
import { ref, toValue, watch } from 'vue'
import type { NodeDragEvent, NodeDragItem, XYPosition } from '../types'
import type { MouseTouchEvent, NodeDragEvent, NodeDragItem, XYPosition } from '../types'
import {
calcAutoPan,
calcNextPosition,
Expand All @@ -12,6 +12,8 @@ import {
getEventPosition,
handleNodeClick,
hasSelector,
isUseDragEvent,
snapPosition,
} from '../utils'
import { useGetPointerPosition, useVueFlow } from '.'

Expand All @@ -21,7 +23,7 @@ interface UseDragParams {
onStart: (event: NodeDragEvent) => void
onDrag: (event: NodeDragEvent) => void
onStop: (event: NodeDragEvent) => void
onClick?: (event: MouseEvent) => void
onClick?: (event: MouseTouchEvent) => void
el: Ref<Element | null>
disabled?: MaybeRefOrGetter<boolean>
selectable?: MaybeRefOrGetter<boolean>
Expand Down Expand Up @@ -87,14 +89,9 @@ export function useDrag(params: UseDragParams) {
dragItems = dragItems.map((n) => {
const nextPosition = { x: x - n.distance.x, y: y - n.distance.y }

if (snapToGrid.value) {
nextPosition.x = snapGrid.value[0] * Math.round(nextPosition.x / snapGrid.value[0])
nextPosition.y = snapGrid.value[1] * Math.round(nextPosition.y / snapGrid.value[1])
}

const { computedPosition } = calcNextPosition(
n,
nextPosition,
snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition,
emits.error,
nodeExtent.value,
n.parentNode ? findNode(n.parentNode) : undefined,
Expand Down Expand Up @@ -171,7 +168,7 @@ export function useDrag(params: UseDragParams) {
)
}

const pointerPos = getPointerPosition(event)
const pointerPos = getPointerPosition(event.sourceEvent)
lastPos = pointerPos
dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id)

Expand All @@ -195,14 +192,14 @@ export function useDrag(params: UseDragParams) {
startDrag(event, nodeEl)
}

lastPos = getPointerPosition(event)
lastPos = getPointerPosition(event.sourceEvent)

containerBounds = vueFlowRef.value?.getBoundingClientRect() || null
mousePosition = getEventPosition(event.sourceEvent, containerBounds!)
}

const eventDrag = (event: UseDragEvent, nodeEl: Element) => {
const pointerPos = getPointerPosition(event)
const pointerPos = getPointerPosition(event.sourceEvent)

if (!autoPanStarted && dragStarted && autoPanOnNodeDrag.value) {
autoPanStarted = true
Expand All @@ -229,16 +226,18 @@ export function useDrag(params: UseDragParams) {
}

const eventEnd = (event: UseDragEvent) => {
if (!dragStarted && !dragging.value && !multiSelectionActive.value) {
const pointerPos = getPointerPosition(event)
if (!isUseDragEvent(event) && !dragStarted && !dragging.value && !multiSelectionActive.value) {
const evt = event as MouseTouchEvent

const pointerPos = getPointerPosition(evt)

const x = pointerPos.xSnapped - (lastPos.x ?? 0)
const y = pointerPos.ySnapped - (lastPos.y ?? 0)
const distance = Math.sqrt(x * x + y * y)

// dispatch a click event if the node was attempted to be dragged but the threshold was not exceeded
if (distance !== 0 && distance <= nodeDragThreshold.value) {
onClick?.(event.sourceEvent)
onClick?.(evt)
}

return
Expand Down
20 changes: 10 additions & 10 deletions packages/core/src/composables/useGetPointerPosition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { UseDragEvent } from './useDrag'
import { getEventPosition, isUseDragEvent, pointToRendererPoint, snapPosition } from '../utils'
import type { MouseTouchEvent } from '../types'
import { useVueFlow } from './useVueFlow'
import type { UseDragEvent } from './useDrag'

/**
* Composable that returns a function to get the pointer position
Expand All @@ -10,19 +12,17 @@ export function useGetPointerPosition() {
const { viewport, snapGrid, snapToGrid } = useVueFlow()

// returns the pointer position projected to the VF coordinate system
return ({ sourceEvent }: UseDragEvent) => {
const x = sourceEvent.touches ? sourceEvent.touches[0].clientX : sourceEvent.clientX
const y = sourceEvent.touches ? sourceEvent.touches[0].clientY : sourceEvent.clientY
return (event: UseDragEvent | MouseTouchEvent) => {
const evt = isUseDragEvent(event) ? event.sourceEvent : event

const pointerPos = {
x: (x - viewport.value.x) / viewport.value.zoom,
y: (y - viewport.value.y) / viewport.value.zoom,
}
const { x, y } = getEventPosition(evt)
const pointerPos = pointToRendererPoint({ x, y }, viewport.value)
const { x: xSnapped, y: ySnapped } = snapToGrid.value ? snapPosition(pointerPos, snapGrid.value) : pointerPos

// we need the snapped position in order to be able to skip unnecessary drag events
return {
xSnapped: snapToGrid.value ? snapGrid.value[0] * Math.round(pointerPos.x / snapGrid.value[0]) : pointerPos.x,
ySnapped: snapToGrid.value ? snapGrid.value[1] * Math.round(pointerPos.y / snapGrid.value[1]) : pointerPos.y,
xSnapped,
ySnapped,
...pointerPos,
}
}
Expand Down
21 changes: 17 additions & 4 deletions packages/core/src/utils/general.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import type { GraphNode } from '../types'
import type { GraphNode, SnapGrid, XYPosition } from '../types'
import type { UseDragEvent } from '../composables'

export function isMouseEvent(event: MouseEvent | TouchEvent): event is MouseEvent {
return 'clientX' in event
}

export function isUseDragEvent(event: any): event is UseDragEvent {
return 'sourceEvent' in event
}

export function getEventPosition(event: MouseEvent | TouchEvent, bounds?: DOMRect) {
const isMouseTriggered = isMouseEvent(event)
const evtX = isMouseTriggered ? event.clientX : event.touches?.[0].clientX
const evtY = isMouseTriggered ? event.clientY : event.touches?.[0].clientY
const isMouse = isMouseEvent(event)

const evtX = isMouse ? event.clientX : event.touches?.[0].clientX
const evtY = isMouse ? event.clientY : event.touches?.[0].clientY

return {
x: evtX - (bounds?.left ?? 0),
Expand All @@ -23,3 +29,10 @@ export function getNodeDimensions(node: GraphNode): { width: number; height: num
height: node.dimensions?.height ?? node.height ?? 0,
}
}

export function snapPosition(position: XYPosition, snapGrid: SnapGrid = [1, 1]): XYPosition {
return {
x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
y: snapGrid[1] * Math.round(position.y / snapGrid[1]),
}
}
13 changes: 3 additions & 10 deletions packages/core/src/utils/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type {
XYPosition,
XYZPosition,
} from '../types'
import { isDef, warn } from '.'
import { isDef, snapPosition, warn } from '.'

export function nodeToRect(node: GraphNode): Rect {
return {
Expand Down Expand Up @@ -299,21 +299,14 @@ export function pointToRendererPoint(
{ x, y }: XYPosition,
{ x: tx, y: ty, zoom: tScale }: ViewportTransform,
snapToGrid: boolean = false,
[snapX, snapY]: [snapX: number, snapY: number] = [1, 1],
snapGrid: [snapX: number, snapY: number] = [1, 1],
): XYPosition {
const position: XYPosition = {
x: (x - tx) / tScale,
y: (y - ty) / tScale,
}

if (snapToGrid) {
return {
x: snapX * Math.round(position.x / snapX),
y: snapY * Math.round(position.y / snapY),
}
}

return position
return snapToGrid ? snapPosition(position, snapGrid) : position
}

function getBoundsOfBoxes(box1: Box, box2: Box): Box {
Expand Down
Loading