refactor(options)!: remove worker and store option

* either unnecessary, broken or needs more work

Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
Braks
2021-11-24 14:07:09 +01:00
parent 725dc8acb7
commit 708fa1f970
8 changed files with 83 additions and 206 deletions

View File

@@ -17,7 +17,7 @@ interface CustomEdgeProps<T = any> extends EdgeProps<T> {
const props = defineProps<CustomEdgeProps>()
const store = useVueFlow()
const onEdgeClick = (evt: Event, id: string) => {
const edge = store.edges.find((edge) => edge.id === id)
const edge = store.getEdges.find((edge) => edge.id === id)
if (edge) {
store.hooks.elementsRemove.trigger([edge])
}

View File

@@ -51,7 +51,6 @@ const updateElements = () => {
<template>
<VueFlow
v-model="elements"
:worker="true"
:loading="{ label: 'Loading...', transition: { name: 'fade', mode: 'out-in' } }"
@load="onLoad"
@elementsRemove="onElementsRemove"

View File

@@ -11,7 +11,6 @@ import {
NodeExtent,
NodeTypes,
EdgeTypes,
FlowStore,
FlowState,
FlowInstance,
Loading,
@@ -24,11 +23,9 @@ import EdgeRenderer from '../EdgeRenderer/EdgeRenderer.vue'
import LoadingIndicator from '../../components/Loading/LoadingIndicator.vue'
import { createHooks, initFlow, useWindow, useZoomPanHelper } from '../../composables'
import { onLoadGetElements, onLoadProject, onLoadToObject } from '../../utils'
import microDiff from 'microdiff'
interface FlowProps extends FlowOptions {
id?: string
store?: FlowStore
modelValue?: Elements
nodeTypes?: NodeTypes
edgeTypes?: EdgeTypes
@@ -65,10 +62,9 @@ interface FlowProps extends FlowOptions {
edgeUpdaterRadius?: number
storageKey?: string
loading?: Loading
worker?: boolean
}
const emit = defineEmits([...Object.keys(createHooks()), 'update:elements', 'update:modelValue'])
const emit = defineEmits([...Object.keys(createHooks()), 'update:modelValue'])
const props = withDefaults(defineProps<FlowProps>(), {
modelValue: () => [],
@@ -110,7 +106,7 @@ const props = withDefaults(defineProps<FlowProps>(), {
loading: false,
worker: false,
})
const store = initFlow(emit, typeof props.storageKey === 'string' ? props.storageKey : props.id, props.store)
const store = initFlow(emit, typeof props.storageKey === 'string' ? props.storageKey : props.id)
const elements = useVModel(props, 'modelValue', emit)
// if there are preloaded elements we overwrite the current elements with the stored ones
@@ -139,7 +135,7 @@ onBeforeUnmount(() => store?.$dispose())
invoke(async () => {
init(options)
await store.setElements(elements.value)
store.setElements(elements.value)
store.isReady = true
// if ssr we can't wait for dimensions, they'll never really exist
@@ -167,15 +163,20 @@ invoke(async () => {
)
})
watch(props.modelValue, (val) => {
const diff = microDiff(val, store.elements, { cyclesFix: false })
if (diff.length) store.setElements(val)
}, { flush: 'post' })
watch(elements, (val) =>{
const diff = microDiff(val, store.elements, { cyclesFix: false })
if (diff.length) store.setElements(val)
}, { flush: 'post' })
watch(store.elements, (val) => (elements.value = val), { flush: 'post' })
watch(
() => props.modelValue.length,
() => store.setElements(elements.value),
)
const { pause, resume } = pausableWatch(elements, store.setElements, { flush: 'post' })
watch(
() => store.elements,
(val) => {
pause()
elements.value = val
nextTick(resume)
},
{ flush: 'post', deep: true },
)
const transitionName = computed(() => {
let name = ''
@@ -186,11 +187,6 @@ const transitionName = computed(() => {
return name
})
</script>
<script lang="ts">
export default {
name: 'VueFlow'
}
</script>
<template>
<div class="vue-flow">
<Transition :key="`vue-flow-transition-${store.$id}`" :name="transitionName">

View File

@@ -1,21 +1,17 @@
import microDiff from 'microdiff'
import { setActivePinia, createPinia, defineStore, StoreDefinition, acceptHMRUpdate } from 'pinia'
import { FlowState, FlowActions, Elements, FlowGetters, GraphNode, NextElements, GraphEdge } from '~/types'
import { FlowState, FlowActions, Elements, FlowGetters, GraphNode, GraphEdge, Edge } from '~/types'
import {
clampPosition,
getConnectedEdges,
getNodesInside,
getRectOfNodes,
parseElements,
defaultNodeTypes,
defaultEdgeTypes,
deepUnref,
isGraphNode,
getSourceTargetNodes,
isEdge,
isGraphEdge,
} from '~/utils'
import parseElementsWorker from '~/workers/parseElements'
const pinia = createPinia()
@@ -43,31 +39,31 @@ export default (id: string, preloadedState: FlowState) => {
return nodeTypes
},
getNodes(): GraphNode[] {
const nodes = this.elements.filter(isGraphNode)
const n = this.onlyRenderVisibleElements
? nodes &&
getNodesInside(
nodes,
{
x: 0,
y: 0,
width: this.dimensions.width,
height: this.dimensions.height,
},
this.transform,
true,
)
: nodes
return n.filter((node) => !node.isHidden) ?? []
if (this.isReady) {
const nodes = this.elements.filter((n) => isGraphNode(n) && !n.isHidden) as GraphNode[]
return this.onlyRenderVisibleElements
? nodes &&
getNodesInside(
nodes,
{
x: 0,
y: 0,
width: this.dimensions.width,
height: this.dimensions.height,
},
this.transform,
true,
)
: nodes ?? []
}
return []
},
getEdges(): GraphEdge[] {
const edges = this.elements.filter(isEdge)
return (
edges
.filter((edge) => !edge.isHidden)
.map((edge) => {
if (!isGraphEdge(edge)) {
const edges = this.elements.filter((e) => isEdge(e) && !e.isHidden) as Edge[]
if (this.isReady) {
return (
edges
.map((edge) => {
const { sourceNode, targetNode } = getSourceTargetNodes(edge, this.getNodes)
if (!sourceNode) console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
if (!targetNode) console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
@@ -79,43 +75,19 @@ export default (id: string, preloadedState: FlowState) => {
targetNode,
},
}
}
return edge
})
.filter(({ sourceTargetNodes: { sourceNode, targetNode } }) => !!(sourceNode && targetNode)) ?? []
)
})
.filter(({ sourceTargetNodes: { sourceNode, targetNode } }) => !!(sourceNode && targetNode)) ?? []
)
}
return []
},
getSelectedNodes(): GraphNode[] {
return this.selectedElements?.filter(isGraphNode) ?? []
},
},
actions: {
async setElements(elements) {
let next: NextElements = {
nextEdges: [],
nextNodes: [],
}
if (!this.worker || import.meta.env.SSR || typeof window === 'undefined') {
next = await parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)
} else if (this.worker) {
const { workerFn, workerTerminate } = parseElementsWorker()
const res = await workerFn(
deepUnref(elements),
deepUnref(this.getNodes),
deepUnref(this.getEdges),
deepUnref(this.nodeExtent),
).catch((err) => {
console.error(err)
workerTerminate('ERROR')
})
if (res) {
workerTerminate('SUCCESS')
next = res
} else next = await parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)
} else {
next = await parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)
}
this.elements = [...next.nextNodes, ...next.nextEdges]
setElements(elements) {
this.elements = parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)
},
setUserSelection(mousePos) {
this.selectionActive = true
@@ -204,8 +176,7 @@ export default (id: string, preloadedState: FlowState) => {
this.elementsSelectable = isInteractive
},
async addElements(elements: Elements) {
const { nextNodes, nextEdges } = await parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)
this.elements = [...this.elements, ...nextNodes, ...nextEdges]
this.elements = [...this.elements, ...parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)]
},
},
})

View File

@@ -3,16 +3,11 @@ import { Edge, EdgeTypes } from './edge'
import { NodeExtent, GraphNode, NodeTypes, TranslateExtent, Node } from './node'
import { ConnectionLineType, ConnectionMode } from './connection'
import { KeyCode, PanOnScrollMode } from './zoom'
import { FlowStore } from './store'
export type ElementId = string
export type FlowElement<T = any> = GraphNode<T> | Edge<T>
export type FlowElements<T = any> = FlowElement<T>[]
export type Elements<T = any> = (Node<T> | Edge<T>)[]
export type NextElements = {
nextNodes: GraphNode[]
nextEdges: Edge[]
}
export type Transform = [number, number, number]
@@ -148,6 +143,4 @@ export interface FlowOptions {
edgeUpdaterRadius?: number
storageKey?: string
loading?: Loading
worker?: boolean
store?: FlowStore
}

View File

@@ -21,7 +21,7 @@ import {
OnConnectStopFunc,
SetConnectionId,
} from './connection'
import { Edge, EdgeComponent, GraphEdge } from './edge'
import { EdgeComponent, GraphEdge } from './edge'
import { NodeComponent, NodeExtent, GraphNode, TranslateExtent } from './node'
import { D3Selection, D3Zoom, D3ZoomHandler, InitD3ZoomPayload } from './zoom'
import { FlowHooks } from './hooks'
@@ -73,7 +73,7 @@ export interface FlowState extends FlowOptions {
}
export interface FlowActions {
setElements: (elements: Elements) => Promise<void>
setElements: (elements: Elements) => void
setUserSelection: (mousePos: XYPosition) => void
updateUserSelection: (mousePos: XYPosition) => void
unsetUserSelection: () => void

View File

@@ -3,7 +3,6 @@ import {
ConnectionMode,
Elements,
FlowState,
NextElements,
NodeExtent,
GraphNode,
PanOnScrollMode,
@@ -97,48 +96,43 @@ export const initialState = (): FlowState => ({
vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-',
})
export const parseElements = async (elements: Elements, nodes: GraphNode[], edges: Edge[], nodeExtent: NodeExtent) =>
new Promise<NextElements>((resolve) => {
const { nextEdges, nextNodes }: NextElements = {
nextNodes: [],
nextEdges: [],
}
for (const element of elements) {
if (isNode(element)) {
const storeNode = nodes[nodes.map((x) => x.id).indexOf(element.id)]
export const parseElements = (elements: Elements, nodes: GraphNode[], edges: Edge[], nodeExtent: NodeExtent) => {
const parsedElements = []
for (const element of elements) {
if (isNode(element)) {
const storeNode = nodes[nodes.map((x) => x.id).indexOf(element.id)]
if (storeNode) {
const updatedNode = {
...storeNode,
...element,
} as GraphNode
if (storeNode) {
const updatedNode = {
...storeNode,
...element,
} as GraphNode
if (typeof element.type !== 'undefined' && element.type !== storeNode.type) {
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
// When the type of a node changes it is possible that the number or positions of handles changes too.
updatedNode.__vf.width = 0
}
nextNodes.push(updatedNode)
} else {
nextNodes.push(parseNode(element, nodeExtent))
if (typeof element.type !== 'undefined' && element.type !== storeNode.type) {
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
// When the type of a node changes it is possible that the number or positions of handles changes too.
updatedNode.__vf.width = 0
}
} else if (isEdge(element)) {
const storeEdge = edges[edges.map((x) => x.id).indexOf(element.id)]
if (storeEdge) {
nextEdges.push({
...storeEdge,
...element,
})
} else {
nextEdges.push(parseEdge(element))
}
parsedElements.push(updatedNode)
} else {
parsedElements.push(parseNode(element, nodeExtent))
}
} else if (isEdge(element)) {
const storeEdge = edges[edges.map((x) => x.id).indexOf(element.id)]
if (storeEdge) {
parsedElements.push({
...storeEdge,
...element,
})
} else {
parsedElements.push(parseEdge(element))
}
}
resolve({ nextEdges, nextNodes })
})
}
return parsedElements
}
const isObject = (val: any) => val !== null && typeof val === 'object'
const isArray = Array.isArray

View File

@@ -1,76 +0,0 @@
import { Connection, Edge, Elements, Node, NodeExtent, GraphNode, XYPosition, NextElements, FlowElement } from '~/types'
export default () =>
useWebWorkerFn((elements: Elements, nodes: GraphNode[], edges: Edge[], nodeExtent: NodeExtent): NextElements => {
const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max)
const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosition => ({
x: clamp(position.x, extent[0][0], extent[1][0]),
y: clamp(position.y, extent[0][1], extent[1][1]),
})
const parseNode = (node: Node, nodeExtent: NodeExtent): GraphNode =>
Object.assign(node as GraphNode, {
id: node.id.toString(),
type: node.type || 'default',
__vf: {
position: clampPosition(node.position, nodeExtent),
width: 0,
height: 0,
handleBounds: {},
isDragging: false,
},
})
const parseEdge = (edge: Edge): Edge =>
Object.assign(edge, {
source: edge.source.toString(),
target: edge.target.toString(),
sourceHandle: edge.sourceHandle ? edge.sourceHandle.toString() : null,
targetHandle: edge.targetHandle ? edge.targetHandle.toString() : null,
id: edge.id.toString(),
type: edge.type || 'default',
})
const isEdge = (element: Node | FlowElement | Connection): element is Edge =>
'id' in element && 'source' in element && 'target' in element
const isNode = (element: Node | FlowElement | Connection): element is Node =>
'id' in element && !('source' in element) && !('target' in element)
const nextElements: NextElements = {
nextNodes: [],
nextEdges: [],
}
for (const element of elements) {
if (isNode(element)) {
const storeNode = nodes[nodes.map((x) => x.id).indexOf(element.id)]
if (storeNode) {
const updatedNode = Object.assign(storeNode, element)
updatedNode.__vf!.position = element.position
if (typeof element.type !== 'undefined' && element.type !== storeNode.type) {
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
// When the type of a node changes it is possible that the number or positions of handles changes too.
updatedNode.__vf!.width = 0
}
nextElements.nextNodes.push(updatedNode)
} else {
nextElements.nextNodes.push(parseNode(element, nodeExtent))
}
} else if (isEdge(element)) {
const storeEdge = edges[edges.map((x) => x.id).indexOf(element.id)]
if (storeEdge) {
nextElements.nextEdges.push(Object.assign(storeEdge, element))
} else {
nextElements.nextEdges.push(parseEdge(element))
}
}
}
return nextElements
})