-
-
Notifications
You must be signed in to change notification settings - Fork 209
/
Handle.vue
109 lines (87 loc) 路 3.12 KB
/
Handle.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<script lang="ts" setup>
import { isFunction, isString } from '@vueuse/core'
import type { Position } from '../../types'
import type { HandleProps } from '../../types/handle'
const { position = 'top' as Position, connectable = true, id, isValidConnection, ...props } = defineProps<HandleProps>()
const type = toRef(props, 'type', 'source')
const { connectionStartHandle, vueFlowRef, nodesConnectable } = $(useVueFlow())
const { id: nodeId, node, nodeEl, connectedEdges } = useNode()
const handle = ref<HTMLDivElement>()
const handleId = $computed(() => id ?? `${nodeId}__handle-${position}`)
const { handlePointerDown, handleClick } = useHandle({
nodeId,
handleId,
isValidConnection,
type,
})
const isConnectable = computed(() => {
if (isString(connectable) && connectable === 'single') {
return !connectedEdges.value.some((edge) => {
const handle = edge[`${type.value}Handle`]
if (edge[type.value] !== nodeId) return false
return handle ? handle === handleId : true
})
} else if (isFunction(connectable)) {
return connectable(node, connectedEdges.value)
}
return isDef(connectable) ? connectable : nodesConnectable
})
onMounted(() => {
// set up handle bounds if they don't exist yet and the node has been initialized (i.e. the handle was added after the node has already been mounted)
until(() => node.initialized)
.toBe(true)
.then(() => {
const existingBounds = node.handleBounds[type.value]?.find((b) => b.id === handleId)
if (!vueFlowRef || existingBounds) return
const viewportNode = vueFlowRef.querySelector('.vue-flow__transformationpane')
if (!nodeEl || !handle.value || !viewportNode || !handleId) return
const nodeBounds = nodeEl.value.getBoundingClientRect()
const handleBounds = handle.value.getBoundingClientRect()
const style = window.getComputedStyle(viewportNode)
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform)
const nextBounds = {
id: handleId,
position,
x: (handleBounds.left - nodeBounds.left) / zoom,
y: (handleBounds.top - nodeBounds.top) / zoom,
...getDimensions(handle.value),
}
node.handleBounds[type.value] = [...(node.handleBounds[type.value] ?? []), nextBounds]
})
})
</script>
<script lang="ts">
export default {
name: 'Handle',
compatConfig: { MODE: 3 },
}
</script>
<template>
<div
ref="handle"
:data-id="`${nodeId}-${handleId}-${type}`"
:data-handleid="handleId"
:data-nodeid="nodeId"
:data-handlepos="position"
class="vue-flow__handle nodrag"
:class="[
`vue-flow__handle-${position}`,
`vue-flow__handle-${handleId}`,
{
source: type !== 'target',
target: type === 'target',
connectable: isConnectable,
connecting:
connectionStartHandle &&
connectionStartHandle.nodeId === nodeId &&
connectionStartHandle.handleId === handleId &&
connectionStartHandle.type === type,
},
]"
@mousedown="handlePointerDown"
@touchstart="handlePointerDown"
@click="handleClick"
>
<slot :id="id" />
</div>
</template>