fix(edges): Remove orphaned edges

# What's changed?

* Remove source/targetNode properties from GraphEdge type, the source/target will be fetched with getters if necessary
* Remove edges when they have no existing source or target nodes that can be found in the state
* Add nodes to custom edge test
This commit is contained in:
Braks
2022-04-21 10:47:53 +02:00
parent 9a5b5465b6
commit ee38f4af9b
10 changed files with 111 additions and 73 deletions
+5 -8
View File
@@ -16,8 +16,7 @@ import {
ComputedGetters,
} from '~/types'
import {
applyNodeChanges as applyNodes,
applyEdgeChanges as applyEdges,
applyChanges,
connectionExists,
createPositionChange,
createSelectionChange,
@@ -241,6 +240,7 @@ export default (state: State, getters: ComputedGetters): Actions => {
const missingTarget = !targetNode || typeof targetNode === 'undefined'
if (missingSource) console.warn(`[vueflow]: Couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
if (missingTarget) console.warn(`[vueflow]: Couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
if (missingSource || missingTarget) return res
const storedEdge = getters.getEdge.value(edge.id)
@@ -249,8 +249,6 @@ export default (state: State, getters: ComputedGetters): Actions => {
...state.defaultEdgeOptions,
...storedEdge,
}),
sourceNode,
targetNode,
})
return res
@@ -284,12 +282,11 @@ export default (state: State, getters: ComputedGetters): Actions => {
const missingTarget = !targetNode || typeof targetNode === 'undefined'
if (missingSource) console.warn(`[vueflow]: Couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
if (missingTarget) console.warn(`[vueflow]: Couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
if (missingTarget || missingSource) return
state.edges.push({
...state.defaultEdgeOptions,
...edge,
sourceNode,
targetNode,
})
}
})
@@ -298,9 +295,9 @@ export default (state: State, getters: ComputedGetters): Actions => {
const updateEdge: Actions['updateEdge'] = (oldEdge, newConnection) =>
updateEdgeAction(oldEdge, newConnection, state.edges, addEdges)
const applyNodeChanges: Actions['applyNodeChanges'] = (changes) => applyNodes(changes, state.nodes)
const applyNodeChanges: Actions['applyNodeChanges'] = (changes) => applyChanges(changes, state.nodes, addNodes)
const applyEdgeChanges: Actions['applyEdgeChanges'] = (changes) => applyEdges(changes, state.edges)
const applyEdgeChanges: Actions['applyEdgeChanges'] = (changes) => applyChanges(changes, state.edges, addEdges)
const setState: Actions['setState'] = (options) => {
const skip = ['modelValue', 'nodes', 'edges', 'maxZoom', 'minZoom', 'translateExtent']
+47 -28
View File
@@ -3,6 +3,11 @@ import { State, GraphEdge, GraphNode, ComputedGetters } from '~/types'
import { getNodesInside, isEdgeVisible } from '~/utils'
export default (state: State): ComputedGetters => {
const nodeIds = computed(() => state.nodes.map((n) => n.id))
const edgeIds = computed(() => state.edges.map((e) => e.id))
const getNode: ComputedGetters['getNode'] = computed(() => (id: string) => state.nodes[nodeIds.value.indexOf(id)])
const getEdge: ComputedGetters['getEdge'] = computed(() => (id: string) => state.edges[edgeIds.value.indexOf(id)])
const getEdgeTypes = computed(() => {
const edgeTypes: Record<string, any> = {
...defaultEdgeTypes,
@@ -41,31 +46,50 @@ export default (state: State): ComputedGetters => {
: nodes ?? []
})
const edgeHidden = (e: GraphEdge) => {
const source = getNode.value(e.source)
const target = getNode.value(e.target)
if (!source || !target) {
console.warn(`[vue-flow]: Orphaned edge ${e.id} will be removed.`)
state.edges.splice(state.edges.indexOf(e), 1)
return true
}
return (
!e.hidden &&
target &&
!target.hidden &&
source &&
!source.hidden &&
source.dimensions.width &&
source.dimensions.height &&
target.dimensions.width &&
target.dimensions.height
)
}
const getEdges = computed<GraphEdge[]>(() => {
if (!state.onlyRenderVisibleElements)
return state.edges.filter((e) => !e.hidden && e.targetNode && !e.targetNode.hidden && e.sourceNode && !e.sourceNode.hidden)
else
return state.edges.filter(
(e) =>
!e.hidden &&
e.sourceNode.dimensions.width &&
e.sourceNode.dimensions.height &&
e.targetNode.dimensions.width &&
e.targetNode.dimensions.height &&
!e.targetNode.hidden &&
!e.sourceNode.hidden &&
isEdgeVisible({
sourcePos: e.sourceNode.computedPosition || { x: 0, y: 0 },
targetPos: e.targetNode.computedPosition || { x: 0, y: 0 },
sourceWidth: e.sourceNode.dimensions.width,
sourceHeight: e.sourceNode.dimensions.height,
targetWidth: e.targetNode.dimensions.width,
targetHeight: e.targetNode.dimensions.height,
width: state.dimensions.width,
height: state.dimensions.height,
viewport: state.viewport,
}),
if (!state.onlyRenderVisibleElements) return state.edges.filter(edgeHidden)
return state.edges.filter((e) => {
const source = getNode.value(e.source)!
const target = getNode.value(e.target)!
return (
edgeHidden(e) &&
isEdgeVisible({
sourcePos: source.computedPosition || { x: 0, y: 0 },
targetPos: target.computedPosition || { x: 0, y: 0 },
sourceWidth: source.dimensions.width,
sourceHeight: source.dimensions.height,
targetWidth: target.dimensions.width,
targetHeight: target.dimensions.height,
width: state.dimensions.width,
height: state.dimensions.height,
viewport: state.viewport,
})
)
})
})
const getSelectedNodes: ComputedGetters['getSelectedNodes'] = computed(() => state.nodes.filter((n) => n.selected))
@@ -75,11 +99,6 @@ export default (state: State): ComputedGetters => {
...(getSelectedEdges.value ?? []),
])
const nodeIds = computed(() => state.nodes.map((n) => n.id))
const edgeIds = computed(() => state.edges.map((e) => e.id))
const getNode: ComputedGetters['getNode'] = computed(() => (id: string) => state.nodes[nodeIds.value.indexOf(id)])
const getEdge: ComputedGetters['getEdge'] = computed(() => (id: string) => state.edges[edgeIds.value.indexOf(id)])
return {
getNode,
getEdge,