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:
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
65
docs/examples/loopback/App.vue
Normal file
65
docs/examples/loopback/App.vue
Normal 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>
|
||||
113
docs/examples/loopback/LoopbackEdge.vue
Normal file
113
docs/examples/loopback/LoopbackEdge.vue
Normal 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>
|
||||
3
docs/examples/loopback/index.ts
Normal file
3
docs/examples/loopback/index.ts
Normal 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'
|
||||
26
docs/examples/loopback/style.css
Normal file
26
docs/examples/loopback/style.css
Normal 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;
|
||||
}
|
||||
@@ -245,6 +245,7 @@ export default defineConfigWithTheme<DefaultTheme.Config>({
|
||||
{ text: 'Custom Connection Line', link: '/examples/edges/connection-line' },
|
||||
{ text: 'Connection Validation', link: '/examples/edges/validation' },
|
||||
{ text: 'Connection Radius', link: '/examples/edges/connection-radius' },
|
||||
{ text: 'Loopback Edge', link: '/examples/edges/loopback' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
8
docs/src/examples/edges/loopback.md
Normal file
8
docs/src/examples/edges/loopback.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Loopback Edges
|
||||
|
||||
Loopback edges are edges that connect a node to itself. They are useful for representing self-referential relationships.
|
||||
This example walks you through creating loopback edges using Vue Flow and custom edges.
|
||||
|
||||
<div class="mt-6">
|
||||
<Repl example="loopback"></Repl>
|
||||
</div>
|
||||
Reference in New Issue
Block a user