refactor(core,edges): add div based edge label renderer

This commit is contained in:
braks
2022-11-04 22:10:40 +01:00
committed by Braks
parent 0f2bc10fd2
commit 43ff2a42e6
10 changed files with 168 additions and 107 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@vue-flow/core': patch
---
Teleport edge labels into separate svg container (fixes edges overlapping edge labels)
@@ -0,0 +1,23 @@
<script lang="ts" setup>
import { useVueFlow } from '../../composables'
const { viewportRef } = useVueFlow()
const teleportTarget = $computed(() => viewportRef.value?.getElementsByClassName('vue-flow__edge-labels')[0])
</script>
<script lang="ts">
export default {
name: 'EdgeLabelRenderer',
}
</script>
<template>
<svg>
<foreignObject height="0" width="0">
<Teleport :to="teleportTarget" :disabled="!teleportTarget">
<slot />
</Teleport>
</foreignObject>
</svg>
</template>
@@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { isString } from '@vueuse/core'
import type { EdgeTextProps } from '../../types/components' import type { EdgeTextProps } from '../../types/components'
import type { Rect as RectType } from '../../types' import type { Rect as RectType } from '../../types'
@@ -55,10 +56,8 @@ export default {
/> />
<text v-bind="$attrs" ref="el" class="vue-flow__edge-text" :y="box.height / 2" dy="0.3em" :style="labelStyle"> <text v-bind="$attrs" ref="el" class="vue-flow__edge-text" :y="box.height / 2" dy="0.3em" :style="labelStyle">
<slot> <slot>
<component :is="label" v-if="typeof label !== 'string' && typeof label" /> <component :is="label" v-if="!isString(label)" />
<template v-else> <template v-else> {{ label }} </template>
{{ label }}
</template>
</slot> </slot>
</text> </text>
</g> </g>
@@ -7,3 +7,4 @@ export { default as StraightEdge } from './StraightEdge'
export { default as EdgeAnchor } from './EdgeAnchor' export { default as EdgeAnchor } from './EdgeAnchor'
export { default as EdgeText } from './EdgeText.vue' export { default as EdgeText } from './EdgeText.vue'
export { default as EdgeWrapper } from './Wrapper' export { default as EdgeWrapper } from './Wrapper'
export { default as EdgeLabelRenderer } from './EdgeLabelRenderer.vue'
@@ -127,24 +127,27 @@ export default {
</script> </script>
<template> <template>
<svg v-for="group of groups" :key="group.level" class="vue-flow__edges vue-flow__container" :style="`z-index: ${group.level}`"> <template v-for="group of groups" :key="group.level">
<MarkerDefinitions v-if="group.isMaxLevel" /> <svg class="vue-flow__edges vue-flow__container" :style="`z-index: ${group.level}`">
<g> <MarkerDefinitions v-if="group.isMaxLevel" />
<EdgeWrapper <g>
v-for="edge of group.edges" <EdgeWrapper
:id="edge.id" v-for="edge of group.edges"
:key="edge.id" :id="edge.id"
:edge="edge" :key="edge.id"
:type="getType(edge.type, edge.template)" :edge="edge"
:name="edge.type || 'default'" :type="getType(edge.type, edge.template)"
:source-node="getNode(edge.source)" :name="edge.type || 'default'"
:target-node="getNode(edge.target)" :source-node="getNode(edge.source)"
:selectable="selectable(edge.selectable)" :target-node="getNode(edge.target)"
:updatable="updatable(edge.updatable)" :selectable="selectable(edge.selectable)"
:class="getClass(edge)" :updatable="updatable(edge.updatable)"
/> :class="getClass(edge)"
</g> />
</svg> </g>
</svg>
</template>
<svg v-if="connectionLineVisible && !!sourceNode" class="vue-flow__edges vue-flow__connectionline vue-flow__container"> <svg v-if="connectionLineVisible && !!sourceNode" class="vue-flow__edges vue-flow__connectionline vue-flow__container">
<ConnectionLine :source-node="sourceNode" /> <ConnectionLine :source-node="sourceNode" />
</svg> </svg>
@@ -53,6 +53,9 @@ export default {
:style="{ transform: `translate(${viewport.x}px,${viewport.y}px) scale(${viewport.zoom})`, opacity: ready ? undefined : 0 }" :style="{ transform: `translate(${viewport.x}px,${viewport.y}px) scale(${viewport.zoom})`, opacity: ready ? undefined : 0 }"
> >
<EdgeRenderer /> <EdgeRenderer />
<div class="vue-flow__edge-labels" />
<NodeRenderer /> <NodeRenderer />
<slot /> <slot />
</div> </div>
+10 -1
View File
@@ -7,7 +7,16 @@ export { default as VueFlow } from './container/VueFlow/VueFlow.vue'
export { default as Handle } from './components/Handle/Handle.vue' export { default as Handle } from './components/Handle/Handle.vue'
export { StraightEdge, StepEdge, BezierEdge, SimpleBezierEdge, SmoothStepEdge, BaseEdge, EdgeText } from './components/Edges' export {
StraightEdge,
StepEdge,
BezierEdge,
SimpleBezierEdge,
SmoothStepEdge,
BaseEdge,
EdgeText,
EdgeLabelRenderer,
} from './components/Edges'
export { getBezierPath, getSimpleBezierPath, getSmoothStepPath, getStraightPath } from './components/Edges/utils' export { getBezierPath, getSimpleBezierPath, getSmoothStepPath, getStraightPath } from './components/Edges/utils'
+7
View File
@@ -39,6 +39,13 @@
z-index: 5; z-index: 5;
} }
.vue-flow__edge-labels {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.vue-flow__nodesselection-rect:focus, .vue-flow__nodesselection-rect:focus,
.vue-flow__nodesselection-rect:focus-visible { .vue-flow__nodesselection-rect:focus-visible {
outline: none; outline: none;
+1 -1
View File
@@ -106,7 +106,7 @@ export const parseEdge = (edge: Edge, defaults?: Partial<GraphEdge>): GraphEdge
selectable: edge.selectable, selectable: edge.selectable,
data: shallowReactive(edge.data || {}), data: shallowReactive(edge.data || {}),
events: shallowReactive(edge.events || {}), events: shallowReactive(edge.events || {}),
label: edge.label && !isString(edge.label) ? markRaw(edge.label) : undefined, label: edge.label && !isString(edge.label) ? markRaw(edge.label) : edge.label,
...defaults, ...defaults,
} as GraphEdge) } as GraphEdge)
: defaults : defaults
+94 -83
View File
@@ -17,9 +17,9 @@
z-index: 1; z-index: 1;
cursor: grab; cursor: grab;
&.dragging { &.dragging {
cursor: grabbing; cursor: grabbing;
} }
} }
.vue-flow__transformationpane { .vue-flow__transformationpane {
@@ -36,6 +36,13 @@
z-index: 5; z-index: 5;
} }
.vue-flow__edge-labels {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.vue-flow__nodesselection-rect:focus, .vue-flow__nodesselection-rect:focus,
.vue-flow__nodesselection-rect:focus-visible { .vue-flow__nodesselection-rect:focus-visible {
outline: none; outline: none;
@@ -57,48 +64,48 @@
pointer-events: visibleStroke; pointer-events: visibleStroke;
cursor: pointer; cursor: pointer;
&.animated path { &.animated path {
stroke-dasharray: 5; stroke-dasharray: 5;
animation: dashdraw 0.5s linear infinite; animation: dashdraw 0.5s linear infinite;
} }
&.inactive { &.inactive {
pointer-events: none; pointer-events: none;
} }
&.selected, &.selected,
&:focus, &:focus,
&:focus-visible { &:focus-visible {
outline: none; outline: none;
} }
&.selected .vue-flow__edge-path, &.selected .vue-flow__edge-path,
&:focus .vue-flow__edge-path, &:focus .vue-flow__edge-path,
&:focus-visible .vue-flow__edge-path { &:focus-visible .vue-flow__edge-path {
stroke: #555; stroke: #555;
} }
&-textwrapper { &-textwrapper {
pointer-events: all; pointer-events: all;
} }
&-textbg { &-textbg {
fill: white; fill: white;
} }
.vue-flow__edge-text { &-text {
pointer-events: none; pointer-events: none;
user-select: none; user-select: none;
} }
} }
.vue-flow__connection { .vue-flow__connection {
pointer-events: none; pointer-events: none;
&.animated { &.animated {
stroke-dasharray: 5; stroke-dasharray: 5;
animation: dashdraw 0.5s linear infinite; animation: dashdraw 0.5s linear infinite;
} }
} }
.vue-flow__connectionline { .vue-flow__connectionline {
@@ -118,9 +125,9 @@
box-sizing: border-box; box-sizing: border-box;
cursor: grab; cursor: grab;
&.dragging { &.dragging {
cursor: grabbing; cursor: grabbing;
} }
} }
.vue-flow__nodesselection { .vue-flow__nodesselection {
@@ -128,11 +135,15 @@
transform-origin: left top; transform-origin: left top;
pointer-events: none; pointer-events: none;
&-rect { &-rect {
position: absolute; position: absolute;
pointer-events: all; pointer-events: all;
cursor: grab; cursor: grab;
}
&.dragging {
cursor: grabbing;
}
}
} }
.vue-flow__handle { .vue-flow__handle {
@@ -141,35 +152,35 @@
min-width: 5px; min-width: 5px;
min-height: 5px; min-height: 5px;
&.connectable { &.connectable {
pointer-events: all; pointer-events: all;
cursor: crosshair; cursor: crosshair;
} }
&-bottom { &-bottom {
top: auto; top: auto;
left: 50%; left: 50%;
bottom: -4px; bottom: -4px;
transform: translate(-50%, 0); transform: translate(-50%, 0);
} }
&-top { &-top {
left: 50%; left: 50%;
top: -4px; top: -4px;
transform: translate(-50%, 0); transform: translate(-50%, 0);
} }
&-left { &-left {
top: 50%; top: 50%;
left: -4px; left: -4px;
transform: translate(0, -50%); transform: translate(0, -50%);
} }
&-right { &-right {
right: -4px; right: -4px;
top: 50%; top: 50%;
transform: translate(0, -50%); transform: translate(0, -50%);
} }
} }
.vue-flow__edgeupdater { .vue-flow__edgeupdater {
@@ -182,26 +193,26 @@
z-index: 5; z-index: 5;
margin: 15px; margin: 15px;
&.top { &.top {
top: 0; top: 0;
} }
&.bottom { &.bottom {
bottom: 0; bottom: 0;
} }
&.left { &.left {
left: 0; left: 0;
} }
&.right { &.right {
right: 0; right: 0;
} }
&.center { &.center {
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }
} }
@keyframes dashdraw { @keyframes dashdraw {