2
2
import { ref , toRef , watch } from ' vue'
3
3
import UserSelection from ' ../../components/UserSelection/UserSelection.vue'
4
4
import NodesSelection from ' ../../components/NodesSelection/NodesSelection.vue'
5
+ import type { NodeChange } from ' ../../types'
5
6
import { SelectionMode } from ' ../../types'
6
7
import { useKeyPress , useVueFlow } from ' ../../composables'
7
- import { getConnectedEdges , getNodesInside } from ' ../../utils'
8
+ import { getEventPosition , getNodesInside , getSelectionChanges } from ' ../../utils'
8
9
import { getMousePosition } from ' ./utils'
9
10
10
11
const { isSelecting, selectionKeyPressed } = defineProps <{ isSelecting: boolean ; selectionKeyPressed: boolean }>()
11
12
12
13
const {
13
14
vueFlowRef,
14
15
getNodes,
15
- getEdges,
16
16
viewport,
17
17
emits,
18
18
userSelectionActive,
@@ -21,7 +21,6 @@ const {
21
21
userSelectionRect,
22
22
elementsSelectable,
23
23
nodesSelectionActive,
24
- addSelectedElements,
25
24
getSelectedEdges,
26
25
getSelectedNodes,
27
26
removeNodes,
@@ -30,6 +29,8 @@ const {
30
29
deleteKeyCode,
31
30
multiSelectionKeyCode,
32
31
multiSelectionActive,
32
+ edgeLookup,
33
+ nodeLookup,
33
34
} = useVueFlow ()
34
35
35
36
const container = ref <HTMLDivElement | null >(null )
@@ -40,6 +41,8 @@ const prevSelectedEdgesCount = ref(0)
40
41
41
42
const containerBounds = ref <DOMRect >()
42
43
44
+ const edgeIdLookup = ref <Map <string , Set <string >>>(new Map ())
45
+
43
46
const hasActiveSelection = toRef (() => elementsSelectable .value && (isSelecting || userSelectionActive .value ))
44
47
45
48
// Used to prevent click events when the user lets go of the selectionKey during a selection
@@ -65,6 +68,16 @@ watch(multiSelectKeyPressed, (isKeyPressed) => {
65
68
multiSelectionActive .value = isKeyPressed
66
69
})
67
70
71
+ function wrapHandler(handler : Function , containerRef : HTMLDivElement | null ) {
72
+ return (event : MouseEvent ) => {
73
+ if (event .target !== containerRef ) {
74
+ return
75
+ }
76
+
77
+ handler ?.(event )
78
+ }
79
+ }
80
+
68
81
function resetUserSelection() {
69
82
userSelectionActive .value = false
70
83
userSelectionRect .value = null
@@ -74,7 +87,7 @@ function resetUserSelection() {
74
87
}
75
88
76
89
function onClick(event : MouseEvent ) {
77
- if (selectionInProgress .value || event . target !== container . value || hasActiveSelection . value ) {
90
+ if (selectionInProgress .value ) {
78
91
selectionInProgress .value = false
79
92
return
80
93
}
@@ -87,10 +100,6 @@ function onClick(event: MouseEvent) {
87
100
}
88
101
89
102
function onContextMenu(event : MouseEvent ) {
90
- if (event .target !== container .value ) {
91
- return
92
- }
93
-
94
103
if (Array .isArray (panOnDrag .value ) && panOnDrag .value ?.includes (2 )) {
95
104
event .preventDefault ()
96
105
return
@@ -100,27 +109,32 @@ function onContextMenu(event: MouseEvent) {
100
109
}
101
110
102
111
function onWheel(event : WheelEvent ) {
103
- if (event .target !== container .value ) {
104
- return
105
- }
106
-
107
112
emits .paneScroll (event )
108
113
}
109
114
110
115
function onPointerDown(event : PointerEvent ) {
111
- if (! hasActiveSelection .value ) {
112
- return emits .paneMouseMove (event )
113
- }
114
-
115
116
containerBounds .value = vueFlowRef .value ?.getBoundingClientRect ()
116
117
container .value ?.setPointerCapture (event .pointerId )
117
118
118
- if (! elementsSelectable || ! isSelecting || event .button !== 0 || event .target !== container .value || ! containerBounds .value ) {
119
+ if (
120
+ ! elementsSelectable .value ||
121
+ ! isSelecting ||
122
+ event .button !== 0 ||
123
+ event .target !== container .value ||
124
+ ! containerBounds .value
125
+ ) {
119
126
return
120
127
}
121
128
122
129
const { x, y } = getMousePosition (event , containerBounds .value )
123
130
131
+ edgeIdLookup .value = new Map ()
132
+
133
+ for (const [id, edge] of edgeLookup .value ) {
134
+ edgeIdLookup .value .set (edge .source , edgeIdLookup .value .get (edge .source )?.add (id ) || new Set ([id ]))
135
+ edgeIdLookup .value .set (edge .target , edgeIdLookup .value .get (edge .target )?.add (id ) || new Set ([id ]))
136
+ }
137
+
124
138
removeSelectedElements ()
125
139
126
140
userSelectionRect .value = {
@@ -138,43 +152,62 @@ function onPointerDown(event: PointerEvent) {
138
152
}
139
153
140
154
function onPointerMove(event : PointerEvent ) {
141
- if (! hasActiveSelection .value ) {
142
- return emits .paneMouseMove (event )
143
- }
144
-
145
155
if (! containerBounds .value || ! userSelectionRect .value ) {
146
156
return
147
157
}
148
158
149
159
selectionInProgress .value = true
150
160
151
- const mousePos = getMousePosition (event , containerBounds .value )
161
+ const { x : mouseX, y : mouseY } = getEventPosition (event , containerBounds .value )
152
162
const { startX = 0 , startY = 0 } = userSelectionRect .value
153
163
154
164
const nextUserSelectRect = {
155
- ... userSelectionRect .value ,
156
- x: mousePos .x < startX ? mousePos .x : startX ,
157
- y: mousePos .y < startY ? mousePos .y : startY ,
158
- width: Math .abs (mousePos .x - startX ),
159
- height: Math .abs (mousePos .y - startY ),
165
+ startX ,
166
+ startY ,
167
+ x: mouseX < startX ? mouseX : startX ,
168
+ y: mouseY < startY ? mouseY : startY ,
169
+ width: Math .abs (mouseX - startX ),
170
+ height: Math .abs (mouseY - startY ),
160
171
}
161
172
162
173
const selectedNodes = getNodesInside (
163
174
getNodes .value ,
164
- userSelectionRect . value ,
175
+ nextUserSelectRect ,
165
176
viewport .value ,
166
177
selectionMode .value === SelectionMode .Partial ,
167
178
true ,
168
179
)
169
180
170
- const selectedEdges = getConnectedEdges (selectedNodes , getEdges .value )
181
+ const selectedEdgeIds = new Set <string >()
182
+ const selectedNodeIds = new Set <string >()
171
183
172
- prevSelectedNodesCount . value = selectedNodes . length
173
- prevSelectedEdgesCount . value = selectedEdges . length
184
+ for ( const selectedNode of selectedNodes ) {
185
+ selectedNodeIds . add ( selectedNode . id )
174
186
175
- userSelectionRect .value = nextUserSelectRect
187
+ const edgeIds = edgeIdLookup .value .get (selectedNode .id )
188
+
189
+ if (edgeIds ) {
190
+ for (const edgeId of edgeIds ) {
191
+ selectedEdgeIds .add (edgeId )
192
+ }
193
+ }
194
+ }
195
+
196
+ if (prevSelectedNodesCount .value !== selectedNodeIds .size ) {
197
+ prevSelectedNodesCount .value = selectedNodeIds .size
198
+ const changes = getSelectionChanges (nodeLookup .value , selectedNodeIds , true ) as NodeChange []
199
+ emits .nodesChange (changes )
200
+ }
176
201
177
- addSelectedElements ([... selectedNodes , ... selectedEdges ])
202
+ if (prevSelectedEdgesCount .value !== selectedEdgeIds .size ) {
203
+ prevSelectedEdgesCount .value = selectedEdgeIds .size
204
+ const changes = getSelectionChanges (edgeLookup .value , selectedEdgeIds )
205
+ emits .edgesChange (changes )
206
+ }
207
+
208
+ userSelectionRect .value = nextUserSelectRect
209
+ userSelectionActive .value = true
210
+ nodesSelectionActive .value = false
178
211
}
179
212
180
213
function onPointerUp(event : PointerEvent ) {
@@ -184,10 +217,6 @@ function onPointerUp(event: PointerEvent) {
184
217
185
218
container .value ?.releasePointerCapture (event .pointerId )
186
219
187
- if (! hasActiveSelection .value ) {
188
- return
189
- }
190
-
191
220
// We only want to trigger click functions when in selection mode if
192
221
// the user did not move the mouse.
193
222
if (! userSelectionActive .value && userSelectionRect .value && event .target === container .value ) {
@@ -206,14 +235,6 @@ function onPointerUp(event: PointerEvent) {
206
235
selectionInProgress .value = false
207
236
}
208
237
}
209
-
210
- function onPointerEnter(event : PointerEvent ) {
211
- if (hasActiveSelection .value ) {
212
- return
213
- }
214
-
215
- emits .paneMouseEnter (event )
216
- }
217
238
</script >
218
239
219
240
<script lang="ts">
@@ -228,13 +249,14 @@ export default {
228
249
ref =" container"
229
250
class =" vue-flow__pane vue-flow__container"
230
251
:class =" { selection: isSelecting }"
231
- @click =" onClick"
232
- @contextmenu =" onContextMenu"
233
- @wheel.passive =" onWheel"
234
- @pointerenter =" onPointerEnter"
235
- @pointerdown =" onPointerDown"
236
- @pointermove =" onPointerMove"
237
- @pointerup =" onPointerUp"
252
+ @click =" (event) => (hasActiveSelection ? undefined : wrapHandler(onClick, container)(event))"
253
+ @contextmenu =" wrapHandler(onContextMenu, container)($event)"
254
+ @wheel.passive =" wrapHandler(onWheel, container)($event)"
255
+ @pointerenter =" (event) => (hasActiveSelection ? undefined : emits.paneMouseEnter(event))"
256
+ @pointerdown =" (event) => (hasActiveSelection ? onPointerDown(event) : emits.paneMouseMove(event))"
257
+ @pointermove =" (event) => (hasActiveSelection ? onPointerMove(event) : emits.paneMouseMove(event))"
258
+ @pointerup =" (event) => (hasActiveSelection ? onPointerUp(event) : undefined)"
259
+ @pointerleave =" emits.paneMouseLeave($event)"
238
260
>
239
261
<slot />
240
262
<UserSelection v-if =" userSelectionActive && userSelectionRect" :user-selection-rect =" userSelectionRect" />
0 commit comments