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:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ function defaultState(): State {
|
||||
},
|
||||
connectionMode: ConnectionMode.Loose,
|
||||
connectionStartHandle: null,
|
||||
connectionEndHandle: null,
|
||||
connectionClickStartHandle: null,
|
||||
connectionPosition: { x: NaN, y: NaN },
|
||||
connectionRadius: 20,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)!,
|
||||
|
||||
Reference in New Issue
Block a user