Skip to content

Commit 268ed0e

Browse files
committed
feat(core): add useNodeConnections composable (#1728)
* feat(core): add `useNodeConnections` composable * chore(core): export `useNodeConnections` * chore(docs): update math example * docs: add guide for `useNodeConnections` * chore(changeset): add
1 parent 1c5caff commit 268ed0e

File tree

6 files changed

+174
-16
lines changed

6 files changed

+174
-16
lines changed

.changeset/gentle-owls-grin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@vue-flow/core": minor
3+
---
4+
5+
Add `useNodeConnections` composable

docs/examples/math/ResultNode.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup>
22
import { computed } from 'vue'
3-
import { Handle, Position, useHandleConnections, useNodesData, useVueFlow } from '@vue-flow/core'
3+
import { Handle, Position, useHandleConnections, useNodeConnections, useNodesData } from '@vue-flow/core'
44
55
defineProps(['id'])
66
@@ -11,8 +11,6 @@ const mathFunctions = {
1111
'/': (a, b) => a / b,
1212
}
1313
14-
const { getConnectedEdges } = useVueFlow()
15-
1614
// Get the source connections of the result node. In this example it's only one operator node.
1715
const sourceConnections = useHandleConnections({
1816
// type target means all connections where *this* node is the target
@@ -21,9 +19,10 @@ const sourceConnections = useHandleConnections({
2119
})
2220
2321
// Get the source connections of the operator node
24-
const operatorSourceConnections = computed(() =>
25-
getConnectedEdges(sourceConnections.value[0].source).filter((e) => e.source !== sourceConnections.value[0].source),
26-
)
22+
const operatorSourceConnections = useNodeConnections({
23+
type: 'target',
24+
nodeId: () => sourceConnections.value[0]?.source,
25+
})
2726
2827
const operatorData = useNodesData(() => sourceConnections.value.map((connection) => connection.source))
2928
@@ -50,6 +49,7 @@ const result = computed(() => {
5049
</script>
5150
5251
<template>
52+
{{ operatorSourceConnections }}
5353
<div class="calculation">
5454
<template v-for="(value, i) in valueData" :key="`${value.id}-${value.data}`">
5555
<span>

docs/src/guide/composables.md

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,14 @@ onInit((instance) => {
7070

7171
## [useHandleConnections](/typedocs/functions/useHandleConnections)
7272

73-
`useHandleConnections` provides you with an array of connections that are connected to the node you pass to it.
73+
`useHandleConnections` provides you with an array of connections that are connected to specific `<Handle>`.
7474

7575
```ts
76-
import { useHandleConnections } from '@vue-flow/core'
76+
import { type HandleConnection, useHandleConnections } from '@vue-flow/core'
7777

7878
// get all connections where this node is the target (incoming connections)
7979
const targetConnections = useHandleConnections({
80+
// type is required
8081
type: 'target',
8182
})
8283

@@ -89,10 +90,42 @@ const connections = useHandleConnections({
8990
id: 'handle-1', // you can explicitly pass a handle id if there are multiple handles of the same type
9091
nodeId: '1', // you can explicitly pass a node id, otherwise it's used from the `NodeId injection
9192
type: 'target',
92-
onConnect: (connections: Connection[]) => {
93+
onConnect: (connections: HandleConnection[]) => {
9394
// do something with the connections
9495
},
95-
onDisconnect: (connections: Connection[]) => {
96+
onDisconnect: (connections: HandleConnection[]) => {
97+
// do something with the connections
98+
},
99+
})
100+
```
101+
102+
## [useNodeConnections](/typedocs/functions/useNodeConnections)
103+
104+
`useNodeConnections` provides you with an array of connections that are connected to a specific node.
105+
This composable is especially useful when you want to get all connections (of either type `source` or `target`) of a node
106+
instead of just the connections of a specific `<Handle>`.
107+
108+
```ts
109+
import { type HandleConnection, useNodeConnections } from '@vue-flow/core'
110+
111+
// get all connections where this node is the target (incoming connections)
112+
const targetConnections = useNodeConnections({
113+
// type is required
114+
type: 'target',
115+
})
116+
117+
// get all connections where this node is the source (outgoing connections)
118+
const sourceConnections = useNodeConnections({
119+
type: 'source',
120+
})
121+
122+
const connections = useNodeConnections({
123+
nodeId: '1', // you can explicitly pass a node id, otherwise it's used from the `NodeId injection
124+
type: 'target',
125+
onConnect: (connections: HandleConnection[]) => {
126+
// do something with the connections
127+
},
128+
onDisconnect: (connections: HandleConnection[]) => {
96129
// do something with the connections
97130
},
98131
})

examples/vite/src/Math/ResultNode.vue

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
<script setup lang="ts">
22
import type { GraphNode } from '@vue-flow/core'
3-
import { Handle, Position, useHandleConnections, useNodesData, useVueFlow } from '@vue-flow/core'
3+
import { Handle, Position, useHandleConnections, useNodeConnections, useNodesData } from '@vue-flow/core'
44
import type { OperatorNodeData, ValueNodeData } from './types'
55
import { mathFunctions } from './utils'
66
77
defineProps<{ id: string }>()
88
9-
const { getConnectedEdges } = useVueFlow()
10-
119
// Get the source connections of the result node. In this example it's only one operator node.
1210
const sourceConnections = useHandleConnections({
1311
// type target means all connections where *this* node is the target
@@ -16,9 +14,10 @@ const sourceConnections = useHandleConnections({
1614
})
1715
1816
// Get the source connections of the operator node
19-
const operatorSourceConnections = computed(() =>
20-
getConnectedEdges(sourceConnections.value[0].source).filter((e) => e.source !== sourceConnections.value[0].source),
21-
)
17+
const operatorSourceConnections = useNodeConnections({
18+
type: 'target',
19+
nodeId: () => sourceConnections.value[0]?.source,
20+
})
2221
2322
const operatorData = useNodesData<GraphNode<OperatorNodeData>>(() =>
2423
sourceConnections.value.map((connection) => connection.source),
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import type { ComputedRef, MaybeRefOrGetter } from 'vue'
2+
import { computed, ref, toRef, toValue, watch } from 'vue'
3+
import type { HandleConnection, HandleElement, HandleType } from '../types'
4+
import { useNodeId } from './useNodeId'
5+
import { useVueFlow } from './useVueFlow'
6+
7+
export interface UseNodeConnectionsParams {
8+
type: MaybeRefOrGetter<HandleType>
9+
nodeId?: MaybeRefOrGetter<string | null>
10+
onConnect?: (connections: HandleConnection[]) => void
11+
onDisconnect?: (connections: HandleConnection[]) => void
12+
}
13+
14+
/**
15+
* Composable that returns existing connections of a node by handle type.
16+
* This is useful when you want to get all connections of a node by a specific handle type.
17+
*
18+
* @public
19+
* @param params
20+
* @param params.type - handle type `source` or `target`
21+
* @param params.nodeId - node id - if not provided, the node id from the `useNodeId` (meaning, the context-based injection) is used
22+
* @param params.onConnect - gets called when a connection is created
23+
* @param params.onDisconnect - gets called when a connection is removed
24+
*
25+
* @returns An array of connections
26+
*/
27+
export function useNodeConnections(params: UseNodeConnectionsParams): ComputedRef<HandleConnection[]> {
28+
const { type, nodeId, onConnect, onDisconnect } = params
29+
30+
const { connectionLookup, findNode } = useVueFlow()
31+
32+
const _nodeId = useNodeId()
33+
34+
const currentNodeId = toRef(() => toValue(nodeId) ?? _nodeId)
35+
36+
const handleType = toRef(() => toValue(type))
37+
38+
const node = computed(() => findNode(currentNodeId.value))
39+
40+
const handleIds = computed(() => {
41+
if (!node.value) {
42+
return []
43+
}
44+
45+
const handles: HandleElement['id'][] = []
46+
for (const handle of node.value?.handleBounds?.[handleType.value] ?? []) {
47+
handles.push(handle.id)
48+
}
49+
50+
return handles
51+
})
52+
53+
const prevConnections = ref<Map<string, HandleConnection> | null>(null)
54+
55+
const connectionsFromLookup = computed(() => {
56+
const nodeConnections = [] as Map<string, HandleConnection>[]
57+
58+
for (const handleId of handleIds.value) {
59+
const connectionMap = connectionLookup.value.get(`${currentNodeId.value}-${handleType.value}-${handleId}`)
60+
if (connectionMap) {
61+
nodeConnections.push(connectionMap)
62+
}
63+
}
64+
65+
return nodeConnections
66+
})
67+
68+
watch(
69+
[connectionsFromLookup, () => typeof onConnect !== 'undefined', () => typeof onDisconnect !== 'undefined'],
70+
([currentConnections]) => {
71+
if (!currentConnections) {
72+
return
73+
}
74+
75+
const newConnections = new Map<string, HandleConnection>()
76+
77+
for (const connectionMap of currentConnections) {
78+
for (const [key, connection] of connectionMap) {
79+
newConnections.set(key, connection)
80+
}
81+
}
82+
83+
if (!prevConnections.value) {
84+
prevConnections.value = new Map(newConnections)
85+
return
86+
}
87+
88+
const prevConnectionsValue = prevConnections.value
89+
90+
const addedConnections = Array.from(newConnections.keys()).filter((key) => !prevConnectionsValue.has(key))
91+
92+
const removedConnections = Array.from(prevConnectionsValue.keys()).filter((key) => !newConnections.has(key))
93+
94+
if (addedConnections.length && onConnect) {
95+
const added = addedConnections.map((key) => newConnections.get(key)!)
96+
onConnect(added)
97+
}
98+
99+
if (removedConnections.length && onDisconnect) {
100+
const removed = removedConnections.map((key) => prevConnectionsValue.get(key)!)
101+
onDisconnect(removed)
102+
}
103+
104+
prevConnections.value = new Map(newConnections)
105+
},
106+
{ immediate: true },
107+
)
108+
109+
return computed(() => {
110+
const connections = [] as HandleConnection[]
111+
112+
for (const connectionMap of connectionsFromLookup.value) {
113+
for (const connection of connectionMap.values()) {
114+
connections.push(connection)
115+
}
116+
}
117+
118+
return connections
119+
})
120+
}

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export { useGetPointerPosition } from './composables/useGetPointerPosition'
7070
export { useNodeId } from './composables/useNodeId'
7171
export { useConnection } from './composables/useConnection'
7272
export { useHandleConnections } from './composables/useHandleConnections'
73+
export { useNodeConnections } from './composables/useNodeConnections'
7374
export { useNodesData } from './composables/useNodesData'
7475
export { useEdgesData } from './composables/useEdgesData'
7576
export { useNodesInitialized } from './composables/useNodesInitialized'

0 commit comments

Comments
 (0)