Skip to content

Commit 5a028cf

Browse files
committed
fix(core): check if dragEnd event is UseDrag or MouseTouch event (#1680)
* feat(core): add snapPosition util Signed-off-by: braks <[email protected]> * fix(core): check if dragEnd event is actually a UseDrag or MouseTouch event Signed-off-by: braks <[email protected]> * chore(core): cleanup Signed-off-by: braks <[email protected]> * chore(changeset): add Signed-off-by: braks <[email protected]> --------- Signed-off-by: braks <[email protected]>
1 parent 61f4b0d commit 5a028cf

File tree

6 files changed

+60
-48
lines changed

6 files changed

+60
-48
lines changed

.changeset/early-walls-attack.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@vue-flow/core": patch
3+
---
4+
5+
check if event on drag end is mouse/touch event or a usedrag event

packages/core/src/components/Nodes/NodeWrapper.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ import {
2222
elementSelectionKeys,
2323
getXYZPos,
2424
handleNodeClick,
25+
snapPosition,
2526
} from '../../utils'
2627
import { NodeId, NodeRef, Slots } from '../../context'
2728
import { isInputDOMNode, useDrag, useNode, useNodeHooks, useUpdateNodePositions, useVueFlow } from '../../composables'
28-
import type { NodeComponent } from '../../types'
29+
import type { MouseTouchEvent, NodeComponent } from '../../types'
2930

3031
interface Props {
3132
id: string
@@ -321,14 +322,15 @@ const NodeWrapper = defineComponent({
321322
}
322323
/** this re-calculates the current position, necessary for clamping by a node's extent */
323324
function clampPosition() {
324-
const nextPos = node.computedPosition
325-
326-
if (snapToGrid.value) {
327-
nextPos.x = snapGrid.value[0] * Math.round(nextPos.x / snapGrid.value[0])
328-
nextPos.y = snapGrid.value[1] * Math.round(nextPos.y / snapGrid.value[1])
329-
}
330-
331-
const { computedPosition, position } = calcNextPosition(node, nextPos, emits.error, nodeExtent.value, parentNode.value)
325+
const nextPosition = node.computedPosition
326+
327+
const { computedPosition, position } = calcNextPosition(
328+
node,
329+
snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition,
330+
emits.error,
331+
nodeExtent.value,
332+
parentNode.value,
333+
)
332334

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

375-
function onSelectNode(event: MouseEvent) {
377+
function onSelectNode(event: MouseTouchEvent) {
376378
if (isSelectable.value && (!selectNodesOnDrag.value || !isDraggable.value || nodeDragThreshold.value > 0)) {
377379
handleNodeClick(
378380
node,

packages/core/src/composables/useDrag.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { drag } from 'd3-drag'
33
import { select } from 'd3-selection'
44
import type { MaybeRefOrGetter, Ref } from 'vue'
55
import { ref, toValue, watch } from 'vue'
6-
import type { NodeDragEvent, NodeDragItem, XYPosition } from '../types'
6+
import type { MouseTouchEvent, NodeDragEvent, NodeDragItem, XYPosition } from '../types'
77
import {
88
calcAutoPan,
99
calcNextPosition,
@@ -12,6 +12,8 @@ import {
1212
getEventPosition,
1313
handleNodeClick,
1414
hasSelector,
15+
isUseDragEvent,
16+
snapPosition,
1517
} from '../utils'
1618
import { useGetPointerPosition, useVueFlow } from '.'
1719

@@ -21,7 +23,7 @@ interface UseDragParams {
2123
onStart: (event: NodeDragEvent) => void
2224
onDrag: (event: NodeDragEvent) => void
2325
onStop: (event: NodeDragEvent) => void
24-
onClick?: (event: MouseEvent) => void
26+
onClick?: (event: MouseTouchEvent) => void
2527
el: Ref<Element | null>
2628
disabled?: MaybeRefOrGetter<boolean>
2729
selectable?: MaybeRefOrGetter<boolean>
@@ -87,14 +89,9 @@ export function useDrag(params: UseDragParams) {
8789
dragItems = dragItems.map((n) => {
8890
const nextPosition = { x: x - n.distance.x, y: y - n.distance.y }
8991

90-
if (snapToGrid.value) {
91-
nextPosition.x = snapGrid.value[0] * Math.round(nextPosition.x / snapGrid.value[0])
92-
nextPosition.y = snapGrid.value[1] * Math.round(nextPosition.y / snapGrid.value[1])
93-
}
94-
9592
const { computedPosition } = calcNextPosition(
9693
n,
97-
nextPosition,
94+
snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition,
9895
emits.error,
9996
nodeExtent.value,
10097
n.parentNode ? findNode(n.parentNode) : undefined,
@@ -171,7 +168,7 @@ export function useDrag(params: UseDragParams) {
171168
)
172169
}
173170

174-
const pointerPos = getPointerPosition(event)
171+
const pointerPos = getPointerPosition(event.sourceEvent)
175172
lastPos = pointerPos
176173
dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id)
177174

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

198-
lastPos = getPointerPosition(event)
195+
lastPos = getPointerPosition(event.sourceEvent)
199196

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

204201
const eventDrag = (event: UseDragEvent, nodeEl: Element) => {
205-
const pointerPos = getPointerPosition(event)
202+
const pointerPos = getPointerPosition(event.sourceEvent)
206203

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

231228
const eventEnd = (event: UseDragEvent) => {
232-
if (!dragStarted && !dragging.value && !multiSelectionActive.value) {
233-
const pointerPos = getPointerPosition(event)
229+
if (!isUseDragEvent(event) && !dragStarted && !dragging.value && !multiSelectionActive.value) {
230+
const evt = event as MouseTouchEvent
231+
232+
const pointerPos = getPointerPosition(evt)
234233

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

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

244243
return

packages/core/src/composables/useGetPointerPosition.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import type { UseDragEvent } from './useDrag'
1+
import { getEventPosition, isUseDragEvent, pointToRendererPoint, snapPosition } from '../utils'
2+
import type { MouseTouchEvent } from '../types'
23
import { useVueFlow } from './useVueFlow'
4+
import type { UseDragEvent } from './useDrag'
35

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

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

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

2222
// we need the snapped position in order to be able to skip unnecessary drag events
2323
return {
24-
xSnapped: snapToGrid.value ? snapGrid.value[0] * Math.round(pointerPos.x / snapGrid.value[0]) : pointerPos.x,
25-
ySnapped: snapToGrid.value ? snapGrid.value[1] * Math.round(pointerPos.y / snapGrid.value[1]) : pointerPos.y,
24+
xSnapped,
25+
ySnapped,
2626
...pointerPos,
2727
}
2828
}

packages/core/src/utils/general.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1-
import type { GraphNode } from '../types'
1+
import type { GraphNode, SnapGrid, XYPosition } from '../types'
2+
import type { UseDragEvent } from '../composables'
23

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

8+
export function isUseDragEvent(event: any): event is UseDragEvent {
9+
return 'sourceEvent' in event
10+
}
11+
712
export function getEventPosition(event: MouseEvent | TouchEvent, bounds?: DOMRect) {
8-
const isMouseTriggered = isMouseEvent(event)
9-
const evtX = isMouseTriggered ? event.clientX : event.touches?.[0].clientX
10-
const evtY = isMouseTriggered ? event.clientY : event.touches?.[0].clientY
13+
const isMouse = isMouseEvent(event)
14+
15+
const evtX = isMouse ? event.clientX : event.touches?.[0].clientX
16+
const evtY = isMouse ? event.clientY : event.touches?.[0].clientY
1117

1218
return {
1319
x: evtX - (bounds?.left ?? 0),
@@ -23,3 +29,10 @@ export function getNodeDimensions(node: GraphNode): { width: number; height: num
2329
height: node.dimensions?.height ?? node.height ?? 0,
2430
}
2531
}
32+
33+
export function snapPosition(position: XYPosition, snapGrid: SnapGrid = [1, 1]): XYPosition {
34+
return {
35+
x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
36+
y: snapGrid[1] * Math.round(position.y / snapGrid[1]),
37+
}
38+
}

packages/core/src/utils/graph.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type {
2121
XYPosition,
2222
XYZPosition,
2323
} from '../types'
24-
import { isDef, warn } from '.'
24+
import { isDef, snapPosition, warn } from '.'
2525

2626
export function nodeToRect(node: GraphNode): Rect {
2727
return {
@@ -299,21 +299,14 @@ export function pointToRendererPoint(
299299
{ x, y }: XYPosition,
300300
{ x: tx, y: ty, zoom: tScale }: ViewportTransform,
301301
snapToGrid: boolean = false,
302-
[snapX, snapY]: [snapX: number, snapY: number] = [1, 1],
302+
snapGrid: [snapX: number, snapY: number] = [1, 1],
303303
): XYPosition {
304304
const position: XYPosition = {
305305
x: (x - tx) / tScale,
306306
y: (y - ty) / tScale,
307307
}
308308

309-
if (snapToGrid) {
310-
return {
311-
x: snapX * Math.round(position.x / snapX),
312-
y: snapY * Math.round(position.y / snapY),
313-
}
314-
}
315-
316-
return position
309+
return snapToGrid ? snapPosition(position, snapGrid) : position
317310
}
318311

319312
function getBoundsOfBoxes(box1: Box, box2: Box): Box {

0 commit comments

Comments
 (0)