Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/core/src/model/EditConfigModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export interface IEditConfigType {
// 开启网格对齐
snapGrid: boolean
isPinching: boolean
anchorOnlyConnectValidate: boolean
}

export type IConfigKeys = keyof IEditConfigType
Expand Down Expand Up @@ -188,6 +189,7 @@ const allKeys = [
'nodeTextVertical', // 节点文本是否纵向显示
'edgeTextVertical', // 边文本是否纵向显示
'isPinching', //是否是双指捏合态
'anchorOnlyConnectValidate', // 仅在靠近锚点时触发连接校验
] as const

/**
Expand Down Expand Up @@ -223,6 +225,7 @@ export class EditConfigModel {
@observable edgeTextDraggable = false
@observable edgeTextMultiple = false // 是否支持多个边文本
@observable edgeTextVertical = false // 边文本朝向是否是纵向
@observable anchorOnlyConnectValidate = false // 仅在靠近锚点时触发连接校验
/*********************************************************
* 节点相关配置
********************************************************/
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/model/edge/BaseEdgeModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ export class BaseEdgeModel<P extends PropertiesType = PropertiesType>
{
readonly BaseType = ElementType.EDGE
static BaseType: ElementType = ElementType.EDGE
static preferredTargetAnchorIdMap: Map<string, string> = new Map()
static setTargetAnchorId(nodeId: string, anchorId?: string) {
if (anchorId) {
this.preferredTargetAnchorIdMap.set(nodeId, anchorId)
} else {
this.preferredTargetAnchorIdMap.delete(nodeId)
}
}

static getTargetAnchorId(nodeId: string) {
return this.preferredTargetAnchorIdMap.get(nodeId)
}

// 数据属性
public id = ''
Expand Down
21 changes: 19 additions & 2 deletions packages/core/src/model/node/BaseNodeModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
isUndefined,
set,
} from 'lodash-es'
import { GraphModel, Model } from '..'
import { GraphModel, Model, BaseEdgeModel } from '..'
import LogicFlow from '../../LogicFlow'
import {
createUuid,
Expand Down Expand Up @@ -150,7 +150,11 @@ export class BaseNodeModel<P extends PropertiesType = PropertiesType>
moveRules: Model.NodeMoveRule[] = [] // 节点移动之前的hook
resizeRules: Model.NodeResizeRule[] = [] // 节点resize之前的hook
hasSetTargetRules = false // 用来限制rules的重复值
hasSetSourceRules = false; // 用来限制rules的重复值
hasSetSourceRules = false // 用来限制rules的重复值
customTargetAnchor?: (
position: Point,
nodeModel: BaseNodeModel,
) => Model.AnchorInfo | undefined;
[propName: string]: any // 支持用户自定义属性

constructor(data: NodeConfig<P>, graphModel: GraphModel) {
Expand Down Expand Up @@ -635,6 +639,19 @@ export class BaseNodeModel<P extends PropertiesType = PropertiesType>
* 手动连接边到节点时,需要连接的锚点
*/
public getTargetAnchor(position: Point): Model.AnchorInfo {
if (typeof this.customTargetAnchor === 'function') {
const info = this.customTargetAnchor(position, this)
if (info) return info
}
const preferredId = BaseEdgeModel.getTargetAnchorId(this.id)
if (preferredId) {
const anchors = this.getAnchorsByOffset()
console.log(anchors)
const idx = anchors.findIndex((a) => a.id == preferredId)
if (idx >= 0) {
return { index: idx, anchor: anchors[idx] }
}
}
return getClosestAnchor(position, this)
}

Expand Down
1 change: 0 additions & 1 deletion packages/core/src/util/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ export class StepDrag {
const DOC: any = window?.document
if (e.button !== LEFT_MOUSE_BUTTON_CODE) return
if (this.isStopPropagation) e.stopPropagation()
e.preventDefault()
this.isStartDragging = true
this.startX = e.clientX
this.startY = e.clientY
Expand Down
80 changes: 52 additions & 28 deletions packages/core/src/view/Anchor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,41 +314,26 @@ class Anchor extends Component<IProps, IState> {
return
}
this.preTargetNode = targetNode
// 支持节点的每个锚点单独设置是否可连接,因此规则key去nodeId + anchorId作为唯一值
const targetInfoId = `${nodeModel.id}_${targetNode.id}_${anchorId}_${anchorData.id}`

// 查看鼠标是否进入过target,若有检验结果,表示进入过, 就不重复计算了。
if (!this.targetRuleResults.has(targetInfoId)) {
const targetAnchor = info.anchor
const sourceRuleResult = nodeModel.isAllowConnectedAsSource(
const d = distance(endX, endY, info.anchor.x, info.anchor.y)
const validateDistance = 10
const { editConfigModel } = graphModel
if (editConfigModel.anchorOnlyConnectValidate && d <= validateDistance) {
this.validateAndSetState(
targetNode,
anchorId,
info.anchor,
nodeModel,
anchorData,
targetAnchor,
)
const targetRuleResult = targetNode.isAllowConnectedAsTarget(
} else if (!editConfigModel.anchorOnlyConnectValidate) {
this.validateAndSetState(
targetNode,
anchorId,
info.anchor,
nodeModel,
anchorData,
targetAnchor,
)
this.sourceRuleResults.set(
targetInfoId,
formatAnchorConnectValidateData(sourceRuleResult),
)
this.targetRuleResults.set(
targetInfoId,
formatAnchorConnectValidateData(targetRuleResult),
)
}
const { isAllPass: isSourcePass } =
this.sourceRuleResults.get(targetInfoId) ?? {}
const { isAllPass: isTargetPass } =
this.targetRuleResults.get(targetInfoId) ?? {}
// 实时提示出即将链接的锚点
if (isSourcePass && isTargetPass) {
targetNode.setElementState(ElementState.ALLOW_CONNECT)
} else {
targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT)
}
// 人工触发进入目标节点事件,同步设置 hovered 以驱动锚点显隐和样式
if (!targetNode.isHovered) {
const nodeData = targetNode.getData()
Expand Down Expand Up @@ -379,6 +364,45 @@ class Anchor extends Component<IProps, IState> {
}
}

validateAndSetState(
targetNode: BaseNodeModel,
anchorId: string | undefined,
targetAnchor: AnchorConfig,
nodeModel: BaseNodeModel,
anchorData: AnchorConfig,
) {
const targetInfoId = `${nodeModel.id}_${targetNode.id}_${anchorId}_${anchorData.id}`
if (!this.targetRuleResults.has(targetInfoId)) {
const sourceRuleResult = nodeModel.isAllowConnectedAsSource(
targetNode,
anchorData,
targetAnchor,
)
const targetRuleResult = targetNode.isAllowConnectedAsTarget(
nodeModel,
anchorData,
targetAnchor,
)
this.sourceRuleResults.set(
targetInfoId,
formatAnchorConnectValidateData(sourceRuleResult),
)
this.targetRuleResults.set(
targetInfoId,
formatAnchorConnectValidateData(targetRuleResult),
)
}
const { isAllPass: isSourcePass } =
this.sourceRuleResults.get(targetInfoId) ?? {}
const { isAllPass: isTargetPass } =
this.targetRuleResults.get(targetInfoId) ?? {}
if (isSourcePass && isTargetPass) {
targetNode.setElementState(ElementState.ALLOW_CONNECT)
} else {
targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT)
}
}

isShowLine() {
const { startX, startY, endX, endY } = this.state
const v = distance(startX, startY, endX, endY)
Expand Down
24 changes: 13 additions & 11 deletions packages/extension/src/insert-node-in-polyline/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import LogicFlow, {
PolylineEdgeModel,
EventType,
formatAnchorConnectValidateData,
getClosestAnchor,
} from '@logicflow/core'
import { cloneDeep } from 'lodash-es'
import { isNodeInSegment } from './edge'
Expand Down Expand Up @@ -132,7 +133,6 @@ export class InsertNodeInPolyline {
} = edges[i]
// fix https://github.com/didi/LogicFlow/issues/996
const startPoint = cloneDeep(pointsList[0])
const endPoint = cloneDeep(crossPoints.startCrossPoint)
this._lf.deleteEdge(id)
const checkResult = this.checkRuleBeforeInsetNode(
sourceNodeId,
Expand All @@ -141,27 +141,29 @@ export class InsertNodeInPolyline {
targetAnchorId!,
nodeData,
)
const startAnchorInfo = getClosestAnchor(
crossPoints.startCrossPoint,
nodeModel,
)
const startAnchor = startAnchorInfo.anchor
this._lf.addEdge({
type,
sourceNodeId,
targetNodeId: nodeData.id,
startPoint,
endPoint,
pointsList: [
...pointsList.slice(0, crossIndex),
crossPoints.startCrossPoint,
],
endPoint: startAnchor,
})
const endAnchorInfo = getClosestAnchor(
crossPoints.endCrossPoint,
nodeModel,
)
const endAnchor = endAnchorInfo.anchor
this._lf.addEdge({
type,
sourceNodeId: nodeData.id,
targetNodeId,
startPoint: cloneDeep(crossPoints.endCrossPoint),
startPoint: cloneDeep(endAnchor),
endPoint: cloneDeep(pointsList[pointsList.length - 1]),
pointsList: [
crossPoints.endCrossPoint,
...pointsList.slice(crossIndex),
],
})
if (!checkResult.isPass) {
this._lf.graphModel.eventCenter.emit(
Expand Down