feat(core): add nodeDragThreshold option

This commit is contained in:
braks
2023-10-06 18:00:01 +02:00
committed by Braks
parent ed31890e4a
commit e71a2aa4ef
4 changed files with 63 additions and 33 deletions
+60 -33
View File
@@ -38,6 +38,7 @@ export function useDrag(params: UseDragParams) {
noDragClassName, noDragClassName,
nodes, nodes,
nodeExtent, nodeExtent,
nodeDragThreshold,
viewport, viewport,
autoPanOnNodeDrag, autoPanOnNodeDrag,
nodesDraggable, nodesDraggable,
@@ -65,6 +66,7 @@ export function useDrag(params: UseDragParams) {
let lastPos: Partial<XYPosition> = { x: undefined, y: undefined } let lastPos: Partial<XYPosition> = { x: undefined, y: undefined }
let mousePosition: XYPosition = { x: 0, y: 0 } let mousePosition: XYPosition = { x: 0, y: 0 }
let dragEvent: MouseEvent | null = null let dragEvent: MouseEvent | null = null
let dragStarted = false
let autoPanId = 0 let autoPanId = 0
let autoPanStarted = false let autoPanStarted = false
@@ -147,41 +149,51 @@ export function useDrag(params: UseDragParams) {
if (isDisabled) { if (isDisabled) {
selection.on('.drag', null) selection.on('.drag', null)
} else { } else {
const startDrag = (event: UseDragEvent) => {
dragStarted = true
const node = findNode(id)
if (!selectNodesOnDrag.value && !multiSelectionActive.value && node) {
if (!node.selected) {
// we need to reset selected nodes when selectNodesOnDrag=false
removeSelectedElements()
}
}
if (node && toValue(selectable) && selectNodesOnDrag.value) {
handleNodeClick(
node,
multiSelectionActive.value,
addSelectedNodes,
removeSelectedElements,
nodesSelectionActive,
false,
nodeEl as HTMLDivElement,
)
}
const pointerPos = getPointerPosition(event)
lastPos = pointerPos
dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id)
if (dragItems.length) {
const [currentNode, nodes] = getEventHandlerParams({
id,
dragItems,
findNode,
})
onStart({ event: event.sourceEvent, node: currentNode, nodes })
}
}
dragHandler = drag() dragHandler = drag()
.on('start', (event: UseDragEvent) => { .on('start', (event: UseDragEvent) => {
const node = findNode(id) if (nodeDragThreshold.value === 0) {
if (!selectNodesOnDrag.value && !multiSelectionActive.value && node) { startDrag(event)
if (!node.selected) {
// we need to reset selected nodes when selectNodesOnDrag=false
removeSelectedElements()
}
} }
if (node && toValue(selectable) && selectNodesOnDrag.value) { lastPos = getPointerPosition(event)
handleNodeClick(
node,
multiSelectionActive.value,
addSelectedNodes,
removeSelectedElements,
nodesSelectionActive,
false,
nodeEl as HTMLDivElement,
)
}
const pointerPos = getPointerPosition(event)
lastPos = pointerPos
dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id)
if (dragItems.length) {
const [currentNode, nodes] = getEventHandlerParams({
id,
dragItems,
findNode,
})
onStart({ event: event.sourceEvent, node: currentNode, nodes })
}
containerBounds = vueFlowRef.value?.getBoundingClientRect() || null containerBounds = vueFlowRef.value?.getBoundingClientRect() || null
mousePosition = getEventPosition(event.sourceEvent, containerBounds!) mousePosition = getEventPosition(event.sourceEvent, containerBounds!)
@@ -189,13 +201,23 @@ export function useDrag(params: UseDragParams) {
.on('drag', (event: UseDragEvent) => { .on('drag', (event: UseDragEvent) => {
const pointerPos = getPointerPosition(event) const pointerPos = getPointerPosition(event)
if (!autoPanStarted && autoPanOnNodeDrag.value) { if (!autoPanStarted && dragStarted && autoPanOnNodeDrag.value) {
autoPanStarted = true autoPanStarted = true
autoPan() autoPan()
} }
if (!dragStarted) {
const x = pointerPos.xSnapped - (lastPos.x ?? 0)
const y = pointerPos.ySnapped - (lastPos.y ?? 0)
const distance = Math.sqrt(x * x + y * y)
if (distance > nodeDragThreshold.value) {
startDrag(event)
}
}
// skip events without movement // skip events without movement
if ((lastPos.x !== pointerPos.xSnapped || lastPos.y !== pointerPos.ySnapped) && dragItems.length) { if ((lastPos.x !== pointerPos.xSnapped || lastPos.y !== pointerPos.ySnapped) && dragItems.length && dragStarted) {
dragEvent = event.sourceEvent as MouseEvent dragEvent = event.sourceEvent as MouseEvent
mousePosition = getEventPosition(event.sourceEvent, containerBounds!) mousePosition = getEventPosition(event.sourceEvent, containerBounds!)
@@ -203,8 +225,13 @@ export function useDrag(params: UseDragParams) {
} }
}) })
.on('end', (event: UseDragEvent) => { .on('end', (event: UseDragEvent) => {
if (!dragStarted) {
return
}
dragging.value = false dragging.value = false
autoPanStarted = false autoPanStarted = false
dragStarted = false
cancelAnimationFrame(autoPanId) cancelAnimationFrame(autoPanId)
if (dragItems.length) { if (dragItems.length) {
+1
View File
@@ -105,6 +105,7 @@ function defaultState(): State {
nodesFocusable: true, nodesFocusable: true,
nodesConnectable: true, nodesConnectable: true,
nodesDraggable: true, nodesDraggable: true,
nodeDragThreshold: 0,
elementsSelectable: true, elementsSelectable: true,
selectNodesOnDrag: true, selectNodesOnDrag: true,
multiSelectionActive: false, multiSelectionActive: false,
+1
View File
@@ -140,6 +140,7 @@ export interface FlowProps {
edgesUpdatable?: EdgeUpdatable edgesUpdatable?: EdgeUpdatable
nodesDraggable?: boolean nodesDraggable?: boolean
nodesConnectable?: boolean nodesConnectable?: boolean
nodeDragThreshold?: number
elementsSelectable?: boolean elementsSelectable?: boolean
selectNodesOnDrag?: boolean selectNodesOnDrag?: boolean
/** move pane on drag, replaced prop `paneMovable` */ /** move pane on drag, replaced prop `paneMovable` */
+1
View File
@@ -105,6 +105,7 @@ export interface State extends Omit<FlowOptions, 'id' | 'modelValue'> {
nodesFocusable: boolean nodesFocusable: boolean
nodesDraggable: boolean nodesDraggable: boolean
nodesConnectable: boolean nodesConnectable: boolean
nodeDragThreshold: number
elementsSelectable: boolean elementsSelectable: boolean
selectNodesOnDrag: boolean selectNodesOnDrag: boolean