refactor(core): add connection start and end handles to store state

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
braks
2023-04-02 20:37:07 +02:00
committed by Braks
parent eab9991762
commit 4efc787067
8 changed files with 53 additions and 32 deletions

View File

@@ -8,6 +8,7 @@ const { sourceNode } = defineProps<{ sourceNode: GraphNode }>()
const {
connectionMode,
connectionStartHandle,
connectionEndHandle,
connectionPosition,
connectionLineType,
connectionLineStyle,
@@ -30,9 +31,7 @@ const handleId = $computed(() => connectionStartHandle!.handleId)
const type = computed(() => connectionStartHandle!.type)
const targetNode = $computed(
() => (connectionStartHandle?.result && findNode(connectionStartHandle.result.connection.target)) || null,
)
const targetNode = $computed(() => (connectionEndHandle?.handleId && findNode(connectionEndHandle.handleId)) || null)
const sourceHandle = $computed(
() =>
@@ -45,13 +44,13 @@ const sourceHandle = $computed(
const targetHandle = $computed(() => {
return (
(targetNode &&
connectionStartHandle?.result?.handleId &&
connectionEndHandle?.handleId &&
((connectionMode === ConnectionMode.Strict
? targetNode.handleBounds[type.value === 'source' ? 'target' : 'source']?.find(
(d) => d.id === connectionStartHandle?.result?.handleId,
(d) => d.id === connectionEndHandle?.handleId,
)
: [...(targetNode.handleBounds.source || []), ...(targetNode.handleBounds.target || [])]?.find(
(d) => d.id === connectionStartHandle?.result?.handleId,
(d) => d.id === connectionEndHandle?.handleId,
)) ||
targetNode.handleBounds[type.value ?? 'target']?.[0])) ||
null

View File

@@ -21,7 +21,8 @@ const emits = defineEmits<{
const type = toRef(props, 'type', 'source')
const { connectionStartHandle, vueFlowRef, nodesConnectable, noDragClassName } = useVueFlow()
const { connectionStartHandle, connectionClickStartHandle, connectionEndHandle, vueFlowRef, nodesConnectable, noDragClassName } =
useVueFlow()
const { id: nodeId, node, nodeEl, connectedEdges } = useNode()
@@ -58,10 +59,19 @@ const isConnectable = computed(() => {
const isConnecting = computed(
() =>
connectionStartHandle.value &&
connectionStartHandle.value.nodeId === nodeId &&
connectionStartHandle.value.handleId === handleId.value &&
connectionStartHandle.value.type === type.value,
(connectionStartHandle.value?.nodeId === nodeId &&
connectionStartHandle.value?.handleId === handleId.value &&
connectionStartHandle.value?.type === type.value) ||
(connectionEndHandle.value?.nodeId === nodeId &&
connectionEndHandle.value?.handleId === handleId.value &&
connectionEndHandle.value?.type === type.value),
)
const isClickConnecting = computed(
() =>
connectionClickStartHandle.value?.nodeId === nodeId &&
connectionClickStartHandle.value?.handleId === handleId.value &&
connectionClickStartHandle.value?.type === type.value,
)
// set up handle bounds if they don't exist yet and the node has been initialized (i.e. the handle was added after the node has already been mounted)
@@ -109,7 +119,7 @@ function onPointerDown(event: MouseEvent | TouchEvent) {
}
function onClick(event: MouseEvent) {
if (!connectionStartHandle && !connectableStart) {
if (!nodeId || (!connectionStartHandle && !connectableStart)) {
return
}
@@ -141,10 +151,10 @@ export default {
type,
{
connectable: isConnectable,
connecting: isConnecting,
connecting: isClickConnecting,
connectablestart: connectableStart,
connectableend: connectableEnd,
connectionindicator: (isConnectable && connectableStart && !isConnecting) || (connectableEnd && isConnecting),
connectionindicator: isConnectable && ((connectableStart && !isConnecting) || (connectableEnd && isConnecting)),
},
]"
@mousedown="onPointerDown"

View File

@@ -60,7 +60,7 @@ export default function useHandle({
const doc = getHostForElement(event.target as HTMLElement)
if ((isMouseTriggered && event.button === 0) || !isMouseTriggered) {
const node = findNode(unref(nodeId))
const node = findNode(nodeId.value)
let isValidConnectionHandler = isValidConnection || isValidConnectionProp.value || alwaysValid
@@ -74,7 +74,7 @@ export default function useHandle({
const { x, y } = getEventPosition(event)
const clickedHandle = doc?.elementFromPoint(x, y)
const handleType = getHandleType(unref(edgeUpdaterType), clickedHandle)
const handleType = getHandleType(edgeUpdaterType.value, clickedHandle)
const containerBounds = vueFlowRef.value?.getBoundingClientRect()
if (!containerBounds || !handleType) {
@@ -160,7 +160,7 @@ export default function useHandle({
viewport.value,
)
: connectionPosition,
result,
result.endHandle,
getConnectionStatus(!!prevClosestHandle, isValid),
)
@@ -229,11 +229,11 @@ export default function useHandle({
if (!connectionClickStartHandle.value) {
emits.clickConnectStart({ event, nodeId: nodeId.value, handleId: handleId.value })
startConnection({ nodeId: unref(nodeId), type: unref(type), handleId: unref(handleId) }, undefined, event, true)
startConnection({ nodeId: nodeId.value, type: unref(type), handleId: handleId.value }, undefined, event, true)
} else {
let isValidConnectionHandler = isValidConnection || isValidConnectionProp.value || alwaysValid
const node = findNode(unref(nodeId))
const node = findNode(nodeId.value)
if (!isValidConnectionHandler && node) {
isValidConnectionHandler = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid

View File

@@ -483,15 +483,16 @@ export function useActions(state: State, getters: ComputedGetters): Actions {
state.connectionStartHandle = startHandle
}
state.connectionEndHandle = null
state.connectionStatus = null
if (position) state.connectionPosition = position
}
const updateConnection: Actions['updateConnection'] = (position, result, status = null) => {
const updateConnection: Actions['updateConnection'] = (position, result = null, status = null) => {
if (state.connectionStartHandle) {
state.connectionPosition = position
state.connectionStartHandle.result = result
state.connectionEndHandle = result
state.connectionStatus = status
}
}

View File

@@ -85,6 +85,7 @@ function defaultState(): State {
},
connectionMode: ConnectionMode.Loose,
connectionStartHandle: null,
connectionEndHandle: null,
connectionClickStartHandle: null,
connectionPosition: { x: NaN, y: NaN },
connectionRadius: 20,

View File

@@ -19,17 +19,16 @@ export interface ConnectionHandle {
}
export interface ValidHandleResult {
handleId: string | null
endHandle: ConnectingHandle | null
handleDomNode: Element | null
isValid: boolean
connection: Connection
}
export interface StartHandle {
export interface ConnectingHandle {
nodeId: string
type: HandleType
handleId: string | null
result?: ValidHandleResult
}
/** A valid connection function can determine if an attempted connection is valid or not, i.e. abort creating a new edge */

View File

@@ -27,7 +27,7 @@ import type { CoordinateExtent, GraphNode, Node } from './node'
import type { D3Selection, D3Zoom, D3ZoomHandler, PanOnScrollMode, ViewportFunctions, ViewportTransform } from './zoom'
import type { CustomEvent, FlowHooks, FlowHooksEmit, FlowHooksOn } from './hooks'
import type { EdgeChange, NodeChange, NodeDragItem } from './changes'
import type { StartHandle, ValidConnectionFunc, ValidHandleResult } from './handle'
import type { ConnectingHandle, ValidConnectionFunc } from './handle'
export interface UpdateNodeDimensionsParams {
id: string
@@ -84,8 +84,9 @@ export interface State extends Omit<FlowOptions, 'id' | 'modelValue'> {
connectionLineType: ConnectionLineType | null
/** @deprecated use {@link ConnectionLineOptions.style} */
connectionLineStyle: CSSProperties | null
connectionStartHandle: StartHandle | null
connectionClickStartHandle: StartHandle | null
connectionStartHandle: ConnectingHandle | null
connectionEndHandle: ConnectingHandle | null
connectionClickStartHandle: ConnectingHandle | null
connectionPosition: XYPosition
connectionRadius: number
connectionStatus: ConnectionStatus | null
@@ -247,9 +248,14 @@ export interface Actions extends ViewportFunctions {
/** force update node internal data, if handle bounds are incorrect, you might want to use this */
updateNodeInternals: UpdateNodeInternals
/** start a connection */
startConnection: (startHandle: StartHandle, position?: XYPosition, event?: MouseEvent | TouchEvent, isClick?: boolean) => void
startConnection: (
startHandle: ConnectingHandle,
position?: XYPosition,
event?: MouseEvent | TouchEvent,
isClick?: boolean,
) => void
/** update connection position */
updateConnection: (position: XYPosition, result?: ValidHandleResult, status?: ConnectionStatus | null) => void
updateConnection: (position: XYPosition, result?: ConnectingHandle | null, status?: ConnectionStatus | null) => void
/** end (or cancel) a connection */
endConnection: (event?: MouseEvent | TouchEvent, isClick?: boolean) => void

View File

@@ -78,7 +78,7 @@ export function isValidHandle(
connectionMode: ConnectionMode,
fromNodeId: string,
fromHandleId: string | null,
fromType: string,
fromType: HandleType,
isValidConnection: ValidConnectionFunc,
doc: Document | ShadowRoot,
edges: GraphEdge[],
@@ -92,10 +92,10 @@ export function isValidHandle(
const handleToCheck = handleBelow?.classList.contains('vue-flow__handle') ? handleBelow : handleDomNode
const result: ValidHandleResult = {
handleId: null,
handleDomNode: handleToCheck,
isValid: false,
connection: { source: '', target: '', sourceHandle: null, targetHandle: null },
endHandle: null,
}
if (handleToCheck) {
@@ -113,7 +113,6 @@ export function isValidHandle(
}
result.connection = connection
result.handleId = handleId
const isConnectable = connectable && connectableEnd
@@ -125,6 +124,12 @@ export function isValidHandle(
: handleNodeId !== fromNodeId || handleId !== fromHandleId)
if (isValid) {
result.endHandle = {
nodeId: handleNodeId,
handleId,
type: handleType as HandleType,
}
result.isValid = isValidConnection(connection, {
edges,
sourceNode: findNode(connection.source)!,