fix(vue-flow,nodes): use computed pos to calculate relative node pos

This commit is contained in:
bcakmakoglu
2022-06-01 19:10:30 +02:00
committed by Braks
parent 30efd62082
commit 29439f005b
6 changed files with 57 additions and 60 deletions
+13 -18
View File
@@ -6,7 +6,7 @@ import type { MaybeRef } from '@vueuse/core'
import useVueFlow from './useVueFlow' import useVueFlow from './useVueFlow'
import { handleNodeClick, pointToRendererPoint } from '~/utils' import { handleNodeClick, pointToRendererPoint } from '~/utils'
import type { NodeDragEvent, NodeDragItem, XYPosition } from '~/types' 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<HTMLDivElement, null, SubjectPosition> export type UseDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>
@@ -41,24 +41,21 @@ function useDrag(params: UseDragParams) {
const dragging = ref(false) const dragging = ref(false)
let dragItems = $ref<NodeDragItem[]>() let dragItems = $ref<NodeDragItem[]>()
let lastPos = $ref<Partial<XYPosition>>({ x: undefined, y: undefined }) let lastPos = $ref<Partial<XYPosition>>({ x: undefined, y: undefined })
let parentPos = $ref<XYPosition>({ x: 0, y: 0 })
let dragHandler = $ref<any>() let dragHandler = $ref<any>()
const getMousePosition = (event: UseDragEvent) => { 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, x,
y: event.sourceEvent.clientY, y,
}, },
viewport, viewport,
snapToGrid, snapToGrid,
snapGrid, snapGrid,
) )
mousePos.x -= parentPos.x
mousePos.y -= parentPos.y
return mousePos
} }
watch( watch(
@@ -73,8 +70,6 @@ function useDrag(params: UseDragParams) {
} else { } else {
dragHandler = drag() dragHandler = drag()
.on('start', (event: UseDragEvent) => { .on('start', (event: UseDragEvent) => {
parentPos = getParentNodePosition(node && node.parentNode ? getNode(node!.parentNode!) : undefined)
if (!selectNodesOnDrag && !multiSelectionActive && id) { if (!selectNodesOnDrag && !multiSelectionActive && id) {
if (!node?.selected) { if (!node?.selected) {
removeSelectedElements() removeSelectedElements()
@@ -92,14 +87,12 @@ function useDrag(params: UseDragParams) {
const [currentNode, nodes] = getEventHandlerParams({ const [currentNode, nodes] = getEventHandlerParams({
id, id,
dragItems, dragItems,
node: node!, getNode: $$(getNode),
}) })
onStart(event.sourceEvent, currentNode, nodes) onStart(event.sourceEvent, currentNode, nodes)
} }
}) })
.on('drag', (event: UseDragEvent) => { .on('drag', (event: UseDragEvent) => {
parentPos = getParentNodePosition(node && node.parentNode ? getNode(node!.parentNode!) : undefined)
const mousePos = getMousePosition(event) const mousePos = getMousePosition(event)
// skip events without movement // skip events without movement
@@ -109,14 +102,14 @@ function useDrag(params: UseDragParams) {
updatePosition(n, mousePos, n.parentNode ? getNode(n.parentNode) : undefined, nodeExtent), updatePosition(n, mousePos, n.parentNode ? getNode(n.parentNode) : undefined, nodeExtent),
) )
updateNodePositions(dragItems) updateNodePositions(dragItems, true, true)
dragging.value = true dragging.value = true
if (onDrag) { if (onDrag) {
const [currentNode, nodes] = getEventHandlerParams({ const [currentNode, nodes] = getEventHandlerParams({
id, id,
dragItems, dragItems,
node: node!, getNode: $$(getNode),
}) })
onDrag(event.sourceEvent, currentNode, nodes) onDrag(event.sourceEvent, currentNode, nodes)
} }
@@ -125,10 +118,12 @@ function useDrag(params: UseDragParams) {
event.on('end', (event) => { event.on('end', (event) => {
dragging.value = false dragging.value = false
if (onStop && dragItems) { if (onStop && dragItems) {
updateNodePositions(dragItems, false, false)
const [currentNode, nodes] = getEventHandlerParams({ const [currentNode, nodes] = getEventHandlerParams({
id, id,
dragItems, dragItems,
node: node!, getNode: $$(getNode),
}) })
onStop(event.sourceEvent, currentNode, nodes) onStop(event.sourceEvent, currentNode, nodes)
} }
+19 -4
View File
@@ -30,16 +30,31 @@ import {
} from '~/utils' } from '~/utils'
export default (state: State, getters: ComputedGetters): Actions => { export default (state: State, getters: ComputedGetters): Actions => {
const updateNodePositions: Actions['updateNodePositions'] = (dragItems) => { const updateNodePositions: Actions['updateNodePositions'] = (dragItems, changed, dragging) => {
const changes: NodePositionChange[] = [] const changes: NodePositionChange[] = []
dragItems.forEach((node) => { dragItems.forEach((node) => {
const change: NodePositionChange = { const change: Partial<NodePositionChange> = {
id: node.id, id: node.id,
type: 'position', 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) { if (changes?.length) {
+3 -3
View File
@@ -8,8 +8,6 @@ export interface NodeDragItem {
position: XYPosition position: XYPosition
// distance from the mouse cursor to the node when start dragging // distance from the mouse cursor to the node when start dragging
distance: XYPosition distance: XYPosition
// delta to previous position
delta: XYPosition
dimensions: Dimensions dimensions: Dimensions
extent?: Node['extent'] extent?: Node['extent']
parentNode?: string parentNode?: string
@@ -25,7 +23,9 @@ export interface NodeDimensionChange {
export interface NodePositionChange { export interface NodePositionChange {
id: string id: string
type: 'position' type: 'position'
position?: XYPosition position: XYPosition
computedPosition: XYPosition
dragging: boolean
} }
export interface NodeSelectionChange { export interface NodeSelectionChange {
+1 -1
View File
@@ -113,7 +113,7 @@ export type SetState = (
| Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>> | Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>
| ((state: State) => Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>), | ((state: State) => Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>),
) => void ) => void
export type UpdateNodePosition = (dragItems: NodeDragItem[]) => void export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void
export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void
export interface Actions { export interface Actions {
+5
View File
@@ -97,6 +97,11 @@ export const applyChanges = <
case 'position': case 'position':
if (isGraphNode(el)) { if (isGraphNode(el)) {
if (typeof change.position !== 'undefined') el.position = change.position 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) { if (el.expandParent && el.parentNode) {
const parent = elements.find((parent) => parent.id === el.parentNode) const parent = elements.find((parent) => parent.id === el.parentNode)
+16 -34
View File
@@ -1,13 +1,6 @@
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { clampPosition, isParentSelected } from './graph' import { clampPosition, isParentSelected } from './graph'
import type { CoordinateExtent, Getters, GraphNode, NodeDragItem, XYPosition } from '~/types' import type { ComputedGetters, CoordinateExtent, Getters, GraphNode, NodeDragItem, XYPosition } from '~/types'
export function getParentNodePosition(parent?: GraphNode): XYPosition {
return {
x: parent?.computedPosition?.x || 0,
y: parent?.computedPosition?.y || 0,
}
}
export function hasSelector(target: Element, selector: string, node: Ref<Element>): boolean { export function hasSelector(target: Element, selector: string, node: Ref<Element>): boolean {
let current = target let current = target
@@ -32,14 +25,10 @@ export function getDragItems(
.filter((n) => (n.selected || n.id === nodeId) && (!n.parentNode || !isParentSelected(n, getNode))) .filter((n) => (n.selected || n.id === nodeId) && (!n.parentNode || !isParentSelected(n, getNode)))
.map((n) => ({ .map((n) => ({
id: n.id, id: n.id,
position: n.position, position: n.computedPosition || { x: 0, y: 0, z: 0 },
distance: { distance: {
x: mousePos.x - n.position.x, x: mousePos.x - n.computedPosition?.x || 0,
y: mousePos.y - n.position.y, y: mousePos.y - n.computedPosition?.y || 0,
},
delta: {
x: 0,
y: 0,
}, },
extent: n.extent, extent: n.extent,
parentNode: n.parentNode, parentNode: n.parentNode,
@@ -50,21 +39,17 @@ export function getDragItems(
export function getEventHandlerParams({ export function getEventHandlerParams({
id, id,
dragItems, dragItems,
node, getNode,
}: { }: {
id?: string id?: string
dragItems: NodeDragItem[] dragItems: NodeDragItem[]
node: GraphNode getNode: ComputedGetters['getNode']
}): [GraphNode, GraphNode[]] { }): [GraphNode, GraphNode[]] {
const extendedDragItems: GraphNode[] = dragItems.map((n) => { const extendedDragItems: GraphNode[] = dragItems.map((n) => {
const node = getNode.value(n.id)!
return { return {
...node, ...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, nodeExtent?: CoordinateExtent,
): NodeDragItem { ): NodeDragItem {
let currentExtent = dragItem.extent || nodeExtent 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.extent === 'parent' && parent) {
if (dragItem.parentNode && dragItem.dimensions.width && dragItem.dimensions.height) { if (dragItem.parentNode && dragItem.dimensions.width && dragItem.dimensions.height) {
currentExtent = currentExtent =
parent.dimensions.width && parent.dimensions.height parent.computedPosition && parent.dimensions.width && parent.dimensions.height
? [ ? [
[0, 0], [parent.computedPosition.x, parent.computedPosition.y],
[parent.dimensions.width - dragItem.dimensions.width, parent.dimensions.height - dragItem.dimensions.height], [
parent.computedPosition.x + parent.dimensions.width - dragItem.dimensions.width,
parent.computedPosition.y + parent.dimensions.height - dragItem.dimensions.height,
],
] ]
: currentExtent : currentExtent
} }
@@ -94,13 +82,7 @@ export function updatePosition(
currentExtent = nodeExtent currentExtent = nodeExtent
} }
nextPosition = currentExtent ? clampPosition(nextPosition, currentExtent as CoordinateExtent) : nextPosition dragItem.position = currentExtent ? clampPosition(nextPosition, currentExtent as CoordinateExtent) : nextPosition
dragItem.delta = {
x: nextPosition.x - dragItem.position.x,
y: nextPosition.y - dragItem.position.y,
}
dragItem.position = nextPosition
return dragItem return dragItem
} }