-
-
Notifications
You must be signed in to change notification settings - Fork 209
/
NodeToolbar.vue
91 lines (74 loc) 路 2.72 KB
/
NodeToolbar.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
<script lang="ts" setup>
import { computed, inject } from 'vue'
import type { GraphNode, Rect, ViewportTransform } from '@vue-flow/core'
import { NodeIdInjection, Position, getRectOfNodes, useVueFlow } from '@vue-flow/core'
import type { CSSProperties } from 'vue'
import type { NodeToolbarProps } from './types'
const props = withDefaults(defineProps<NodeToolbarProps>(), {
position: Position.Top,
offset: 10,
})
const contextNodeId = inject(NodeIdInjection, null)
const { viewportRef, viewport, getSelectedNodes, findNode } = useVueFlow()
function getTransform(nodeRect: Rect, transform: ViewportTransform, position: Position, offset: number): string {
let xPos = (nodeRect.x + nodeRect.width / 2) * transform.zoom + transform.x
let yPos = nodeRect.y * transform.zoom + transform.y - offset
let xShift = -50
let yShift = -100
switch (position) {
case Position.Right:
xPos = (nodeRect.x + nodeRect.width) * transform.zoom + transform.x + offset
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
xShift = 0
yShift = -50
break
case Position.Bottom:
yPos = (nodeRect.y + nodeRect.height) * transform.zoom + transform.y + offset
yShift = 0
break
case Position.Left:
xPos = nodeRect.x * transform.zoom + transform.x - offset
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
xShift = -100
yShift = -50
break
}
return `translate(${xPos}px, ${yPos}px) translate(${xShift}%, ${yShift}%)`
}
const nodes = computed(() => {
const nodeIds = Array.isArray(props.nodeId) ? props.nodeId : [props.nodeId || contextNodeId || '']
return nodeIds.reduce<GraphNode[]>((acc, id) => {
const node = findNode(id)
if (node) {
acc.push(node)
}
return acc
}, [] as GraphNode[])
})
const isActive = computed(() =>
typeof props.isVisible === 'boolean'
? props.isVisible
: nodes.value.length === 1 && nodes.value[0].selected && getSelectedNodes.value.length === 1,
)
const nodeRect = computed<Rect>(() => getRectOfNodes(nodes.value))
const zIndex = computed<number>(() => Math.max(...nodes.value.map((node) => (node.computedPosition.z || 1) + 1)))
const wrapperStyle = computed<CSSProperties>(() => ({
position: 'absolute',
transform: getTransform(nodeRect.value, viewport.value, props.position, props.offset),
zIndex: zIndex.value,
}))
</script>
<script lang="ts">
export default {
name: 'NodeToolbar',
compatConfig: { MODE: 3 },
inheritAttrs: false,
}
</script>
<template>
<Teleport :to="viewportRef" :disabled="!viewportRef">
<div v-if="isActive && nodes.length" v-bind="$attrs" :style="wrapperStyle" class="vue-flow__node-toolbar">
<slot />
</div>
</Teleport>
</template>