diff --git a/package/src/container/VueFlow/VueFlow.vue b/package/src/container/VueFlow/VueFlow.vue index 6c7f8684..dbbd67b7 100644 --- a/package/src/container/VueFlow/VueFlow.vue +++ b/package/src/container/VueFlow/VueFlow.vue @@ -23,64 +23,18 @@ const props = withDefaults(defineProps(), { applyDefault: undefined, fitViewOnInit: undefined, connectOnClick: undefined, - connectionLineStyle: null, + connectionLineStyle: undefined, }) -const emit = defineEmits([...Object.keys(createHooks()), 'update:modelValue', 'update:edges', 'update:nodes']) -const { - id, - store, - hooks, - applyDefault, - setElements, - setEdges, - setNodes, - getNodeTypes, - getEdgeTypes, - onNodesChange, - onEdgesChange, - applyNodeChanges, - applyEdgeChanges, - nodes: storedNodes, - edges: storedEdges, -} = useVueFlow(props) +const emit = defineEmits([...Object.keys(createHooks()), 'update:modelValue', 'update:nodes', 'update:edges']) + +const modelProps = useVModels(props, emit) + +const { id, hooks, getNodeTypes, getEdgeTypes, ...rest } = useVueFlow() + +useWatch(modelProps, { id, hooks, getNodeTypes, getEdgeTypes, ...rest }) + useHooks(emit, hooks.value) -const { modelValue, nodes, edges } = useVModels(props, emit) -onMounted(() => useWatch({ modelValue, nodes, edges }, props, store)) - -if (applyDefault.value) { - onNodesChange(applyNodeChanges) - onEdgesChange(applyEdgeChanges) -} -if (props.modelValue && !storedNodes.value.length) setElements(props.modelValue) -if (props.nodes && !storedNodes.value.length) setNodes(props.nodes) -if (props.edges && !storedEdges.value.length) setEdges(props.edges) - -if (modelValue && modelValue.value) { - watch( - [() => storedEdges.value.length, () => storedNodes.value.length], - () => { - modelValue.value = [...storedNodes.value, ...storedEdges.value] - }, - { immediate: true }, - ) -} - -if (nodes && nodes.value) { - watch( - () => storedNodes.value.length, - () => (nodes.value = [...storedNodes.value]), - { immediate: true }, - ) -} - -if (edges && edges.value) { - watch( - () => storedEdges.value.length, - () => (edges.value = [...storedEdges.value]), - { immediate: true }, - ) -} provide(Slots, useSlots()) diff --git a/package/src/container/VueFlow/watch.ts b/package/src/container/VueFlow/watch.ts index e885c432..567f127d 100644 --- a/package/src/container/VueFlow/watch.ts +++ b/package/src/container/VueFlow/watch.ts @@ -1,76 +1,187 @@ -import { Ref } from 'vue' -import { FlowProps, Store, Node, Edge, Elements } from '~/types' +import { Ref, ToRefs } from 'vue' +import { WatchPausableReturn } from '@vueuse/core' +import { FlowProps, UseVueFlow } from '~/types' const isDef = (val: T): val is NonNullable => typeof val !== 'undefined' -export default ( - { - modelValue, - nodes, - edges, - }: { - modelValue?: Ref - nodes?: Ref - edges?: Ref - }, - props: FlowProps, - store: Store, -) => { +export default (models: ToRefs, store: UseVueFlow) => { const scope = getCurrentScope() + scope?.run(() => { - if (isDefined(props.modelValue)) { - const { pause, resume } = pausableWatch([() => props.modelValue, () => props.modelValue?.length], async ([v]) => { - if (v && Array.isArray(v)) { - pause() - store.setElements(v) - 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)) { - pause() - store.setNodes(v) - if (nodes) nodes.value = 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)) { - pause() - store.setEdges(v) - if (edges) edges.value = store.edges - await nextTick() - resume() - } - }) - } + const watchModelValue = () => { + scope.run(() => { + let pauseModel: WatchPausableReturn + let pauseStore: WatchPausableReturn - watch( - () => props.maxZoom, - (v) => isDef(v) && store.setMaxZoom(v), - { immediate: isDef(props.maxZoom) }, - ) - watch( - () => props.minZoom, - (v) => isDef(v) && store.setMinZoom(v), - { immediate: isDef(props.minZoom) }, - ) + // eslint-disable-next-line prefer-const + pauseModel = watchPausable( + [models.modelValue, () => models.modelValue?.value?.length], + ([v]) => { + if (v && Array.isArray(v)) { + if (pauseStore) pauseStore.pause() + if (pauseModel) pauseModel.pause() - const skip = ['modelValue', 'edges', 'nodes', 'maxZoom', 'minZoom'] - Object.keys(props).forEach((p) => { - if (!skip.includes(p)) { - const prop = props[p as keyof typeof props] - watch( - () => prop, - (v) => isDef(v) && ((store as any)[p] = v), - { immediate: isDef(prop) }, + store.setElements(v) + + pauseStore = watchPausable( + [() => store.edges.value.length, () => store.nodes.value.length], + () => { + models.modelValue!.value = [...store.nodes.value, ...store.edges.value] + }, + { immediate: true }, + ) + + nextTick(() => { + if (pauseStore) pauseStore.resume() + if (pauseModel) pauseModel.resume() + }) + } + }, + { immediate: !!(models.modelValue && models.modelValue.value), flush: 'post' }, ) + }) + } + + const watchNodesValue = () => { + scope.run(() => { + let pauseModel: WatchPausableReturn + let pauseStore: WatchPausableReturn + + // eslint-disable-next-line prefer-const + pauseModel = watchPausable( + [models.nodes, () => models.nodes?.value?.length], + async ([v]) => { + if (v && Array.isArray(v)) { + if (pauseStore) pauseStore.pause() + if (pauseModel) pauseModel.pause() + + store.setNodes(v) + + pauseStore = watchPausable( + () => store.nodes.value.length, + () => (models.nodes!.value = [...store.nodes.value]), + { immediate: true }, + ) + + nextTick(() => { + if (pauseStore) pauseStore.resume() + if (pauseModel) pauseModel.resume() + }) + } + }, + { immediate: !!(models.nodes && models.nodes.value), flush: 'post' }, + ) + }) + } + + const watchEdgesValue = () => { + scope.run(() => { + let pauseModel: WatchPausableReturn + let pauseStore: WatchPausableReturn + + // eslint-disable-next-line prefer-const + pauseModel = watchPausable( + [models.edges, () => models.edges?.value?.length], + async ([v]) => { + if (v && Array.isArray(v)) { + if (pauseStore) pauseStore.pause() + if (pauseModel) pauseModel.pause() + + store.setEdges(v) + + pauseStore = watchPausable( + () => store.edges.value.length, + () => (models.edges!.value = [...store.edges.value]), + { immediate: true }, + ) + + nextTick(() => { + if (pauseStore) pauseStore.resume() + if (pauseModel) pauseModel.resume() + }) + } + }, + { immediate: !!(models.edges && models.edges.value), flush: 'post' }, + ) + }) + } + + const watchMaxZoom = () => { + scope.run(() => { + watch( + [() => models.maxZoom, models.maxZoom], + () => { + if (models.maxZoom && isDef(models.maxZoom.value)) { + store.setMaxZoom(models.maxZoom.value) + } + }, + { immediate: isDef(models.maxZoom?.value) }, + ) + }) + } + + const watchMinZoom = () => { + scope.run(() => { + watch( + [() => models.minZoom, models.minZoom], + () => { + if (models.minZoom && isDef(models.minZoom.value)) { + store.setMinZoom(models.minZoom.value) + } + }, + { immediate: isDef(models.minZoom?.value) }, + ) + }) + } + + const watchApplyDefault = () => { + scope.run(() => { + watch( + [() => models.applyDefault, models.applyDefault], + () => { + if (models.applyDefault && isDef(models.applyDefault.value)) { + store.applyDefault.value = models.applyDefault.value + } + }, + { immediate: isDef(models.applyDefault?.value) }, + ) + + watch( + store.applyDefault, + () => { + if (store.applyDefault.value) { + store.onNodesChange(store.applyNodeChanges) + store.onEdgesChange(store.applyEdgeChanges) + } + }, + { immediate: true }, + ) + }) + } + + watchModelValue() + watchNodesValue() + watchEdgesValue() + watchMaxZoom() + watchMinZoom() + watchApplyDefault() + + const skip = ['id', 'modelValue', 'edges', 'nodes', 'maxZoom', 'minZoom', 'applyDefault'] + Object.keys(models).forEach((m) => { + if (!skip.includes(m)) { + const model = models[m as keyof typeof models] + const storedValue = (store)[m] as Ref + + scope.run(() => { + watch( + [() => model, model], + () => { + if (model && isDef(model.value)) { + storedValue.value = model.value + } + }, + { immediate: isDef(model?.value) }, + ) + }) } }) }) diff --git a/package/src/store/state.ts b/package/src/store/state.ts index 878a0faf..deb7686b 100644 --- a/package/src/store/state.ts +++ b/package/src/store/state.ts @@ -38,6 +38,8 @@ const isDef = (val: T): val is NonNullable => typeof val !== 'undefined' const defaultState = (): State => ({ nodes: [], edges: [], + nodeTypes: {}, + edgeTypes: {}, initialized: false, instance: null,