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:
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user