|
|
|
@ -1,12 +1,12 @@ |
|
|
|
<template> |
|
|
|
<div style="width:100%;height:100%"> |
|
|
|
<div ref="canvasEl" style="width:100%;height:100%;outline:none" tabindex="0" @keydown="onKeyDown" @click.self="focusCanvas"> |
|
|
|
<VueFlow |
|
|
|
v-if="ready" |
|
|
|
:nodes="props.nodes" |
|
|
|
:edges="props.edges" |
|
|
|
@nodes-change="onNodesChange" |
|
|
|
@edges-change="onEdgesChange" |
|
|
|
@node-click="(e: any) => $emit('node-click', e)" |
|
|
|
@node-click="(e: any) => { onNodeClickLocal(e); $emit('node-click', e) }" |
|
|
|
@pane-click="onPaneClickLocal" |
|
|
|
@connect="handleConnect" |
|
|
|
@drop="(e: any) => $emit('drop', e)" |
|
|
|
@ -24,6 +24,25 @@ |
|
|
|
zoomable |
|
|
|
/> |
|
|
|
|
|
|
|
<template #edge-default="{ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data }"> |
|
|
|
<BaseEdge |
|
|
|
:id="id" |
|
|
|
:path="getSmoothStepPath({ |
|
|
|
sourceX, |
|
|
|
sourceY, |
|
|
|
targetX, |
|
|
|
targetY, |
|
|
|
sourcePosition, |
|
|
|
targetPosition, |
|
|
|
borderRadius: 8, |
|
|
|
})" |
|
|
|
:style="data?.style || {}" |
|
|
|
:marker-end="data?.markerEnd || ''" |
|
|
|
:class="{ 'selected-edge': selectedEdgeId === id }" |
|
|
|
@click.stop="onEdgeClick({ edge: { id, ...data } })" |
|
|
|
/> |
|
|
|
</template> |
|
|
|
|
|
|
|
<template #node-custom="nodeProps"> |
|
|
|
<FlowNode |
|
|
|
:id="nodeProps.id" |
|
|
|
@ -38,7 +57,7 @@ |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
import { ref, watch } from 'vue' |
|
|
|
import { MarkerType } from '@vue-flow/core' |
|
|
|
import { MarkerType, BaseEdge, getSmoothStepPath } from '@vue-flow/core' |
|
|
|
import { VueFlow } from '@vue-flow/core' |
|
|
|
import { Background } from '@vue-flow/background' |
|
|
|
import { Controls } from '@vue-flow/controls' |
|
|
|
@ -62,7 +81,13 @@ const emit = defineEmits([ |
|
|
|
|
|
|
|
const ready = ref(true) |
|
|
|
const selectedEdgeId = ref<string | null>(null) |
|
|
|
const selectedNodeId = ref<string | null>(null) |
|
|
|
let skipNextNodesChange = false |
|
|
|
const canvasEl = ref<HTMLElement | null>(null) |
|
|
|
|
|
|
|
function focusCanvas() { |
|
|
|
canvasEl.value?.focus() |
|
|
|
} |
|
|
|
|
|
|
|
watch(() => props.nodes, () => { |
|
|
|
skipNextNodesChange = true |
|
|
|
@ -99,13 +124,37 @@ function onEdgesChange(changes: any[]) { |
|
|
|
|
|
|
|
function onEdgeClick({ edge }: any) { |
|
|
|
selectedEdgeId.value = edge.id |
|
|
|
selectedNodeId.value = null |
|
|
|
focusCanvas() |
|
|
|
} |
|
|
|
|
|
|
|
function onNodeClickLocal({ node }: any) { |
|
|
|
selectedNodeId.value = node.id |
|
|
|
selectedEdgeId.value = null |
|
|
|
focusCanvas() |
|
|
|
} |
|
|
|
|
|
|
|
function onPaneClickLocal() { |
|
|
|
selectedEdgeId.value = null |
|
|
|
selectedNodeId.value = null |
|
|
|
emit('pane-click') |
|
|
|
} |
|
|
|
|
|
|
|
function onKeyDown(event: KeyboardEvent) { |
|
|
|
if (event.key === 'Delete' || event.key === 'Backspace') { |
|
|
|
event.preventDefault() |
|
|
|
|
|
|
|
if (selectedEdgeId.value) { |
|
|
|
const updatedEdges = props.edges.filter((e: any) => e.id !== selectedEdgeId.value) |
|
|
|
emit('update:edges', updatedEdges) |
|
|
|
selectedEdgeId.value = null |
|
|
|
} else if (selectedNodeId.value) { |
|
|
|
emit('node-delete', selectedNodeId.value) |
|
|
|
selectedNodeId.value = null |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function onContextMenu(event: MouseEvent) { |
|
|
|
const target = event.target as HTMLElement |
|
|
|
const edgeEl = target.closest('.vue-flow__edge') |
|
|
|
@ -149,4 +198,11 @@ defineExpose({ |
|
|
|
get canRedo() { return false }, |
|
|
|
getSnapshot, |
|
|
|
}) |
|
|
|
</script> |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
:deep(.selected-edge path) { |
|
|
|
stroke-width: 4; |
|
|
|
filter: drop-shadow(0 0 4px rgba(64, 158, 255, 0.6)); |
|
|
|
} |
|
|
|
</style> |