diff --git a/packages/vue-flow/src/composables/useDrag.ts b/packages/vue-flow/src/composables/useDrag.ts index d113c262..750e3824 100644 --- a/packages/vue-flow/src/composables/useDrag.ts +++ b/packages/vue-flow/src/composables/useDrag.ts @@ -6,7 +6,7 @@ import type { MaybeRef } from '@vueuse/core' import useVueFlow from './useVueFlow' import { handleNodeClick, pointToRendererPoint } from '~/utils' import type { NodeDragEvent, NodeDragItem, XYPosition } from '~/types' -import { getDragItems, getEventHandlerParams, getParentNodePosition, hasSelector, updatePosition } from '~/utils/drag' +import { getDragItems, getEventHandlerParams, hasSelector, updatePosition } from '~/utils/drag' export type UseDragEvent = D3DragEvent @@ -41,24 +41,21 @@ function useDrag(params: UseDragParams) { const dragging = ref(false) let dragItems = $ref() let lastPos = $ref>({ x: undefined, y: undefined }) - let parentPos = $ref({ x: 0, y: 0 }) let dragHandler = $ref() const getMousePosition = (event: UseDragEvent) => { - const mousePos = pointToRendererPoint( + const x = event.sourceEvent.touches ? event.sourceEvent.touches[0].clientX : event.sourceEvent.clientX + const y = event.sourceEvent.touches ? event.sourceEvent.touches[0].clientY : event.sourceEvent.clientY + + return pointToRendererPoint( { - x: event.sourceEvent.clientX, - y: event.sourceEvent.clientY, + x, + y, }, viewport, snapToGrid, snapGrid, ) - - mousePos.x -= parentPos.x - mousePos.y -= parentPos.y - - return mousePos } watch( @@ -73,8 +70,6 @@ function useDrag(params: UseDragParams) { } else { dragHandler = drag() .on('start', (event: UseDragEvent) => { - parentPos = getParentNodePosition(node && node.parentNode ? getNode(node!.parentNode!) : undefined) - if (!selectNodesOnDrag && !multiSelectionActive && id) { if (!node?.selected) { removeSelectedElements() @@ -92,14 +87,12 @@ function useDrag(params: UseDragParams) { const [currentNode, nodes] = getEventHandlerParams({ id, dragItems, - node: node!, + getNode: $$(getNode), }) onStart(event.sourceEvent, currentNode, nodes) } }) .on('drag', (event: UseDragEvent) => { - parentPos = getParentNodePosition(node && node.parentNode ? getNode(node!.parentNode!) : undefined) - const mousePos = getMousePosition(event) // skip events without movement @@ -109,14 +102,14 @@ function useDrag(params: UseDragParams) { updatePosition(n, mousePos, n.parentNode ? getNode(n.parentNode) : undefined, nodeExtent), ) - updateNodePositions(dragItems) + updateNodePositions(dragItems, true, true) dragging.value = true if (onDrag) { const [currentNode, nodes] = getEventHandlerParams({ id, dragItems, - node: node!, + getNode: $$(getNode), }) onDrag(event.sourceEvent, currentNode, nodes) } @@ -125,10 +118,12 @@ function useDrag(params: UseDragParams) { event.on('end', (event) => { dragging.value = false if (onStop && dragItems) { + updateNodePositions(dragItems, false, false) + const [currentNode, nodes] = getEventHandlerParams({ id, dragItems, - node: node!, + getNode: $$(getNode), }) onStop(event.sourceEvent, currentNode, nodes) } diff --git a/packages/vue-flow/src/store/actions.ts b/packages/vue-flow/src/store/actions.ts index 4fb4c7aa..3d9164ff 100644 --- a/packages/vue-flow/src/store/actions.ts +++ b/packages/vue-flow/src/store/actions.ts @@ -30,16 +30,31 @@ import { } from '~/utils' export default (state: State, getters: ComputedGetters): Actions => { - const updateNodePositions: Actions['updateNodePositions'] = (dragItems) => { + const updateNodePositions: Actions['updateNodePositions'] = (dragItems, changed, dragging) => { const changes: NodePositionChange[] = [] dragItems.forEach((node) => { - const change: NodePositionChange = { + const change: Partial = { id: node.id, type: 'position', - position: node.position, + dragging, } - changes.push(change) + + if (changed) { + change.computedPosition = node.position + change.position = node.position + + if (node.parentNode) { + const parentNode = getters.getNode.value(node.parentNode) + + change.position = { + x: change.position.x - (parentNode?.computedPosition?.x ?? 0), + y: change.position.y - (parentNode?.computedPosition?.y ?? 0), + } + } + } + + changes.push(change as NodePositionChange) }) if (changes?.length) { diff --git a/packages/vue-flow/src/types/changes.ts b/packages/vue-flow/src/types/changes.ts index 9e4be6d8..5fd59d4e 100644 --- a/packages/vue-flow/src/types/changes.ts +++ b/packages/vue-flow/src/types/changes.ts @@ -8,8 +8,6 @@ export interface NodeDragItem { position: XYPosition // distance from the mouse cursor to the node when start dragging distance: XYPosition - // delta to previous position - delta: XYPosition dimensions: Dimensions extent?: Node['extent'] parentNode?: string @@ -25,7 +23,9 @@ export interface NodeDimensionChange { export interface NodePositionChange { id: string type: 'position' - position?: XYPosition + position: XYPosition + computedPosition: XYPosition + dragging: boolean } export interface NodeSelectionChange { diff --git a/packages/vue-flow/src/types/store.ts b/packages/vue-flow/src/types/store.ts index 2f9d0c60..c72b2a23 100644 --- a/packages/vue-flow/src/types/store.ts +++ b/packages/vue-flow/src/types/store.ts @@ -113,7 +113,7 @@ export type SetState = ( | Partial> | ((state: State) => Partial>), ) => void -export type UpdateNodePosition = (dragItems: NodeDragItem[]) => void +export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void export interface Actions { diff --git a/packages/vue-flow/src/utils/changes.ts b/packages/vue-flow/src/utils/changes.ts index 639a94e0..c8c6ddca 100644 --- a/packages/vue-flow/src/utils/changes.ts +++ b/packages/vue-flow/src/utils/changes.ts @@ -97,6 +97,11 @@ export const applyChanges = < case 'position': if (isGraphNode(el)) { if (typeof change.position !== 'undefined') el.position = change.position + + if (typeof change.computedPosition !== 'undefined') { + el.computedPosition = { ...el.computedPosition, ...change.computedPosition } + } + if (el.expandParent && el.parentNode) { const parent = elements.find((parent) => parent.id === el.parentNode) diff --git a/packages/vue-flow/src/utils/drag.ts b/packages/vue-flow/src/utils/drag.ts index ff284767..c6b732a4 100644 --- a/packages/vue-flow/src/utils/drag.ts +++ b/packages/vue-flow/src/utils/drag.ts @@ -1,13 +1,6 @@ import type { Ref } from 'vue' import { clampPosition, isParentSelected } from './graph' -import type { CoordinateExtent, Getters, GraphNode, NodeDragItem, XYPosition } from '~/types' - -export function getParentNodePosition(parent?: GraphNode): XYPosition { - return { - x: parent?.computedPosition?.x || 0, - y: parent?.computedPosition?.y || 0, - } -} +import type { ComputedGetters, CoordinateExtent, Getters, GraphNode, NodeDragItem, XYPosition } from '~/types' export function hasSelector(target: Element, selector: string, node: Ref): boolean { let current = target @@ -32,14 +25,10 @@ export function getDragItems( .filter((n) => (n.selected || n.id === nodeId) && (!n.parentNode || !isParentSelected(n, getNode))) .map((n) => ({ id: n.id, - position: n.position, + position: n.computedPosition || { x: 0, y: 0, z: 0 }, distance: { - x: mousePos.x - n.position.x, - y: mousePos.y - n.position.y, - }, - delta: { - x: 0, - y: 0, + x: mousePos.x - n.computedPosition?.x || 0, + y: mousePos.y - n.computedPosition?.y || 0, }, extent: n.extent, parentNode: n.parentNode, @@ -50,21 +39,17 @@ export function getDragItems( export function getEventHandlerParams({ id, dragItems, - node, + getNode, }: { id?: string dragItems: NodeDragItem[] - node: GraphNode + getNode: ComputedGetters['getNode'] }): [GraphNode, GraphNode[]] { const extendedDragItems: GraphNode[] = dragItems.map((n) => { + const node = getNode.value(n.id)! + return { ...node, - position: n.position, - computedPosition: { - x: (node.computedPosition?.x || 0) + n.delta.x, - y: (node.computedPosition?.y || 0) + n.delta.y, - z: node.computedPosition.z, - }, } }) @@ -78,15 +63,18 @@ export function updatePosition( nodeExtent?: CoordinateExtent, ): NodeDragItem { let currentExtent = dragItem.extent || nodeExtent - let nextPosition = { x: mousePos.x - dragItem.distance.x, y: mousePos.y - dragItem.distance.y } + const nextPosition = { x: mousePos.x - dragItem.distance.x, y: mousePos.y - dragItem.distance.y } if (dragItem.extent === 'parent' && parent) { if (dragItem.parentNode && dragItem.dimensions.width && dragItem.dimensions.height) { currentExtent = - parent.dimensions.width && parent.dimensions.height + parent.computedPosition && parent.dimensions.width && parent.dimensions.height ? [ - [0, 0], - [parent.dimensions.width - dragItem.dimensions.width, parent.dimensions.height - dragItem.dimensions.height], + [parent.computedPosition.x, parent.computedPosition.y], + [ + parent.computedPosition.x + parent.dimensions.width - dragItem.dimensions.width, + parent.computedPosition.y + parent.dimensions.height - dragItem.dimensions.height, + ], ] : currentExtent } @@ -94,13 +82,7 @@ export function updatePosition( currentExtent = nodeExtent } - nextPosition = currentExtent ? clampPosition(nextPosition, currentExtent as CoordinateExtent) : nextPosition - - dragItem.delta = { - x: nextPosition.x - dragItem.position.x, - y: nextPosition.y - dragItem.position.y, - } - dragItem.position = nextPosition + dragItem.position = currentExtent ? clampPosition(nextPosition, currentExtent as CoordinateExtent) : nextPosition return dragItem }