docs(examples): add loopback edge example (#1708)

docs(examples): add loopback example

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
Braks
2024-12-03 15:12:24 +01:00
committed by GitHub
parent 0d246804b9
commit 4d0978343f
7 changed files with 222 additions and 1 deletions

View File

@@ -20,7 +20,7 @@ import { NodeResizerApp, ResizableNode } from './node-resizer'
import { ToolbarApp, ToolbarNode } from './node-toolbar'
import { LayoutApp, LayoutEdge, LayoutElements, LayoutIcon, LayoutNode, useLayout, useRunProcess, useShuffle } from './layout'
import { SimpleLayoutApp, SimpleLayoutElements, SimpleLayoutIcon, useSimpleLayout } from './layout-simple'
import { LoopbackApp, LoopbackCSS, LoopbackEdge } from './loopback'
import { MathApp, MathCSS, MathElements, MathIcon, MathOperatorNode, MathResultNode, MathValueNode } from './math'
import { ConfirmApp, ConfirmDialog, useDialog } from './confirm-delete'
@@ -165,4 +165,9 @@ export const exampleImports = {
'Dialog.vue': ConfirmDialog,
'useDialog.js': useDialog,
},
loopback: {
'App.vue': LoopbackApp,
'LoopbackEdge.vue': LoopbackEdge,
'style.css': LoopbackCSS,
},
}

View File

@@ -0,0 +1,65 @@
<script setup>
import { ref } from 'vue'
import { Background } from '@vue-flow/background'
import { Panel, Position, VueFlow, useVueFlow } from '@vue-flow/core'
import LoopbackEdge from './LoopbackEdge.vue'
const { updateEdgeData, updateNode } = useVueFlow()
const nodes = ref([
{
id: '1',
position: { x: 0, y: 0 },
data: { label: 'I connect to myself' },
},
])
const pathType = ref('bezier')
const isHorizontal = ref(false)
const edges = ref([{ id: 'e1-1', type: 'loopback', source: '1', target: '1', data: { pathType: pathType.value } }])
</script>
<template>
<VueFlow :nodes="nodes" :edges="edges" fit-view-on-init>
<template #edge-loopback="customEdgeProps">
<LoopbackEdge
:id="customEdgeProps.id"
:source-x="customEdgeProps.sourceX"
:source-y="customEdgeProps.sourceY"
:target-x="customEdgeProps.targetX"
:target-y="customEdgeProps.targetY"
:source-position="customEdgeProps.sourcePosition"
:target-position="customEdgeProps.targetPosition"
:source-node="customEdgeProps.sourceNode"
:target-node="customEdgeProps.targetNode"
:data="customEdgeProps.data"
/>
</template>
<Background />
<Panel>
<select v-model="pathType" @change="updateEdgeData('e1-1', { pathType })">
<option value="bezier">Bezier</option>
<option value="smoothstep">Smoothstep</option>
</select>
<label for="is-horizontal"
>{{ isHorizontal ? 'Vertical' : 'Horizontal' }}
<input
id="is-horizontal"
v-model="isHorizontal"
type="checkbox"
@change="
updateNode('1', {
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
targetPosition: isHorizontal ? Position.Left : Position.Top,
})
"
/>
</label>
</Panel>
</VueFlow>
</template>

View File

@@ -0,0 +1,113 @@
<script setup>
import { Position, getBezierPath, getSmoothStepPath } from '@vue-flow/core'
import { computed } from 'vue'
const props = defineProps({
id: {
type: String,
required: true,
},
sourceX: {
type: Number,
required: true,
},
sourceY: {
type: Number,
required: true,
},
targetX: {
type: Number,
required: true,
},
targetY: {
type: Number,
required: true,
},
sourcePosition: {
type: String,
required: true,
},
targetPosition: {
type: String,
required: true,
},
sourceNode: {
type: Object,
required: true,
},
targetNode: {
type: Object,
required: true,
},
data: {
type: Object,
required: true,
},
})
const path = computed(() => {
if (props.sourceNode && props.targetNode) {
if (props.data.pathType === 'bezier') {
if (
(props.sourcePosition === Position.Bottom && props.targetPosition === Position.Top) ||
(props.sourcePosition === Position.Top && props.targetPosition === Position.Bottom)
) {
// for horizontal loopback edges
const radiusX = 60
const radiusY = props.sourceY - props.targetY
return [`M ${props.sourceX} ${props.sourceY} A ${radiusX} ${radiusY} 0 1 0 ${props.targetX} ${props.targetY}`]
} else if (
(props.sourcePosition === Position.Left && props.targetPosition === Position.Right) ||
(props.sourcePosition === Position.Right && props.targetPosition === Position.Left)
) {
// for vertical loopback edges
const radiusX = (props.sourceX - props.targetX) * 0.6
const radiusY = 50
return [`M ${props.sourceX} ${props.sourceY} A ${radiusX} ${radiusY} 0 1 0 ${props.targetX} ${props.targetY}`]
}
} else if (props.data.pathType === 'smoothstep') {
let centerX, centerY
if (props.sourceNode === props.targetNode) {
if (
(props.sourcePosition === Position.Bottom && props.targetPosition === Position.Top) ||
(props.sourcePosition === Position.Top && props.targetPosition === Position.Bottom)
) {
const source = props.sourceNode
centerX = props.sourceX - 40 - source.dimensions.width / 2
centerY = (props.sourceY + props.targetY) / 2
} else if (
(props.sourcePosition === Position.Left && props.targetPosition === Position.Right) ||
(props.sourcePosition === Position.Right && props.targetPosition === Position.Left)
) {
const source = props.sourceNode
centerX = (props.sourceX + props.targetX) / 2
centerY = props.sourceY + 40 + source.dimensions.height / 2
}
}
return getSmoothStepPath({
sourcePosition: props.sourcePosition,
targetPosition: props.targetPosition,
centerX,
centerY,
...props,
})
}
}
// default to bezier path
return getBezierPath(props)
})
</script>
<script>
export default {
inheritAttrs: false,
}
</script>
<template>
<path :d="path[0]" fill="none" stroke="black" />
</template>

View File

@@ -0,0 +1,3 @@
export { default as LoopbackApp } from './App.vue?raw'
export { default as LoopbackEdge } from './LoopbackEdge.vue?raw'
export { default as LoopbackCSS } from './style.css?inline'

View File

@@ -0,0 +1,26 @@
.vue-flow__panel {
background-color: #2d3748;
padding: 10px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
color: white;
display: flex;
flex-direction: column;
gap: 1rem;
font-size: 12px;
font-weight: 600;
width: 100px;
}
.vue-flow__panel label {
display: flex;
justify-content: space-between;
align-items: center;
gap: 5px;
cursor: pointer;
}
.vue-flow__panel label input {
cursor: pointer;
}