Skip to content

Commit e71a2aa

Browse files
committed
feat(core): add nodeDragThreshold option
1 parent ed31890 commit e71a2aa

File tree

4 files changed

+61
-31
lines changed

4 files changed

+61
-31
lines changed

packages/core/src/composables/useDrag.ts

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export function useDrag(params: UseDragParams) {
3838
noDragClassName,
3939
nodes,
4040
nodeExtent,
41+
nodeDragThreshold,
4142
viewport,
4243
autoPanOnNodeDrag,
4344
nodesDraggable,
@@ -65,6 +66,7 @@ export function useDrag(params: UseDragParams) {
6566
let lastPos: Partial<XYPosition> = { x: undefined, y: undefined }
6667
let mousePosition: XYPosition = { x: 0, y: 0 }
6768
let dragEvent: MouseEvent | null = null
69+
let dragStarted = false
6870

6971
let autoPanId = 0
7072
let autoPanStarted = false
@@ -147,64 +149,89 @@ export function useDrag(params: UseDragParams) {
147149
if (isDisabled) {
148150
selection.on('.drag', null)
149151
} else {
150-
dragHandler = drag()
151-
.on('start', (event: UseDragEvent) => {
152-
const node = findNode(id)
153-
if (!selectNodesOnDrag.value && !multiSelectionActive.value && node) {
154-
if (!node.selected) {
155-
// we need to reset selected nodes when selectNodesOnDrag=false
156-
removeSelectedElements()
157-
}
152+
const startDrag = (event: UseDragEvent) => {
153+
dragStarted = true
154+
155+
const node = findNode(id)
156+
if (!selectNodesOnDrag.value && !multiSelectionActive.value && node) {
157+
if (!node.selected) {
158+
// we need to reset selected nodes when selectNodesOnDrag=false
159+
removeSelectedElements()
158160
}
161+
}
162+
163+
if (node && toValue(selectable) && selectNodesOnDrag.value) {
164+
handleNodeClick(
165+
node,
166+
multiSelectionActive.value,
167+
addSelectedNodes,
168+
removeSelectedElements,
169+
nodesSelectionActive,
170+
false,
171+
nodeEl as HTMLDivElement,
172+
)
173+
}
159174

160-
if (node && toValue(selectable) && selectNodesOnDrag.value) {
161-
handleNodeClick(
162-
node,
163-
multiSelectionActive.value,
164-
addSelectedNodes,
165-
removeSelectedElements,
166-
nodesSelectionActive,
167-
false,
168-
nodeEl as HTMLDivElement,
169-
)
170-
}
175+
const pointerPos = getPointerPosition(event)
176+
lastPos = pointerPos
177+
dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id)
171178

172-
const pointerPos = getPointerPosition(event)
173-
lastPos = pointerPos
174-
dragItems = getDragItems(nodes.value, nodesDraggable.value, pointerPos, findNode, id)
179+
if (dragItems.length) {
180+
const [currentNode, nodes] = getEventHandlerParams({
181+
id,
182+
dragItems,
183+
findNode,
184+
})
175185

176-
if (dragItems.length) {
177-
const [currentNode, nodes] = getEventHandlerParams({
178-
id,
179-
dragItems,
180-
findNode,
181-
})
186+
onStart({ event: event.sourceEvent, node: currentNode, nodes })
187+
}
188+
}
182189

183-
onStart({ event: event.sourceEvent, node: currentNode, nodes })
190+
dragHandler = drag()
191+
.on('start', (event: UseDragEvent) => {
192+
if (nodeDragThreshold.value === 0) {
193+
startDrag(event)
184194
}
185195

196+
lastPos = getPointerPosition(event)
197+
186198
containerBounds = vueFlowRef.value?.getBoundingClientRect() || null
187199
mousePosition = getEventPosition(event.sourceEvent, containerBounds!)
188200
})
189201
.on('drag', (event: UseDragEvent) => {
190202
const pointerPos = getPointerPosition(event)
191203

192-
if (!autoPanStarted && autoPanOnNodeDrag.value) {
204+
if (!autoPanStarted && dragStarted && autoPanOnNodeDrag.value) {
193205
autoPanStarted = true
194206
autoPan()
195207
}
196208

209+
if (!dragStarted) {
210+
const x = pointerPos.xSnapped - (lastPos.x ?? 0)
211+
const y = pointerPos.ySnapped - (lastPos.y ?? 0)
212+
const distance = Math.sqrt(x * x + y * y)
213+
214+
if (distance > nodeDragThreshold.value) {
215+
startDrag(event)
216+
}
217+
}
218+
197219
// skip events without movement
198-
if ((lastPos.x !== pointerPos.xSnapped || lastPos.y !== pointerPos.ySnapped) && dragItems.length) {
220+
if ((lastPos.x !== pointerPos.xSnapped || lastPos.y !== pointerPos.ySnapped) && dragItems.length && dragStarted) {
199221
dragEvent = event.sourceEvent as MouseEvent
200222
mousePosition = getEventPosition(event.sourceEvent, containerBounds!)
201223

202224
updateNodes(pointerPos)
203225
}
204226
})
205227
.on('end', (event: UseDragEvent) => {
228+
if (!dragStarted) {
229+
return
230+
}
231+
206232
dragging.value = false
207233
autoPanStarted = false
234+
dragStarted = false
208235
cancelAnimationFrame(autoPanId)
209236

210237
if (dragItems.length) {

packages/core/src/store/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ function defaultState(): State {
105105
nodesFocusable: true,
106106
nodesConnectable: true,
107107
nodesDraggable: true,
108+
nodeDragThreshold: 0,
108109
elementsSelectable: true,
109110
selectNodesOnDrag: true,
110111
multiSelectionActive: false,

packages/core/src/types/flow.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export interface FlowProps {
140140
edgesUpdatable?: EdgeUpdatable
141141
nodesDraggable?: boolean
142142
nodesConnectable?: boolean
143+
nodeDragThreshold?: number
143144
elementsSelectable?: boolean
144145
selectNodesOnDrag?: boolean
145146
/** move pane on drag, replaced prop `paneMovable` */

packages/core/src/types/store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export interface State extends Omit<FlowOptions, 'id' | 'modelValue'> {
105105
nodesFocusable: boolean
106106
nodesDraggable: boolean
107107
nodesConnectable: boolean
108+
nodeDragThreshold: number
108109

109110
elementsSelectable: boolean
110111
selectNodesOnDrag: boolean

0 commit comments

Comments
 (0)