refactor(core): use connection lookup to check for selected elements (#1737)
* refactor(core): use connection lookup to check for selected elements Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(changeset): add Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --------- Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@vue-flow/core": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Use connection lookup to check what elements should be selected by a user selection box.
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
import { ref, toRef, watch } 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 { NodeChange } from '../../types'
|
import type { EdgeChange, NodeChange } from '../../types'
|
||||||
import { SelectionMode } from '../../types'
|
import { SelectionMode } from '../../types'
|
||||||
import { useKeyPress, useVueFlow } from '../../composables'
|
import { useKeyPress, useVueFlow } from '../../composables'
|
||||||
import { getEventPosition, getNodesInside, getSelectionChanges } from '../../utils'
|
import { areSetsEqual, getEventPosition, getNodesInside, getSelectionChanges } from '../../utils'
|
||||||
import { getMousePosition } from './utils'
|
import { getMousePosition } from './utils'
|
||||||
|
|
||||||
const { isSelecting, selectionKeyPressed } = defineProps<{ isSelecting: boolean; selectionKeyPressed: boolean }>()
|
const { isSelecting, selectionKeyPressed } = defineProps<{ isSelecting: boolean; selectionKeyPressed: boolean }>()
|
||||||
@@ -30,18 +30,18 @@ const {
|
|||||||
multiSelectionActive,
|
multiSelectionActive,
|
||||||
edgeLookup,
|
edgeLookup,
|
||||||
nodeLookup,
|
nodeLookup,
|
||||||
|
connectionLookup,
|
||||||
|
defaultEdgeOptions,
|
||||||
} = useVueFlow()
|
} = useVueFlow()
|
||||||
|
|
||||||
const container = ref<HTMLDivElement | null>(null)
|
const container = ref<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const prevSelectedNodesCount = ref(0)
|
const selectedNodeIds = ref<Set<string>>(new Set())
|
||||||
|
|
||||||
const prevSelectedEdgesCount = ref(0)
|
const selectedEdgeIds = ref<Set<string>>(new Set())
|
||||||
|
|
||||||
const containerBounds = ref<DOMRect>()
|
const containerBounds = ref<DOMRect>()
|
||||||
|
|
||||||
const edgeIdLookup = ref<Map<string, Set<string>>>(new Map())
|
|
||||||
|
|
||||||
const hasActiveSelection = toRef(() => elementsSelectable.value && (isSelecting || userSelectionActive.value))
|
const hasActiveSelection = toRef(() => elementsSelectable.value && (isSelecting || userSelectionActive.value))
|
||||||
|
|
||||||
// Used to prevent click events when the user lets go of the selectionKey during a selection
|
// Used to prevent click events when the user lets go of the selectionKey during a selection
|
||||||
@@ -78,14 +78,6 @@ function wrapHandler(handler: Function, containerRef: HTMLDivElement | null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetUserSelection() {
|
|
||||||
userSelectionActive.value = false
|
|
||||||
userSelectionRect.value = null
|
|
||||||
|
|
||||||
prevSelectedNodesCount.value = 0
|
|
||||||
prevSelectedEdgesCount.value = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClick(event: MouseEvent) {
|
function onClick(event: MouseEvent) {
|
||||||
if (selectionInProgress) {
|
if (selectionInProgress) {
|
||||||
selectionInProgress = false
|
selectionInProgress = false
|
||||||
@@ -129,12 +121,6 @@ function onPointerDown(event: PointerEvent) {
|
|||||||
|
|
||||||
selectionStarted = true
|
selectionStarted = true
|
||||||
selectionInProgress = false
|
selectionInProgress = false
|
||||||
edgeIdLookup.value = new Map()
|
|
||||||
|
|
||||||
for (const [id, edge] of edgeLookup.value) {
|
|
||||||
edgeIdLookup.value.set(edge.source, edgeIdLookup.value.get(edge.source)?.add(id) || new Set([id]))
|
|
||||||
edgeIdLookup.value.set(edge.target, edgeIdLookup.value.get(edge.target)?.add(id) || new Set([id]))
|
|
||||||
}
|
|
||||||
|
|
||||||
removeSelectedElements()
|
removeSelectedElements()
|
||||||
|
|
||||||
@@ -169,38 +155,39 @@ function onPointerMove(event: PointerEvent) {
|
|||||||
height: Math.abs(mouseY - startY),
|
height: Math.abs(mouseY - startY),
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedNodes = getNodesInside(
|
const prevSelectedNodeIds = selectedNodeIds.value
|
||||||
nodes.value,
|
const prevSelectedEdgeIds = selectedEdgeIds.value
|
||||||
nextUserSelectRect,
|
|
||||||
viewport.value,
|
selectedNodeIds.value = new Set(
|
||||||
selectionMode.value === SelectionMode.Partial,
|
getNodesInside(nodes.value, nextUserSelectRect, viewport.value, selectionMode.value === SelectionMode.Partial, true).map(
|
||||||
true,
|
(node) => node.id,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectedEdgeIds = new Set<string>()
|
selectedEdgeIds.value = new Set()
|
||||||
const selectedNodeIds = new Set<string>()
|
const edgesSelectable = defaultEdgeOptions.value?.selectable ?? true
|
||||||
|
|
||||||
for (const selectedNode of selectedNodes) {
|
// We look for all edges connected to the selected nodes
|
||||||
selectedNodeIds.add(selectedNode.id)
|
for (const nodeId of selectedNodeIds.value) {
|
||||||
|
const connections = connectionLookup.value.get(nodeId)
|
||||||
const edgeIds = edgeIdLookup.value.get(selectedNode.id)
|
if (!connections) {
|
||||||
|
continue
|
||||||
if (edgeIds) {
|
}
|
||||||
for (const edgeId of edgeIds) {
|
for (const { edgeId } of connections.values()) {
|
||||||
selectedEdgeIds.add(edgeId)
|
const edge = edgeLookup.value.get(edgeId)
|
||||||
|
if (edge && (edge.selectable ?? edgesSelectable)) {
|
||||||
|
selectedEdgeIds.value.add(edgeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevSelectedNodesCount.value !== selectedNodeIds.size) {
|
if (!areSetsEqual(prevSelectedNodeIds, selectedNodeIds.value)) {
|
||||||
prevSelectedNodesCount.value = selectedNodeIds.size
|
const changes = getSelectionChanges(nodeLookup.value, selectedNodeIds.value, true) as NodeChange[]
|
||||||
const changes = getSelectionChanges(nodeLookup.value, selectedNodeIds, true) as NodeChange[]
|
|
||||||
emits.nodesChange(changes)
|
emits.nodesChange(changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevSelectedEdgesCount.value !== selectedEdgeIds.size) {
|
if (!areSetsEqual(prevSelectedEdgeIds, selectedEdgeIds.value)) {
|
||||||
prevSelectedEdgesCount.value = selectedEdgeIds.size
|
const changes = getSelectionChanges(edgeLookup.value, selectedEdgeIds.value) as EdgeChange[]
|
||||||
const changes = getSelectionChanges(edgeLookup.value, selectedEdgeIds)
|
|
||||||
emits.edgesChange(changes)
|
emits.edgesChange(changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,11 +209,9 @@ function onPointerUp(event: PointerEvent) {
|
|||||||
onClick(event)
|
onClick(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevSelectedNodesCount.value > 0) {
|
userSelectionActive.value = false
|
||||||
nodesSelectionActive.value = true
|
userSelectionRect.value = null
|
||||||
}
|
nodesSelectionActive.value = selectedNodeIds.value.size > 0
|
||||||
|
|
||||||
resetUserSelection()
|
|
||||||
|
|
||||||
emits.selectionEnd(event)
|
emits.selectionEnd(event)
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,23 @@ export function areConnectionMapsEqual(a?: Map<string, Connection>, b?: Map<stri
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function areSetsEqual(a: Set<string>, b: Set<string>) {
|
||||||
|
if (a.size !== b.size) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of a) {
|
||||||
|
if (!b.has(item)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user