chore(examples): add easy connect example
Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -118,6 +118,10 @@ export const routes: RouterOptions['routes'] = [
|
||||
path: '/rgb',
|
||||
component: () => import('./src/RGBFlow/RGBFlow.vue'),
|
||||
},
|
||||
{
|
||||
path: '/easy-connect',
|
||||
component: () => import('./src/EasyConnect/EasyConnect.vue'),
|
||||
},
|
||||
]
|
||||
|
||||
export const router = createRouter({
|
||||
|
||||
27
examples/vite/src/EasyConnect/CustomConnectionLine.vue
Normal file
27
examples/vite/src/EasyConnect/CustomConnectionLine.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import type { GraphNode } from '@vue-flow/core'
|
||||
import { getStraightPath } from '@vue-flow/core'
|
||||
import { computed } from '@vue/reactivity'
|
||||
const props = defineProps<{ sourceNode: GraphNode; sourceX: number; sourceY: number; targetX: number; targetY: number }>()
|
||||
|
||||
const edgePath = computed(() =>
|
||||
getStraightPath({
|
||||
...props,
|
||||
sourceX: props.sourceX - props.sourceNode.dimensions.width / 2,
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<g>
|
||||
<path
|
||||
:style="{
|
||||
strokeWidth: 3,
|
||||
stroke: 'black',
|
||||
}"
|
||||
fill="none"
|
||||
:d="edgePath[0]"
|
||||
/>
|
||||
<circle :cx="targetX" :cy="targetY" fill="black" :r="3" stroke="black" :stroke-width="1.5" />
|
||||
</g>
|
||||
</template>
|
||||
83
examples/vite/src/EasyConnect/CustomNode.vue
Normal file
83
examples/vite/src/EasyConnect/CustomNode.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<script lang="ts" setup>
|
||||
import { Handle, Position, useVueFlow } from '@vue-flow/core'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{ id: string }>()
|
||||
|
||||
const { connectionStartHandle } = useVueFlow()
|
||||
|
||||
const isTarget = computed(() => connectionStartHandle.value && connectionStartHandle.value.nodeId !== props.id)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="customNode">
|
||||
<div
|
||||
class="customNodeBody"
|
||||
:style="{
|
||||
borderStyle: isTarget ? 'dashed' : 'solid',
|
||||
backgroundColor: isTarget ? '#ffcce3' : '#ccd9f6',
|
||||
}"
|
||||
>
|
||||
<Handle class="targetHandle" style="z-index: 2" :position="Position.Right" type="source" connectable />
|
||||
<Handle class="targetHandle" :style="{ zIndex: isTarget ? 3 : 1 }" :position="Position.Left" type="target" connectable />
|
||||
{{ isTarget ? 'Drop here' : 'Drag to connect' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.customNodeBody {
|
||||
width: 150px;
|
||||
height: 80px;
|
||||
border: 3px solid black;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.customNode:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 50%;
|
||||
height: 20px;
|
||||
width: 40px;
|
||||
transform: translate(-50%, 0);
|
||||
background: #d6d5e6;
|
||||
z-index: 1000;
|
||||
line-height: 1;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 9px;
|
||||
border: 2px solid #222138;
|
||||
}
|
||||
|
||||
.sourceHandle {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 0;
|
||||
transform: none;
|
||||
border: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.targetHandle {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: blue;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 0;
|
||||
transform: none;
|
||||
border: none;
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
83
examples/vite/src/EasyConnect/EasyConnect.vue
Normal file
83
examples/vite/src/EasyConnect/EasyConnect.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { Background } from '@vue-flow/background'
|
||||
import { Controls } from '@vue-flow/controls'
|
||||
import { MiniMap } from '@vue-flow/minimap'
|
||||
import type { Elements } from '@vue-flow/core'
|
||||
import { MarkerType, VueFlow, useVueFlow } from '@vue-flow/core'
|
||||
import CustomNode from './CustomNode.vue'
|
||||
import CustomConnectionLine from './CustomConnectionLine.vue'
|
||||
import FloatingEdge from './FloatingEdge.vue'
|
||||
|
||||
const { onConnect, addEdges } = useVueFlow()
|
||||
|
||||
const defaultEdgeOptions = {
|
||||
style: { strokeWidth: 3, stroke: 'black' },
|
||||
type: 'floating',
|
||||
markerEnd: {
|
||||
type: MarkerType.ArrowClosed,
|
||||
color: 'black',
|
||||
},
|
||||
}
|
||||
|
||||
const elements = ref<Elements>([
|
||||
{
|
||||
id: '1',
|
||||
type: 'custom',
|
||||
position: { x: 0, y: 0 },
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'custom',
|
||||
position: { x: 250, y: 320 },
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: 'custom',
|
||||
position: { x: 40, y: 300 },
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
type: 'custom',
|
||||
position: { x: 300, y: 0 },
|
||||
},
|
||||
])
|
||||
|
||||
onConnect((params) => {
|
||||
console.log('connecting')
|
||||
|
||||
addEdges([params])
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="height: 100vh">
|
||||
<VueFlow
|
||||
v-model="elements"
|
||||
:elevate-nodes-on-select="false"
|
||||
class="vue-flow-basic-example"
|
||||
:default-zoom="1.5"
|
||||
:default-edge-options="defaultEdgeOptions"
|
||||
:min-zoom="0.2"
|
||||
:max-zoom="4"
|
||||
>
|
||||
<Background pattern-color="#aaa" :gap="8" />
|
||||
|
||||
<MiniMap />
|
||||
|
||||
<Controls />
|
||||
|
||||
<template #node-custom="props">
|
||||
<CustomNode :id="props.id" />
|
||||
</template>
|
||||
|
||||
<template #edge-floating="fProps">
|
||||
<FloatingEdge v-bind="fProps" />
|
||||
</template>
|
||||
|
||||
<template #connection-line="cProps">
|
||||
<CustomConnectionLine v-bind="cProps" />
|
||||
</template>
|
||||
</VueFlow>
|
||||
</div>
|
||||
</template>
|
||||
24
examples/vite/src/EasyConnect/FloatingEdge.vue
Normal file
24
examples/vite/src/EasyConnect/FloatingEdge.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import type { GraphNode } from '@vue-flow/core'
|
||||
import { getStraightPath } from '@vue-flow/core'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { getEdgeParams } from './utils'
|
||||
|
||||
const props = defineProps<{ id: string; sourceNode: GraphNode; targetNode: GraphNode; markerEnd: string; style: any }>()
|
||||
|
||||
const edgeParams = computed(() => getEdgeParams(props.sourceNode, props.targetNode))
|
||||
|
||||
const edgePath = computed(() =>
|
||||
getStraightPath({
|
||||
sourceX: edgeParams.value.sx,
|
||||
sourceY: edgeParams.value.sy,
|
||||
targetX: edgeParams.value.tx,
|
||||
targetY: edgeParams.value.ty,
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<path :id="id" class="vue-flow__edge-path" :d="edgePath[0]" :marker-end="markerEnd" :style="style" />
|
||||
</template>
|
||||
73
examples/vite/src/EasyConnect/utils.ts
Normal file
73
examples/vite/src/EasyConnect/utils.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import type { GraphNode, XYPosition } from '@vue-flow/core'
|
||||
import { Position } from '@vue-flow/core'
|
||||
|
||||
// this helper function returns the intersection point
|
||||
// of the line between the center of the intersectionNode and the target node
|
||||
function getNodeIntersection(intersectionNode: GraphNode, targetNode: GraphNode) {
|
||||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
||||
const {
|
||||
dimensions: { width: intersectionNodeWidth, height: intersectionNodeHeight },
|
||||
computedPosition: intersectionNodePosition,
|
||||
} = intersectionNode
|
||||
const targetPosition = targetNode.computedPosition
|
||||
|
||||
const w = intersectionNodeWidth / 2
|
||||
const h = intersectionNodeHeight / 2
|
||||
|
||||
const x2 = intersectionNodePosition.x + w
|
||||
const y2 = intersectionNodePosition.y + h
|
||||
const x1 = targetPosition.x + w
|
||||
const y1 = targetPosition.y + h
|
||||
|
||||
const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h)
|
||||
const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h)
|
||||
const a = 1 / (Math.abs(xx1) + Math.abs(yy1))
|
||||
const xx3 = a * xx1
|
||||
const yy3 = a * yy1
|
||||
const x = w * (xx3 + yy3) + x2
|
||||
const y = h * (-xx3 + yy3) + y2
|
||||
|
||||
return { x, y }
|
||||
}
|
||||
|
||||
// returns the position (top,right,bottom or right) passed node compared to the intersection point
|
||||
function getEdgePosition(node: GraphNode, intersectionPoint: XYPosition) {
|
||||
const n = { ...node.computedPosition, ...node }
|
||||
const nx = Math.round(n.x)
|
||||
const ny = Math.round(n.y)
|
||||
const px = Math.round(intersectionPoint.x)
|
||||
const py = Math.round(intersectionPoint.y)
|
||||
|
||||
if (px <= nx + 1) {
|
||||
return Position.Left
|
||||
}
|
||||
if (px >= nx + n.dimensions.width - 1) {
|
||||
return Position.Right
|
||||
}
|
||||
if (py <= ny + 1) {
|
||||
return Position.Top
|
||||
}
|
||||
if (py >= n.y + n.dimensions.height - 1) {
|
||||
return Position.Bottom
|
||||
}
|
||||
|
||||
return Position.Top
|
||||
}
|
||||
|
||||
// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
|
||||
export function getEdgeParams(source: GraphNode, target: GraphNode) {
|
||||
const sourceIntersectionPoint = getNodeIntersection(source, target)
|
||||
const targetIntersectionPoint = getNodeIntersection(target, source)
|
||||
|
||||
const sourcePos = getEdgePosition(source, sourceIntersectionPoint)
|
||||
const targetPos = getEdgePosition(target, targetIntersectionPoint)
|
||||
|
||||
return {
|
||||
sx: sourceIntersectionPoint.x,
|
||||
sy: sourceIntersectionPoint.y,
|
||||
tx: targetIntersectionPoint.x,
|
||||
ty: targetIntersectionPoint.y,
|
||||
sourcePos,
|
||||
targetPos,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user