From e047be6854da3c4ed97fb7e270e85fd3b8bc3628 Mon Sep 17 00:00:00 2001 From: bcakmakoglu <78412429+bcakmakoglu@users.noreply.github.com> Date: Thu, 16 Jun 2022 13:17:15 +0200 Subject: [PATCH] feat(vue-flow): add `autoConnect` option # What's changed? * auto-connect allows to easily enable the default add edge change handler (shown in the examples) * allow auto-connect to be a function that returns false when connection is cancelled or partial edge when it should be created --- .../src/container/VueFlow/VueFlow.vue | 1 + .../vue-flow/src/container/VueFlow/watch.ts | 55 +++++++++++++++++-- packages/vue-flow/src/store/state.ts | 1 + packages/vue-flow/src/types/connection.ts | 5 ++ packages/vue-flow/src/types/flow.ts | 4 +- packages/vue-flow/src/types/store.ts | 3 +- 6 files changed, 62 insertions(+), 7 deletions(-) diff --git a/packages/vue-flow/src/container/VueFlow/VueFlow.vue b/packages/vue-flow/src/container/VueFlow/VueFlow.vue index 09a59a89..9e099351 100644 --- a/packages/vue-flow/src/container/VueFlow/VueFlow.vue +++ b/packages/vue-flow/src/container/VueFlow/VueFlow.vue @@ -36,6 +36,7 @@ const props = withDefaults(defineProps(), { fitViewOnInit: undefined, connectOnClick: undefined, connectionLineStyle: undefined, + autoConnect: undefined, }) const emit = defineEmits<{ diff --git a/packages/vue-flow/src/container/VueFlow/watch.ts b/packages/vue-flow/src/container/VueFlow/watch.ts index cc560327..b56aa383 100644 --- a/packages/vue-flow/src/container/VueFlow/watch.ts +++ b/packages/vue-flow/src/container/VueFlow/watch.ts @@ -1,6 +1,7 @@ import type { Ref, ToRefs } from 'vue' import type { WatchPausableReturn } from '@vueuse/core' -import type { FlowProps, GraphEdge, GraphNode, VueFlowStore } from '~/types' +import { isFunction } from '@vueuse/core' +import type { Connection, FlowProps, GraphEdge, GraphNode, VueFlowStore } from '~/types' const isDef = (val: T): val is NonNullable => typeof val !== 'undefined' export default (models: ToRefs>, props: FlowProps, store: VueFlowStore) => { @@ -171,8 +172,45 @@ export default (models: ToRefs }) } + const watchAutoConnect = () => { + scope.run(() => { + watch( + () => props.autoConnect, + () => { + if (isDef(props.autoConnect)) { + store.autoConnect.value = props.autoConnect + } + }, + { immediate: isDef(props.autoConnect) }, + ) + + watch( + store.autoConnect, + () => { + const autoConnector = async (params: Connection) => { + let connect: boolean | Connection = true + if (isFunction(props.autoConnect)) { + connect = await props.autoConnect(params) + } + + if (connect) { + store.addEdges([params]) + } + } + + if (store.autoConnect) { + store.onConnect(autoConnector) + } else { + store.hooks.value.connect.off(autoConnector) + } + }, + { immediate: true }, + ) + }) + } + const watchRest = () => { - const skip = ['id', 'modelValue', 'edges', 'nodes', 'maxZoom', 'minZoom', 'applyDefault'] + const skip = ['id', 'modelValue', 'edges', 'nodes', 'maxZoom', 'minZoom', 'applyDefault', 'autoConnect'] Object.keys(props).forEach((prop) => { if (!skip.includes(prop)) { const model = props[prop as keyof typeof props] @@ -193,9 +231,16 @@ export default (models: ToRefs }) } - ;[watchModelValue, watchNodesValue, watchEdgesValue, watchMinZoom, watchMaxZoom, watchApplyDefault, watchRest].forEach( - (watch) => watch(), - ) + ;[ + watchModelValue, + watchNodesValue, + watchEdgesValue, + watchMinZoom, + watchMaxZoom, + watchApplyDefault, + watchAutoConnect, + watchRest, + ].forEach((watch) => watch()) }) return () => scope.stop() diff --git a/packages/vue-flow/src/store/state.ts b/packages/vue-flow/src/store/state.ts index b6f78996..8e5bc587 100644 --- a/packages/vue-flow/src/store/state.ts +++ b/packages/vue-flow/src/store/state.ts @@ -101,6 +101,7 @@ const defaultState = (): State => ({ hooks: createHooks(), applyDefault: true, + autoConnect: false, fitViewOnInit: false, noDragClassName: 'nodrag', diff --git a/packages/vue-flow/src/types/connection.ts b/packages/vue-flow/src/types/connection.ts index 2f0aebcf..452cd794 100644 --- a/packages/vue-flow/src/types/connection.ts +++ b/packages/vue-flow/src/types/connection.ts @@ -2,6 +2,7 @@ import type { CSSProperties } from 'vue' import type { Position } from './flow' import type { GraphNode } from './node' import type { HandleElement, HandleType } from './handle' +import type { Edge } from './edge' /** Connection line types (same as default edge types */ export enum ConnectionLineType { @@ -23,6 +24,10 @@ export interface Connection { targetHandle: string | null } +export type Connector = ( + params: Connection, +) => Promise<(Connection & Partial) | false> | ((Connection & Partial) | false) + /** The source nodes params when connection is initiated */ export interface OnConnectStartParams { /** Source node id */ diff --git a/packages/vue-flow/src/types/flow.ts b/packages/vue-flow/src/types/flow.ts index 7d3cc0f6..726ee3da 100644 --- a/packages/vue-flow/src/types/flow.ts +++ b/packages/vue-flow/src/types/flow.ts @@ -1,7 +1,7 @@ import type { CSSProperties } from 'vue' import type { DefaultEdgeOptions, Edge, GraphEdge } from './edge' import type { CoordinateExtent, GraphNode, Node } from './node' -import type { ConnectionLineType, ConnectionMode } from './connection' +import type { ConnectionLineType, ConnectionMode, Connector } from './connection' import type { KeyCode, PanOnScrollMode } from './zoom' import type { DefaultEdgeTypes, DefaultNodeTypes, EdgeComponent, NodeComponent } from './components' @@ -122,6 +122,8 @@ export interface FlowProps { connectOnClick?: boolean /** apply default change handlers for position, dimensions, adding/removing nodes. set this to false if you want to apply the changes manually */ applyDefault?: boolean + /** automatically create an edge when connection is triggered */ + autoConnect?: boolean | Connector noDragClassName?: string noWheelClassName?: string noPanClassName?: string diff --git a/packages/vue-flow/src/types/store.ts b/packages/vue-flow/src/types/store.ts index 6a6ffc91..f14b93cd 100644 --- a/packages/vue-flow/src/types/store.ts +++ b/packages/vue-flow/src/types/store.ts @@ -1,7 +1,7 @@ import type { CSSProperties, ComputedRef, ToRefs } from 'vue' import type { Dimensions, ElementData, Elements, FlowElements, FlowExportObject, FlowOptions, SnapGrid, XYPosition } from './flow' import type { DefaultEdgeTypes, DefaultNodeTypes, EdgeComponent, NodeComponent } from './components' -import type { Connection, ConnectionLineType, ConnectionMode } from './connection' +import type { Connection, ConnectionLineType, ConnectionMode, Connector } from './connection' import type { DefaultEdgeOptions, Edge, GraphEdge } from './edge' import type { CoordinateExtent, GraphNode, Node } from './node' import type { D3Selection, D3Zoom, D3ZoomHandler, KeyCode, PanOnScrollMode, Viewport, ViewportFunctions } from './zoom' @@ -88,6 +88,7 @@ export interface State extends Omit { initialized: boolean applyDefault: boolean + autoConnect: boolean | Connector fitViewOnInit?: boolean