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

* feat(core): add snapPosition util

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>

* fix(core): check if dragEnd event is actually a UseDrag or MouseTouch event

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>

* chore(core): cleanup

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>

* chore(changeset): add

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>

---------

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
Braks
2024-11-13 20:20:43 +01:00
parent 61f4b0d447
commit 5a028cf2d4
6 changed files with 59 additions and 47 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@vue-flow/core": patch
---
check if event on drag end is mouse/touch event or a usedrag event
@@ -22,10 +22,11 @@ import {
elementSelectionKeys, elementSelectionKeys,
getXYZPos, getXYZPos,
handleNodeClick, handleNodeClick,
snapPosition,
} from '../../utils' } from '../../utils'
import { NodeId, NodeRef, Slots } from '../../context' import { NodeId, NodeRef, Slots } from '../../context'
import { isInputDOMNode, useDrag, useNode, useNodeHooks, useUpdateNodePositions, useVueFlow } from '../../composables' import { isInputDOMNode, useDrag, useNode, useNodeHooks, useUpdateNodePositions, useVueFlow } from '../../composables'
import type { NodeComponent } from '../../types' import type { MouseTouchEvent, NodeComponent } from '../../types'
interface Props { interface Props {
id: string id: string
@@ -321,14 +322,15 @@ const NodeWrapper = defineComponent({
} }
/** this re-calculates the current position, necessary for clamping by a node's extent */ /** this re-calculates the current position, necessary for clamping by a node's extent */
function clampPosition() { function clampPosition() {
const nextPos = node.computedPosition const nextPosition = node.computedPosition
if (snapToGrid.value) { const { computedPosition, position } = calcNextPosition(
nextPos.x = snapGrid.value[0] * Math.round(nextPos.x / snapGrid.value[0]) node,
nextPos.y = snapGrid.value[1] * Math.round(nextPos.y / snapGrid.value[1]) snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition,
} emits.error,
nodeExtent.value,
const { computedPosition, position } = calcNextPosition(node, nextPos, emits.error, nodeExtent.value, parentNode.value) parentNode.value,
)
// only overwrite positions if there are changes when clamping // only overwrite positions if there are changes when clamping
if (node.computedPosition.x !== computedPosition.x || node.computedPosition.y !== computedPosition.y) { if (node.computedPosition.x !== computedPosition.x || node.computedPosition.y !== computedPosition.y) {
@@ -372,7 +374,7 @@ const NodeWrapper = defineComponent({
return emit.doubleClick({ event, node }) return emit.doubleClick({ event, node })
} }
function onSelectNode(event: MouseEvent) { function onSelectNode(event: MouseTouchEvent) {
if (isSelectable.value && (!selectNodesOnDrag.value || !isDraggable.value || nodeDragThreshold.value > 0)) { if (isSelectable.value && (!selectNodesOnDrag.value || !isDraggable.value || nodeDragThreshold.value > 0)) {
handleNodeClick( handleNodeClick(
node, node,
+13 -14
View File
@@ -3,7 +3,7 @@ import { drag } from 'd3-drag'
import { select } from 'd3-selection' import { select } from 'd3-selection'
import type { MaybeRefOrGetter, Ref } from 'vue' import type { MaybeRefOrGetter, Ref } from 'vue'
import { ref, toValue, watch } from 'vue' import { ref, toValue, watch } from 'vue'
import type { NodeDragEvent, NodeDragItem, XYPosition } from '../types' import type { MouseTouchEvent, NodeDragEvent, NodeDragItem, XYPosition } from '../types'
import { import {
calcAutoPan, calcAutoPan,
calcNextPosition, calcNextPosition,
@@ -12,6 +12,8 @@ import {
getEventPosition, getEventPosition,
handleNodeClick, handleNodeClick,
hasSelector, hasSelector,
isUseDragEvent,
snapPosition,
} from '../utils' } from '../utils'
import { useGetPointerPosition, useVueFlow } from '.' import { useGetPointerPosition, useVueFlow } from '.'
@@ -21,7 +23,7 @@ interface UseDragParams {
onStart: (event: NodeDragEvent) => void onStart: (event: NodeDragEvent) => void
onDrag: (event: NodeDragEvent) => void onDrag: (event: NodeDragEvent) => void
onStop: (event: NodeDragEvent) => void onStop: (event: NodeDragEvent) => void
onClick?: (event: MouseEvent) => void onClick?: (event: MouseTouchEvent) => void
el: Ref<Element | null> el: Ref<Element | null>
disabled?: MaybeRefOrGetter<boolean> disabled?: MaybeRefOrGetter<boolean>
selectable?: MaybeRefOrGetter<boolean> selectable?: MaybeRefOrGetter<boolean>
@@ -87,14 +89,9 @@ export function useDrag(params: UseDragParams) {
dragItems = dragItems.map((n) => { dragItems = dragItems.map((n) => {
const nextPosition = { x: x - n.distance.x, y: y - n.distance.y } 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( const { computedPosition } = calcNextPosition(
n, n,
nextPosition, snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition,
emits.error, emits.error,
nodeExtent.value, nodeExtent.value,
n.parentNode ? findNode(n.parentNode) : undefined, n.parentNode ? findNode(n.parentNode) : undefined,
@@ -171,7 +168,7 @@ export function useDrag(params: UseDragParams) {
) )
} }
const pointerPos = getPointerPosition(event) const pointerPos = getPointerPosition(event.sourceEvent)
lastPos = pointerPos lastPos = pointerPos
dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id) dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id)
@@ -195,14 +192,14 @@ export function useDrag(params: UseDragParams) {
startDrag(event, nodeEl) startDrag(event, nodeEl)
} }
lastPos = getPointerPosition(event) lastPos = getPointerPosition(event.sourceEvent)
containerBounds = vueFlowRef.value?.getBoundingClientRect() || null containerBounds = vueFlowRef.value?.getBoundingClientRect() || null
mousePosition = getEventPosition(event.sourceEvent, containerBounds!) mousePosition = getEventPosition(event.sourceEvent, containerBounds!)
} }
const eventDrag = (event: UseDragEvent, nodeEl: Element) => { const eventDrag = (event: UseDragEvent, nodeEl: Element) => {
const pointerPos = getPointerPosition(event) const pointerPos = getPointerPosition(event.sourceEvent)
if (!autoPanStarted && dragStarted && autoPanOnNodeDrag.value) { if (!autoPanStarted && dragStarted && autoPanOnNodeDrag.value) {
autoPanStarted = true autoPanStarted = true
@@ -229,8 +226,10 @@ export function useDrag(params: UseDragParams) {
} }
const eventEnd = (event: UseDragEvent) => { const eventEnd = (event: UseDragEvent) => {
if (!dragStarted && !dragging.value && !multiSelectionActive.value) { if (!isUseDragEvent(event) && !dragStarted && !dragging.value && !multiSelectionActive.value) {
const pointerPos = getPointerPosition(event) const evt = event as MouseTouchEvent
const pointerPos = getPointerPosition(evt)
const x = pointerPos.xSnapped - (lastPos.x ?? 0) const x = pointerPos.xSnapped - (lastPos.x ?? 0)
const y = pointerPos.ySnapped - (lastPos.y ?? 0) const y = pointerPos.ySnapped - (lastPos.y ?? 0)
@@ -238,7 +237,7 @@ export function useDrag(params: UseDragParams) {
// dispatch a click event if the node was attempted to be dragged but the threshold was not exceeded // dispatch a click event if the node was attempted to be dragged but the threshold was not exceeded
if (distance !== 0 && distance <= nodeDragThreshold.value) { if (distance !== 0 && distance <= nodeDragThreshold.value) {
onClick?.(event.sourceEvent) onClick?.(evt)
} }
return return
@@ -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 { useVueFlow } from './useVueFlow'
import type { UseDragEvent } from './useDrag'
/** /**
* Composable that returns a function to get the pointer position * Composable that returns a function to get the pointer position
@@ -10,19 +12,17 @@ export function useGetPointerPosition() {
const { viewport, snapGrid, snapToGrid } = useVueFlow() const { viewport, snapGrid, snapToGrid } = useVueFlow()
// returns the pointer position projected to the VF coordinate system // returns the pointer position projected to the VF coordinate system
return ({ sourceEvent }: UseDragEvent) => { return (event: UseDragEvent | MouseTouchEvent) => {
const x = sourceEvent.touches ? sourceEvent.touches[0].clientX : sourceEvent.clientX const evt = isUseDragEvent(event) ? event.sourceEvent : event
const y = sourceEvent.touches ? sourceEvent.touches[0].clientY : sourceEvent.clientY
const pointerPos = { const { x, y } = getEventPosition(evt)
x: (x - viewport.value.x) / viewport.value.zoom, const pointerPos = pointToRendererPoint({ x, y }, viewport.value)
y: (y - viewport.value.y) / viewport.value.zoom, 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 // we need the snapped position in order to be able to skip unnecessary drag events
return { return {
xSnapped: snapToGrid.value ? snapGrid.value[0] * Math.round(pointerPos.x / snapGrid.value[0]) : pointerPos.x, xSnapped,
ySnapped: snapToGrid.value ? snapGrid.value[1] * Math.round(pointerPos.y / snapGrid.value[1]) : pointerPos.y, ySnapped,
...pointerPos, ...pointerPos,
} }
} }
+17 -4
View File
@@ -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 { export function isMouseEvent(event: MouseEvent | TouchEvent): event is MouseEvent {
return 'clientX' in event return 'clientX' in event
} }
export function isUseDragEvent(event: any): event is UseDragEvent {
return 'sourceEvent' in event
}
export function getEventPosition(event: MouseEvent | TouchEvent, bounds?: DOMRect) { export function getEventPosition(event: MouseEvent | TouchEvent, bounds?: DOMRect) {
const isMouseTriggered = isMouseEvent(event) const isMouse = isMouseEvent(event)
const evtX = isMouseTriggered ? event.clientX : event.touches?.[0].clientX
const evtY = isMouseTriggered ? event.clientY : event.touches?.[0].clientY const evtX = isMouse ? event.clientX : event.touches?.[0].clientX
const evtY = isMouse ? event.clientY : event.touches?.[0].clientY
return { return {
x: evtX - (bounds?.left ?? 0), x: evtX - (bounds?.left ?? 0),
@@ -23,3 +29,10 @@ export function getNodeDimensions(node: GraphNode): { width: number; height: num
height: node.dimensions?.height ?? node.height ?? 0, 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]),
}
}
+3 -10
View File
@@ -21,7 +21,7 @@ import type {
XYPosition, XYPosition,
XYZPosition, XYZPosition,
} from '../types' } from '../types'
import { isDef, warn } from '.' import { isDef, snapPosition, warn } from '.'
export function nodeToRect(node: GraphNode): Rect { export function nodeToRect(node: GraphNode): Rect {
return { return {
@@ -299,21 +299,14 @@ export function pointToRendererPoint(
{ x, y }: XYPosition, { x, y }: XYPosition,
{ x: tx, y: ty, zoom: tScale }: ViewportTransform, { x: tx, y: ty, zoom: tScale }: ViewportTransform,
snapToGrid: boolean = false, snapToGrid: boolean = false,
[snapX, snapY]: [snapX: number, snapY: number] = [1, 1], snapGrid: [snapX: number, snapY: number] = [1, 1],
): XYPosition { ): XYPosition {
const position: XYPosition = { const position: XYPosition = {
x: (x - tx) / tScale, x: (x - tx) / tScale,
y: (y - ty) / tScale, y: (y - ty) / tScale,
} }
if (snapToGrid) { return snapToGrid ? snapPosition(position, snapGrid) : position
return {
x: snapX * Math.round(position.x / snapX),
y: snapY * Math.round(position.y / snapY),
}
}
return position
} }
function getBoundsOfBoxes(box1: Box, box2: Box): Box { function getBoundsOfBoxes(box1: Box, box2: Box): Box {