diff --git a/packages/core/src/algorithm/outline.ts b/packages/core/src/algorithm/outline.ts index d55e5a9bf..0f8b15321 100644 --- a/packages/core/src/algorithm/outline.ts +++ b/packages/core/src/algorithm/outline.ts @@ -66,7 +66,7 @@ export const getEdgeOutline = ( edge: BaseEdgeModel, ): OutlineInfo | undefined => { if (edge.modelType === ModelType.LINE_EDGE) { - return getLineOutline(edge) + return getLineOutline(edge as LineEdgeModel) } if (edge.modelType === ModelType.POLYLINE_EDGE) { return getPolylineOutline(edge as PolylineEdgeModel) diff --git a/packages/core/src/constant/index.ts b/packages/core/src/constant/index.ts index 238b5e5c0..994e6afba 100644 --- a/packages/core/src/constant/index.ts +++ b/packages/core/src/constant/index.ts @@ -57,6 +57,8 @@ export enum EventType { NODE_CONTEXTMENU = 'node:contextmenu', NODE_ROTATE = 'node:rotate', NODE_RESIZE = 'node:resize', + NODE_FOCUS = 'node:focus', + NODE_BLUR = 'node:blur', // 节点 properties 变化事件 NODE_PROPERTIES_CHANGE = 'node:properties-change', @@ -67,6 +69,8 @@ export enum EventType { EDGE_DELETE = 'edge:delete', EDGE_CLICK = 'edge:click', EDGE_DBCLICK = 'edge:dbclick', + EDGE_FOCUS = 'edge:focus', + EDGE_BLUR = 'edge:blur', EDGE_MOUSEENTER = 'edge:mouseenter', EDGE_MOUSELEAVE = 'edge:mouseleave', diff --git a/packages/core/src/event/eventArgs.ts b/packages/core/src/event/eventArgs.ts index b2667e62b..e30d29d19 100644 --- a/packages/core/src/event/eventArgs.ts +++ b/packages/core/src/event/eventArgs.ts @@ -188,6 +188,14 @@ interface NodeEventArgs { */ properties: Record } + /** + * 节点获焦 + */ + 'node:focus': NodeEventArgsPick<'data'> + /** + * 节点失焦 + */ + 'node:blur': NodeEventArgsPick<'data'> } type EdgeEventArgsPick = Pick< @@ -259,6 +267,14 @@ interface EdgeEventArgs { oldEdge: EdgeData } } + /** + * 边获焦 + */ + 'edge:focus': EdgeEventArgsPick<'data'> + /** + * 边失焦 + */ + 'edge:blur': EdgeEventArgsPick<'data'> } /** diff --git a/packages/core/src/model/GraphModel.ts b/packages/core/src/model/GraphModel.ts index 651797531..23ff233bc 100644 --- a/packages/core/src/model/GraphModel.ts +++ b/packages/core/src/model/GraphModel.ts @@ -1,4 +1,4 @@ -import { find, forEach, map, merge, isBoolean, debounce } from 'lodash-es' +import { find, forEach, map, merge, isBoolean, debounce, isEqual } from 'lodash-es' import { action, computed, observable } from 'mobx' import { BaseEdgeModel, @@ -41,6 +41,7 @@ import Position = LogicFlow.Position import PointTuple = LogicFlow.PointTuple import GraphData = LogicFlow.GraphData import NodeConfig = LogicFlow.NodeConfig +import AnchorConfig = Model.AnchorConfig import BaseNodeModelCtor = LogicFlow.BaseNodeModelCtor import BaseEdgeModelCtor = LogicFlow.BaseEdgeModelCtor @@ -462,6 +463,58 @@ export class GraphModel { throw new Error(`找不到${edge.type}对应的边。`) } const edgeModel = new Model(edge, this) + // 根据edgeModel中存储的数据找到当前画布上的起终锚点坐标 + // 判断当前起终锚点数据和Model中存储的起终点数据是否一致,不一致更新起终点信息 + const { + sourceNodeId, + targetNodeId, + sourceAnchorId = '', + targetAnchorId = '', + startPoint, + endPoint, + text, + textPosition, + } = edgeModel + const updateAnchorPoint = ( + node: BaseNodeModel | undefined, + anchorId: string, + point: Position, + updatePoint: (anchor: AnchorConfig) => void, + ) => { + const anchor = node?.anchors.find((anchor) => anchor.id === anchorId) + if (anchor && !isEqual(anchor, point)) { + updatePoint(anchor) + } + } + + const sourceNode = this.getNodeModelById(sourceNodeId) + const targetNode = this.getNodeModelById(targetNodeId) + + updateAnchorPoint( + sourceNode, + sourceAnchorId, + startPoint, + edgeModel.updateStartPoint.bind(edgeModel), + ) + updateAnchorPoint( + targetNode, + targetAnchorId, + endPoint, + edgeModel.updateEndPoint.bind(edgeModel), + ) + + // 而文本需要先算一下文本与默认文本位置之间的相对位置差 + // 再计算新路径的文本默认位置,加上相对位置差,得到调整后边的文本的位置 + if (text) { + const { x, y } = text + const { x: defaultX, y: defaultY } = textPosition + if (x && y && defaultX && defaultY) { + const deltaX = x - defaultX + const deltaY = y - defaultY + edgeModel.resetTextPosition() + edgeModel.moveText(deltaX, deltaY) + } + } this.edgeModelMap.set(edgeModel.id, edgeModel) this.elementsModelMap.set(edgeModel.id, edgeModel) diff --git a/packages/core/src/model/edge/LineEdgeModel.ts b/packages/core/src/model/edge/LineEdgeModel.ts index 42c6c6fb2..e6843c282 100644 --- a/packages/core/src/model/edge/LineEdgeModel.ts +++ b/packages/core/src/model/edge/LineEdgeModel.ts @@ -17,6 +17,14 @@ export class LineEdgeModel extends BaseEdgeModel { ...cloneDeep(customStyle), } } + initEdgeData(data: LogicFlow.EdgeConfig): void { + super.initEdgeData(data) + this.points = this.getPath([this.startPoint, this.endPoint]) + } + getPath(points: Point[]): string { + const [start, end] = points + return `${start.x},${start.y} ${end.x},${end.y}` + } getTextPosition(): Point { return { x: (this.startPoint.x + this.endPoint.x) / 2, diff --git a/packages/core/src/model/edge/PolylineEdgeModel.ts b/packages/core/src/model/edge/PolylineEdgeModel.ts index 78b5ae22f..d55b5c085 100644 --- a/packages/core/src/model/edge/PolylineEdgeModel.ts +++ b/packages/core/src/model/edge/PolylineEdgeModel.ts @@ -326,12 +326,14 @@ export class PolylineEdgeModel extends BaseEdgeModel { }) } + getPath(points: Point[]): string { + return points.map((point) => `${point.x},${point.y}`).join(' ') + } + @action initPoints() { if (this.pointsList.length > 0) { - this.points = this.pointsList - .map((point) => `${point.x},${point.y}`) - .join(' ') + this.points = this.getPath(this.pointsList) } else { this.updatePoints() } diff --git a/packages/core/src/style/index.less b/packages/core/src/style/index.less index 6cbfb8cb4..a4555957a 100644 --- a/packages/core/src/style/index.less +++ b/packages/core/src/style/index.less @@ -19,6 +19,11 @@ cursor: move; } +// 在元素focus时浏览器会给元素outline设置一个5像素宽的默认样式,这里先全局禁用一下,后续再根据需要再做调整 +*:focus { + outline: none; +} + .lf-node-anchor { cursor: crosshair; } diff --git a/packages/core/src/style/raw.ts b/packages/core/src/style/raw.ts index b73733f07..11531c6e5 100644 --- a/packages/core/src/style/raw.ts +++ b/packages/core/src/style/raw.ts @@ -21,6 +21,9 @@ export const content = `.lf-graph { .lf-text-draggable { cursor: move; } +*:focus { + outline: none; +} .lf-node-anchor { cursor: crosshair; } diff --git a/packages/core/src/view/edge/BaseEdge.tsx b/packages/core/src/view/edge/BaseEdge.tsx index db878a6b6..890f08707 100644 --- a/packages/core/src/view/edge/BaseEdge.tsx +++ b/packages/core/src/view/edge/BaseEdge.tsx @@ -459,6 +459,20 @@ export abstract class BaseEdge

extends Component< this.toFront() } + handleFocus = () => { + const { model, graphModel } = this.props + graphModel.eventCenter.emit(EventType.EDGE_FOCUS, { + data: model.getData(), + }) + } + + handleBlur = () => { + const { model, graphModel } = this.props + graphModel.eventCenter.emit(EventType.EDGE_BLUR, { + data: model.getData(), + }) + } + /** * @overridable 支持重写, 此方法为获取边的形状,如果需要自定义边的形状,请重写此方法。 * @example https://docs.logic-flow.cn/docs/#/zh/guide/basic/edge?id=%e5%9f%ba%e4%ba%8e-react-%e7%bb%84%e4%bb%b6%e8%87%aa%e5%ae%9a%e4%b9%89%e8%be%b9 @@ -499,6 +513,8 @@ export abstract class BaseEdge

extends Component< onMouseOver={this.setHoverOn} onMouseEnter={this.setHoverOn} onMouseLeave={this.setHoverOff} + onFocus={this.handleFocus} + onBlur={this.handleBlur} > {this.getShape()} {this.getAppend()} diff --git a/packages/core/src/view/node/BaseNode.tsx b/packages/core/src/view/node/BaseNode.tsx index bfde37d11..72e2e5a5e 100644 --- a/packages/core/src/view/node/BaseNode.tsx +++ b/packages/core/src/view/node/BaseNode.tsx @@ -424,6 +424,20 @@ export abstract class BaseNode

extends Component< } } + handleFocus = () => { + const { model, graphModel } = this.props + graphModel.eventCenter.emit(EventType.NODE_FOCUS, { + data: model.getData(), + }) + } + + handleBlur = () => { + const { model, graphModel } = this.props + graphModel.eventCenter.emit(EventType.NODE_BLUR, { + data: model.getData(), + }) + } + // 因为自定义节点的时候,可能会基于hover状态自定义不同的样式。 setHoverOn = (ev: MouseEvent) => { const { model, graphModel } = this.props @@ -508,6 +522,8 @@ export abstract class BaseNode

extends Component< onMouseLeave={this.setHoverOff} onMouseOut={this.onMouseOut} onContextMenu={this.handleContextMenu} + onFocus={this.handleFocus} + onBlur={this.handleBlur} {...restAttributes} > {nodeShapeInner} diff --git a/packages/core/src/view/overlay/OutlineOverlay.tsx b/packages/core/src/view/overlay/OutlineOverlay.tsx index 97eabb7f6..cdaabe72f 100644 --- a/packages/core/src/view/overlay/OutlineOverlay.tsx +++ b/packages/core/src/view/overlay/OutlineOverlay.tsx @@ -82,7 +82,7 @@ export class OutlineOverlay extends Component { (hoverOutline && edge.isHovered) ) { if (edge.modelType === ModelType.LINE_EDGE) { - edgeOutline.push(this.getLineOutline(edge)) + edgeOutline.push(this.getLineOutline(edge as LineEdgeModel)) } else if (edge.modelType === ModelType.POLYLINE_EDGE) { edgeOutline.push(this.getPolylineOutline(edge as PolylineEdgeModel)) } else if (edge.modelType === ModelType.BEZIER_EDGE) {