refactor(core): use pointer evts in pane and prevent cancel of selection
This commit is contained in:
@@ -1,14 +1,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, toRef } from 'vue'
|
import { ref, toRef, watch } from 'vue'
|
||||||
import UserSelection from '../../components/UserSelection/UserSelection.vue'
|
import UserSelection from '../../components/UserSelection/UserSelection.vue'
|
||||||
import NodesSelection from '../../components/NodesSelection/NodesSelection.vue'
|
import NodesSelection from '../../components/NodesSelection/NodesSelection.vue'
|
||||||
import type { GraphNode } from '../../types'
|
import { type GraphNode, SelectionMode } from '../../types'
|
||||||
import { SelectionMode } from '../../types'
|
|
||||||
import { useKeyPress, useVueFlow } from '../../composables'
|
import { useKeyPress, useVueFlow } from '../../composables'
|
||||||
import { getConnectedEdges, getNodesInside } from '../../utils'
|
import { getConnectedEdges, getNodesInside } from '../../utils'
|
||||||
import { getMousePosition } from './utils'
|
import { getMousePosition } from './utils'
|
||||||
|
|
||||||
const { isSelecting } = defineProps<{ isSelecting: boolean }>()
|
const { isSelecting, selectionKeyPressed } = defineProps<{ isSelecting: boolean; selectionKeyPressed: boolean }>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
vueFlowRef,
|
vueFlowRef,
|
||||||
@@ -43,41 +42,44 @@ const containerBounds = ref<DOMRect>()
|
|||||||
|
|
||||||
const hasActiveSelection = toRef(() => elementsSelectable.value && (isSelecting || userSelectionActive.value))
|
const hasActiveSelection = toRef(() => elementsSelectable.value && (isSelecting || userSelectionActive.value))
|
||||||
|
|
||||||
useKeyPress(
|
// Used to prevent click events when the user lets go of the selectionKey during a selection
|
||||||
deleteKeyCode,
|
const selectionInProgress = ref(false)
|
||||||
(keyPressed) => {
|
|
||||||
if (!keyPressed) {
|
const deleteKeyPressed = useKeyPress(deleteKeyCode, { actInsideInputWithModifier: false })
|
||||||
return
|
|
||||||
|
const multiSelectKeyPressed = useKeyPress(multiSelectionKeyCode)
|
||||||
|
|
||||||
|
watch(deleteKeyPressed, (isKeyPressed) => {
|
||||||
|
if (!isKeyPressed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodesToRemove: GraphNode[] = []
|
||||||
|
for (const node of getNodes.value) {
|
||||||
|
if (!node.selected && node.parentNode && nodesToRemove.some((n) => n.id === node.parentNode)) {
|
||||||
|
nodesToRemove.push(node)
|
||||||
|
} else if (node.selected) {
|
||||||
|
nodesToRemove.push(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodesToRemove || getSelectedEdges.value) {
|
||||||
|
if (getSelectedEdges.value.length > 0) {
|
||||||
|
removeEdges(getSelectedEdges.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodesToRemove: GraphNode[] = []
|
if (nodesToRemove.length > 0) {
|
||||||
for (const node of getNodes.value) {
|
removeNodes(nodesToRemove)
|
||||||
if (!node.selected && node.parentNode && nodesToRemove.some((n) => n.id === node.parentNode)) {
|
|
||||||
nodesToRemove.push(node)
|
|
||||||
} else if (node.selected) {
|
|
||||||
nodesToRemove.push(node)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodesToRemove || getSelectedEdges.value) {
|
nodesSelectionActive.value = false
|
||||||
if (getSelectedEdges.value.length > 0) {
|
|
||||||
removeEdges(getSelectedEdges.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodesToRemove.length > 0) {
|
removeSelectedElements()
|
||||||
removeNodes(nodesToRemove)
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
nodesSelectionActive.value = false
|
watch(multiSelectKeyPressed, (isKeyPressed) => {
|
||||||
|
multiSelectionActive.value = isKeyPressed
|
||||||
removeSelectedElements()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ actInsideInputWithModifier: false },
|
|
||||||
)
|
|
||||||
|
|
||||||
useKeyPress(multiSelectionKeyCode, (keyPressed) => {
|
|
||||||
multiSelectionActive.value = keyPressed
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function resetUserSelection() {
|
function resetUserSelection() {
|
||||||
@@ -89,7 +91,8 @@ function resetUserSelection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onClick(event: MouseEvent) {
|
function onClick(event: MouseEvent) {
|
||||||
if (event.target !== container.value || hasActiveSelection.value) {
|
if (selectionInProgress.value || event.target !== container.value || hasActiveSelection.value) {
|
||||||
|
selectionInProgress.value = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,8 +124,9 @@ function onWheel(event: WheelEvent) {
|
|||||||
emits.paneScroll(event)
|
emits.paneScroll(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseDown(event: MouseEvent) {
|
function onPointerDown(event: PointerEvent) {
|
||||||
containerBounds.value = vueFlowRef.value!.getBoundingClientRect()
|
containerBounds.value = vueFlowRef.value?.getBoundingClientRect()
|
||||||
|
container.value?.setPointerCapture(event.pointerId)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!hasActiveSelection.value ||
|
!hasActiveSelection.value ||
|
||||||
@@ -153,26 +157,19 @@ function onMouseDown(event: MouseEvent) {
|
|||||||
emits.selectionStart(event)
|
emits.selectionStart(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseMove(event: MouseEvent) {
|
function onPointerMove(event: PointerEvent) {
|
||||||
if (!hasActiveSelection.value) {
|
if (!hasActiveSelection.value) {
|
||||||
return emits.paneMouseMove(event)
|
return emits.paneMouseMove(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSelecting || !containerBounds.value || !userSelectionRect.value) {
|
if (!containerBounds.value || !userSelectionRect.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userSelectionActive.value) {
|
selectionInProgress.value = true
|
||||||
userSelectionActive.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodesSelectionActive.value) {
|
|
||||||
nodesSelectionActive.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const mousePos = getMousePosition(event, containerBounds.value)
|
const mousePos = getMousePosition(event, containerBounds.value)
|
||||||
const startX = userSelectionRect.value.startX ?? 0
|
const { startX = 0, startY = 0 } = userSelectionRect.value
|
||||||
const startY = userSelectionRect.value.startY ?? 0
|
|
||||||
|
|
||||||
const nextUserSelectRect = {
|
const nextUserSelectRect = {
|
||||||
...userSelectionRect.value,
|
...userSelectionRect.value,
|
||||||
@@ -187,6 +184,7 @@ function onMouseMove(event: MouseEvent) {
|
|||||||
userSelectionRect.value,
|
userSelectionRect.value,
|
||||||
viewport.value,
|
viewport.value,
|
||||||
selectionMode.value === SelectionMode.Partial,
|
selectionMode.value === SelectionMode.Partial,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectedEdges = getConnectedEdges(selectedNodes, getEdges.value)
|
const selectedEdges = getConnectedEdges(selectedNodes, getEdges.value)
|
||||||
@@ -199,14 +197,12 @@ function onMouseMove(event: MouseEvent) {
|
|||||||
addSelectedElements([...selectedNodes, ...selectedEdges])
|
addSelectedElements([...selectedNodes, ...selectedEdges])
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseUp(event: MouseEvent) {
|
function onPointerUp(event: PointerEvent) {
|
||||||
if (!hasActiveSelection.value) {
|
if (!hasActiveSelection.value || event.button !== 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.button !== 0) {
|
container.value?.releasePointerCapture(event.pointerId)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only want to trigger click functions when in selection mode if
|
// We only want to trigger click functions when in selection mode if
|
||||||
// the user did not move the mouse.
|
// the user did not move the mouse.
|
||||||
@@ -219,22 +215,15 @@ function onMouseUp(event: MouseEvent) {
|
|||||||
resetUserSelection()
|
resetUserSelection()
|
||||||
|
|
||||||
emits.selectionEnd(event)
|
emits.selectionEnd(event)
|
||||||
|
|
||||||
|
// If the user kept holding the selectionKey during the selection,
|
||||||
|
// we need to reset the selectionInProgress, so the next click event is not prevented
|
||||||
|
if (selectionKeyPressed) {
|
||||||
|
selectionInProgress.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseLeave(event: MouseEvent) {
|
function onPointerEnter(event: PointerEvent) {
|
||||||
if (!hasActiveSelection.value) {
|
|
||||||
return emits.paneMouseLeave(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userSelectionActive.value) {
|
|
||||||
nodesSelectionActive.value = prevSelectedNodesCount.value > 0
|
|
||||||
emits.selectionEnd?.(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
resetUserSelection()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseEnter(event: MouseEvent) {
|
|
||||||
if (hasActiveSelection.value) {
|
if (hasActiveSelection.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -258,11 +247,10 @@ export default {
|
|||||||
@click="onClick"
|
@click="onClick"
|
||||||
@contextmenu="onContextMenu"
|
@contextmenu="onContextMenu"
|
||||||
@wheel.passive="onWheel"
|
@wheel.passive="onWheel"
|
||||||
@mouseenter="onMouseEnter"
|
@pointerenter="onPointerEnter"
|
||||||
@mousedown="onMouseDown"
|
@pointerdown="onPointerDown"
|
||||||
@mousemove="onMouseMove"
|
@pointermove="onPointerMove"
|
||||||
@mouseup="onMouseUp"
|
@pointerup="onPointerUp"
|
||||||
@mouseleave="onMouseLeave"
|
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
<UserSelection v-if="userSelectionActive && userSelectionRect" :user-selection-rect="userSelectionRect" />
|
<UserSelection v-if="userSelectionActive && userSelectionRect" :user-selection-rect="userSelectionRect" />
|
||||||
|
|||||||
@@ -403,6 +403,7 @@ export default {
|
|||||||
<div ref="viewportRef" class="vue-flow__viewport vue-flow__container">
|
<div ref="viewportRef" class="vue-flow__viewport vue-flow__container">
|
||||||
<Pane
|
<Pane
|
||||||
:is-selecting="isSelecting"
|
:is-selecting="isSelecting"
|
||||||
|
:selection-key-pressed="selectionKeyPressed"
|
||||||
:class="{ connecting: !!connectionStartHandle, dragging: paneDragging, draggable: shouldPanOnDrag }"
|
:class="{ connecting: !!connectionStartHandle, dragging: paneDragging, draggable: shouldPanOnDrag }"
|
||||||
>
|
>
|
||||||
<Transform>
|
<Transform>
|
||||||
|
|||||||
Reference in New Issue
Block a user