feat: try to dispose states on unmount

Signed-off-by: bcakmakoglu <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
bcakmakoglu
2022-02-21 20:25:17 +01:00
committed by Braks
parent d835e4546c
commit c5b39d4588
10 changed files with 240 additions and 224 deletions
+1 -7
View File
@@ -6,12 +6,6 @@ const selectAll = () => {
addSelectedNodes(getNodes.value) addSelectedNodes(getNodes.value)
nodesSelectionActive.value = true nodesSelectionActive.value = true
} }
const transformString = computed(() => [
transform.value[0].toFixed(2),
transform.value[1].toFixed(2),
transform.value[2].toFixed(2),
])
</script> </script>
<template> <template>
<aside> <aside>
@@ -20,7 +14,7 @@ const transformString = computed(() => [
</div> </div>
<div class="title">Zoom & pan transform</div> <div class="title">Zoom & pan transform</div>
<div class="transform"> <div class="transform">
{{ transformString }} {{ [transform[0].toFixed(2), transform[1].toFixed(2), transform[2].toFixed(2)] }}
</div> </div>
<div class="title">Nodes</div> <div class="title">Nodes</div>
<div v-for="node of getNodes" :key="node.id"> <div v-for="node of getNodes" :key="node.id">
+1 -1
View File
@@ -38,7 +38,7 @@ const onChange = ({ color: c, val }: { color: keyof Colors; val: number }) => (c
<RGBNode v-bind="props" :amount="color" @change="onChange" /> <RGBNode v-bind="props" :amount="color" @change="onChange" />
</template> </template>
<template #node-rgb-output="props"> <template #node-rgb-output="props">
<RGBOutputNode :v-bind="props" :rgb="`rgb(${color.red}, ${color.green}, ${color.blue})`" /> <RGBOutputNode v-bind="props" :rgb="`rgb(${color.red}, ${color.green}, ${color.blue})`" />
</template> </template>
</VueFlow> </VueFlow>
</div> </div>
@@ -14,7 +14,7 @@ const props = withDefaults(defineProps<MiniMapProps>(), {
maskColor: 'rgb(240, 242, 243, 0.7)', maskColor: 'rgb(240, 242, 243, 0.7)',
}) })
const attrs: any = useAttrs() const attrs = useAttrs()
const window = useWindow() const window = useWindow()
const defaultWidth = 200 const defaultWidth = 200
+5
View File
@@ -166,4 +166,9 @@ export default (store: FlowStore = useVueFlow().store) =>
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject) doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject) doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
onScopeDispose(() => {
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
})
} }
+19 -11
View File
@@ -1,17 +1,17 @@
import { getCurrentInstance } from 'vue' import { EffectScope } from 'vue'
import { FlowOptions, UseVueFlow } from '~/types' import { FlowOptions, UseVueFlow, Store } from '~/types'
import { VueFlow } from '~/context' import { VueFlow } from '~/context'
import { useStore } from '~/store' import { useStore } from '~/store'
let id = 0 let id = 0
type Scope = EffectScope & { vueFlow: UseVueFlow }
export default <N = any, E = N>(options?: Partial<FlowOptions<N, E>>): UseVueFlow<N, E> => { export default <N = any, E = N>(options?: Partial<FlowOptions<N, E>>): UseVueFlow<N, E> => {
const currentInstance: any = getCurrentInstance() const scope = getCurrentScope() as Scope
let vueFlow: false | UseVueFlow = currentInstance let vueFlow: UseVueFlow | null = scope ? inject(VueFlow, null) ?? (scope.vueFlow as UseVueFlow) : null
? inject(VueFlow, undefined) ?? (currentInstance.vueFlow as UseVueFlow)
: false
if (!vueFlow || (vueFlow && options?.id && options.id !== vueFlow.id)) { if (!vueFlow || (vueFlow && options?.id && options.id !== vueFlow.id)) {
const name = options?.id ?? `vue-flow-${id++}` const name = options?.id ?? `vue-flow-${id++}`
const store = useStore(options) let store: Store = useStore(options)
vueFlow = { vueFlow = {
id: name, id: name,
store: reactive(store), store: reactive(store),
@@ -20,11 +20,19 @@ export default <N = any, E = N>(options?: Partial<FlowOptions<N, E>>): UseVueFlo
...store.actions, ...store.actions,
...store.hooksOn, ...store.hooksOn,
} as unknown as UseVueFlow } as unknown as UseVueFlow
if (scope) {
provide(VueFlow, vueFlow)
scope.vueFlow = vueFlow
}
onScopeDispose(() => {
vueFlow = null as UseVueFlow
scope.vueFlow = null as UseVueFlow
store = null as Store
})
} }
if (currentInstance) { if (!vueFlow) throw new Error('VueFlow instance not found.')
provide(VueFlow, vueFlow)
currentInstance.vueFlow = vueFlow
}
return <UseVueFlow<N, E>>vueFlow return <UseVueFlow<N, E>>vueFlow
} }
+191 -193
View File
@@ -15,198 +15,196 @@ export default (
props: FlowProps, props: FlowProps,
store: FlowStore, store: FlowStore,
) => { ) => {
if (isDefined(props.modelValue)) { const scope = getCurrentScope()
const { pause, resume } = pausableWatch([() => props.modelValue, () => props.modelValue?.length], async ([v]) => { scope?.run(() => {
if (v && Array.isArray(v)) { if (isDefined(props.modelValue)) {
pause() const { pause, resume } = pausableWatch([() => props.modelValue, () => props.modelValue?.length], async ([v]) => {
store.setElements(v) if (v && Array.isArray(v)) {
if (modelValue) modelValue.value = [...store.nodes, ...store.edges] pause()
await nextTick() store.setElements(v)
resume() if (modelValue) modelValue.value = [...store.nodes, ...store.edges]
} await nextTick()
}) resume()
} }
if (isDefined(props.nodes)) { })
const { pause, resume } = pausableWatch([() => props.nodes, () => props.nodes?.length], async ([v]) => { }
if (v && Array.isArray(v)) { if (isDefined(props.nodes)) {
pause() const { pause, resume } = pausableWatch([() => props.nodes, () => props.nodes?.length], async ([v]) => {
store.setNodes(v) if (v && Array.isArray(v)) {
if (nodes) nodes.value = store.nodes pause()
await nextTick() store.setNodes(v)
resume() if (nodes) nodes = store.nodes
} await nextTick()
}) resume()
} }
if (isDefined(props.edges)) { })
const { pause, resume } = pausableWatch([() => props.edges, () => props.edges?.length], async ([v]) => { }
if (v && Array.isArray(v)) { if (isDefined(props.edges)) {
pause() const { pause, resume } = pausableWatch([() => props.edges, () => props.edges?.length], async ([v]) => {
store.setEdges(v) if (v && Array.isArray(v)) {
if (edges) edges.value = store.edges pause()
await nextTick() store.setEdges(v)
resume() if (edges) edges = store.edges
} await nextTick()
}) resume()
} }
})
}
watch( watch(
() => props.maxZoom, () => props.maxZoom,
(v) => isDef(v) && store.setMaxZoom(v), (v) => isDef(v) && store.setMaxZoom(v),
{ immediate: isDef(props.maxZoom) }, { immediate: isDef(props.maxZoom) },
) )
watch( watch(
() => props.minZoom, () => props.minZoom,
(v) => isDef(v) && store.setMinZoom(v), (v) => isDef(v) && store.setMinZoom(v),
{ immediate: isDef(props.minZoom) }, { immediate: isDef(props.minZoom) },
) )
watch( watch(
() => props.edgesUpdatable, () => props.edgesUpdatable,
(v) => isDef(v) && (store.edgesUpdatable = v), (v) => isDef(v) && (store.edgesUpdatable = v),
{ immediate: isDef(props.edgesUpdatable) }, { immediate: isDef(props.edgesUpdatable) },
) )
watch( watch(
() => props.elementsSelectable, () => props.elementsSelectable,
(v) => isDef(v) && (store.elementsSelectable = v), (v) => isDef(v) && (store.elementsSelectable = v),
{ immediate: isDef(props.elementsSelectable) }, { immediate: isDef(props.elementsSelectable) },
) )
watch( watch(
() => props.nodesDraggable, () => props.nodesDraggable,
(v) => isDef(v) && (store.nodesDraggable = v), (v) => isDef(v) && (store.nodesDraggable = v),
{ immediate: isDef(props.nodesDraggable) }, { immediate: isDef(props.nodesDraggable) },
) )
watch( watch(
() => props.nodesConnectable, () => props.nodesConnectable,
(v) => isDef(v) && (store.nodesConnectable = v), (v) => isDef(v) && (store.nodesConnectable = v),
{ immediate: isDef(props.nodesConnectable) }, { immediate: isDef(props.nodesConnectable) },
) )
watch( watch(
() => props.onlyRenderVisibleElements, () => props.onlyRenderVisibleElements,
(v) => isDef(v) && (store.onlyRenderVisibleElements = v), (v) => isDef(v) && (store.onlyRenderVisibleElements = v),
{ immediate: isDef(props.onlyRenderVisibleElements) }, { immediate: isDef(props.onlyRenderVisibleElements) },
) )
watch( watch(
() => props.snapToGrid, () => props.snapToGrid,
(v) => isDef(v) && (store.snapToGrid = v), (v) => isDef(v) && (store.snapToGrid = v),
{ immediate: isDef(props.snapToGrid) }, { immediate: isDef(props.snapToGrid) },
) )
watch( watch(
() => props.snapGrid, () => props.snapGrid,
(v) => isDef(v) && (store.snapGrid = v), (v) => isDef(v) && (store.snapGrid = v),
{ immediate: isDef(props.snapGrid) }, { immediate: isDef(props.snapGrid) },
) )
watch( watch(
() => props.paneMoveable, () => props.paneMoveable,
(v) => isDef(v) && (store.paneMoveable = v), (v) => isDef(v) && (store.paneMoveable = v),
{ immediate: isDef(props.paneMoveable) }, { immediate: isDef(props.paneMoveable) },
) )
watch( watch(
() => props.panOnScroll, () => props.panOnScroll,
(v) => isDef(v) && (store.panOnScroll = v), (v) => isDef(v) && (store.panOnScroll = v),
{ immediate: isDef(props.panOnScroll) }, { immediate: isDef(props.panOnScroll) },
) )
watch( watch(
() => props.panOnScrollMode, () => props.panOnScrollMode,
(v) => isDef(v) && (store.panOnScrollMode = v), (v) => isDef(v) && (store.panOnScrollMode = v),
{ immediate: isDef(props.panOnScrollMode) }, { immediate: isDef(props.panOnScrollMode) },
) )
watch( watch(
() => props.panOnScrollSpeed, () => props.panOnScrollSpeed,
(v) => isDef(v) && (store.panOnScrollSpeed = v), (v) => isDef(v) && (store.panOnScrollSpeed = v),
{ immediate: isDef(props.panOnScrollSpeed) }, { immediate: isDef(props.panOnScrollSpeed) },
) )
watch( watch(
() => props.zoomOnPinch, () => props.zoomOnPinch,
(v) => isDef(v) && (store.zoomOnPinch = v), (v) => isDef(v) && (store.zoomOnPinch = v),
{ immediate: isDef(props.zoomOnPinch) }, { immediate: isDef(props.zoomOnPinch) },
) )
watch( watch(
() => props.zoomOnDoubleClick, () => props.zoomOnDoubleClick,
(v) => isDef(v) && (store.zoomOnDoubleClick = v), (v) => isDef(v) && (store.zoomOnDoubleClick = v),
{ immediate: isDef(props.zoomOnDoubleClick) }, { immediate: isDef(props.zoomOnDoubleClick) },
) )
watch( watch(
() => props.zoomOnScroll, () => props.zoomOnScroll,
(v) => isDef(v) && (store.zoomOnScroll = v), (v) => isDef(v) && (store.zoomOnScroll = v),
{ immediate: isDef(props.zoomOnScroll) }, { immediate: isDef(props.zoomOnScroll) },
) )
watch( watch(
() => props.deleteKeyCode, () => props.deleteKeyCode,
(v) => isDef(v) && (store.deleteKeyCode = v), (v) => isDef(v) && (store.deleteKeyCode = v),
{ immediate: isDef(props.deleteKeyCode) }, { immediate: isDef(props.deleteKeyCode) },
) )
watch( watch(
() => props.zoomActivationKeyCode, () => props.zoomActivationKeyCode,
(v) => isDef(v) && (store.zoomActivationKeyCode = v), (v) => isDef(v) && (store.zoomActivationKeyCode = v),
{ immediate: isDef(props.zoomActivationKeyCode) }, { immediate: isDef(props.zoomActivationKeyCode) },
) )
watch( watch(
() => props.selectionKeyCode, () => props.selectionKeyCode,
(v) => isDef(v) && (store.selectionKeyCode = v), (v) => isDef(v) && (store.selectionKeyCode = v),
{ immediate: isDef(props.selectionKeyCode) }, { immediate: isDef(props.selectionKeyCode) },
) )
watch( watch(
() => props.multiSelectionKeyCode, () => props.multiSelectionKeyCode,
(v) => isDef(v) && (store.multiSelectionKeyCode = v), (v) => isDef(v) && (store.multiSelectionKeyCode = v),
{ immediate: isDef(props.multiSelectionKeyCode) }, { immediate: isDef(props.multiSelectionKeyCode) },
) )
watch( watch(
() => props.connectionLineStyle, () => props.connectionLineStyle,
(v) => isDef(v) && (store.connectionLineStyle = v), (v) => isDef(v) && (store.connectionLineStyle = v),
{ immediate: isDef(props.connectionLineStyle) }, { immediate: isDef(props.connectionLineStyle) },
) )
watch( watch(
() => props.connectionMode, () => props.connectionMode,
(v) => isDef(v) && (store.connectionMode = v), (v) => isDef(v) && (store.connectionMode = v),
{ immediate: isDef(props.connectionMode) }, { immediate: isDef(props.connectionMode) },
) )
watch( watch(
() => props.connectionLineType, () => props.connectionLineType,
(v) => isDef(v) && (store.connectionLineType = v), (v) => isDef(v) && (store.connectionLineType = v),
{ immediate: isDef(props.connectionLineType) }, { immediate: isDef(props.connectionLineType) },
) )
watch( watch(
() => props.defaultMarkerColor, () => props.defaultMarkerColor,
(v) => isDef(v) && (store.defaultMarkerColor = v), (v) => isDef(v) && (store.defaultMarkerColor = v),
{ immediate: isDef(props.defaultMarkerColor) }, { immediate: isDef(props.defaultMarkerColor) },
) )
watch( watch(
() => props.defaultPosition, () => props.defaultPosition,
(v) => isDef(v) && (store.defaultPosition = v), (v) => isDef(v) && (store.defaultPosition = v),
{ immediate: isDef(props.defaultPosition) }, { immediate: isDef(props.defaultPosition) },
) )
watch( watch(
() => props.defaultZoom, () => props.defaultZoom,
(v) => isDef(v) && (store.defaultZoom = v), (v) => isDef(v) && (store.defaultZoom = v),
{ immediate: isDef(props.defaultZoom) }, { immediate: isDef(props.defaultZoom) },
) )
watch( watch(
() => props.translateExtent, () => props.translateExtent,
(v) => isDef(v) && (store.translateExtent = v), (v) => isDef(v) && (store.translateExtent = v),
{ immediate: isDef(props.translateExtent) }, { immediate: isDef(props.translateExtent) },
) )
watch( watch(
() => props.nodeExtent, () => props.nodeExtent,
(v) => isDef(v) && (store.nodeExtent = v), (v) => isDef(v) && (store.nodeExtent = v),
{ immediate: isDef(props.nodeExtent) }, { immediate: isDef(props.nodeExtent) },
) )
watch( watch(
() => props.selectNodesOnDrag, () => props.selectNodesOnDrag,
(v) => isDef(v) && (store.selectNodesOnDrag = v), (v) => isDef(v) && (store.selectNodesOnDrag = v),
{ immediate: isDef(props.selectNodesOnDrag) }, { immediate: isDef(props.selectNodesOnDrag) },
) )
watch( watch(
() => props.edgeUpdaterRadius, () => props.edgeUpdaterRadius,
(v) => isDef(v) && (store.edgeUpdaterRadius = v), (v) => isDef(v) && (store.edgeUpdaterRadius = v),
{ immediate: isDef(props.edgeUpdaterRadius) }, { immediate: isDef(props.edgeUpdaterRadius) },
) )
watch( watch(
() => props.applyDefault, () => props.applyDefault,
(v) => isDef(v) && (store.applyDefault = v), (v) => isDef(v) && (store.applyDefault = v),
{ immediate: isDef(props.applyDefault) }, { immediate: isDef(props.applyDefault) },
) )
watch( })
() => props.fitViewOnInit,
(v) => isDef(v) && (store.fitViewOnInit = v),
{ immediate: isDef(props.fitViewOnInit) },
)
} }
+4
View File
@@ -1,3 +1,4 @@
import useState from './state'
import { import {
CoordinateExtent, CoordinateExtent,
EdgeChange, EdgeChange,
@@ -304,5 +305,8 @@ export default (state: State, getters: ComputedGetters): Actions => {
setConnectionNodeId, setConnectionNodeId,
setInteractive, setInteractive,
setState, setState,
$reset: () => {
setState(useState())
},
} }
} }
+1 -1
View File
@@ -97,7 +97,7 @@ export default (opts?: FlowOptions): State => {
applyDefault: true, applyDefault: true,
vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-', vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-',
} } as State
if (opts) { if (opts) {
if (typeof opts.panOnScroll !== 'undefined') state.panOnScroll = opts.panOnScroll if (typeof opts.panOnScroll !== 'undefined') state.panOnScroll = opts.panOnScroll
+16 -9
View File
@@ -1,31 +1,38 @@
import useState from './state' import useState from './state'
import useActions from './actions' import useActions from './actions'
import useGetters from './getters' import useGetters from './getters'
import { FlowHooksOn, FlowOptions, Store } from '~/types' import { FlowHooksOn, FlowOptions, Store, State } from '~/types'
export default (preloadedState?: FlowOptions): Store => { export default (preloadedState?: FlowOptions): Store => {
const state = reactive(useState(preloadedState)) const state: State = useState(preloadedState)
const getters = useGetters(state) const reactiveState = reactive(state)
const actions = useActions(state, getters) const getters = useGetters(reactiveState)
const actions = useActions(reactiveState, getters)
const hooksOn: FlowHooksOn = <any>{} const hooksOn: FlowHooksOn = <any>{}
Object.entries(state.hooks).forEach(([n, h]) => { Object.entries(reactiveState.hooks).forEach(([n, h]) => {
const name = `on${n.charAt(0).toUpperCase() + n.slice(1)}` const name = `on${n.charAt(0).toUpperCase() + n.slice(1)}`
hooksOn[<keyof FlowHooksOn>name] = h.on as any hooksOn[<keyof FlowHooksOn>name] = h.on as any
}) })
actions.setState(state) actions.setState(reactiveState)
if (preloadedState) { if (preloadedState) {
if (preloadedState.modelValue) actions.setElements(preloadedState.modelValue) if (preloadedState.modelValue) actions.setElements(preloadedState.modelValue)
if (preloadedState.nodes) actions.setNodes(preloadedState.nodes) if (preloadedState.nodes) actions.setNodes(preloadedState.nodes)
if (preloadedState.edges) actions.setEdges(preloadedState.edges) if (preloadedState.edges) actions.setEdges(preloadedState.edges)
} }
return { const store = {
state, state: reactiveState,
actions, actions,
getters, getters,
hooksOn, hooksOn,
...toRefs(state), ...toRefs(reactiveState),
...getters, ...getters,
...actions, ...actions,
} as unknown as Store } as unknown as Store
onScopeDispose(() => {
store.$reset()
})
return store
} }
+1 -1
View File
@@ -90,7 +90,7 @@ export interface Actions<N = any, E = N> {
setInteractive: (isInteractive: boolean) => void setInteractive: (isInteractive: boolean) => void
setState: (state: Partial<FlowOptions<N, E>>) => void setState: (state: Partial<FlowOptions<N, E>>) => void
updateNodePosition: ({ id, diff, dragging }: { id?: string; diff?: XYPosition; dragging?: boolean }) => void updateNodePosition: ({ id, diff, dragging }: { id?: string; diff?: XYPosition; dragging?: boolean }) => void
$destroy: () => void $reset: () => void
} }
export interface Getters<N = any, E = N> { export interface Getters<N = any, E = N> {