gg-editor
Version:
A visual graph editor based on G6 and React
220 lines (172 loc) • 5.88 kB
text/typescript
import isPlainObject from 'lodash/isPlainObject';
import { guid } from '@/utils';
import { ItemType, ItemState, GraphType, AnchorPointState, GraphCustomEvent } from '@/common/constants';
import { Node, Edge, Behavior, GraphEvent, EdgeModel, AnchorPoint } from '@/common/interfaces';
import behaviorManager from '@/common/behaviorManager';
interface DragAddEdgeBehavior extends Behavior {
edge: Edge | null;
isEnabledAnchorPoint(e: GraphEvent): boolean;
isNotSelf(e: GraphEvent): boolean;
canFindTargetAnchorPoint(e: GraphEvent): boolean;
shouldAddDelegateEdge(e: GraphEvent): boolean;
shouldAddRealEdge(): boolean;
handleNodeMouseEnter(e: GraphEvent): void;
handleNodeMouseLeave(e: GraphEvent): void;
handleNodeMouseDown(e: GraphEvent): void;
handleMouseMove(e: GraphEvent): void;
handleMouseUp(e: GraphEvent): void;
}
interface DefaultConfig {
/** 边线类型 */
edgeType: string;
/** 获取来源节点锚点状态 */
getAnchorPointStateOfSourceNode(sourceNode: Node, sourceAnchorPoint: AnchorPoint): AnchorPointState;
/** 获取目标节点锚点状态 */
getAnchorPointStateOfTargetNode(
sourceNode: Node,
sourceAnchorPoint: AnchorPoint,
targetNode: Node,
targetAnchorPoint: AnchorPoint,
): AnchorPointState;
}
const dragAddEdgeBehavior: DragAddEdgeBehavior & ThisType<DragAddEdgeBehavior & DefaultConfig> = {
edge: null,
graphType: GraphType.Flow,
getDefaultCfg(): DefaultConfig {
return {
edgeType: 'bizFlowEdge',
getAnchorPointStateOfSourceNode: () => AnchorPointState.Enabled,
getAnchorPointStateOfTargetNode: () => AnchorPointState.Enabled,
};
},
getEvents() {
return {
'node:mouseenter': 'handleNodeMouseEnter',
'node:mouseleave': 'handleNodeMouseLeave',
'node:mousedown': 'handleNodeMouseDown',
mousemove: 'handleMouseMove',
mouseup: 'handleMouseUp',
};
},
isEnabledAnchorPoint(e) {
const { target } = e;
return !!target.get('isAnchorPoint') && target.get('anchorPointState') === AnchorPointState.Enabled;
},
isNotSelf(e) {
const { edge } = this;
const { item } = e;
return item.getModel().id !== edge.getSource().getModel().id;
},
getTargetNodes(sourceId: string) {
const { graph } = this;
const nodes = graph.getNodes();
return nodes.filter(node => node.getModel().id !== sourceId);
},
canFindTargetAnchorPoint(e) {
return this.isEnabledAnchorPoint(e) && this.isNotSelf(e);
},
shouldAddDelegateEdge(e) {
return this.isEnabledAnchorPoint(e);
},
shouldAddRealEdge() {
const { edge } = this;
const target = edge.getTarget();
return !isPlainObject(target);
},
handleNodeMouseEnter(e) {
const { graph, getAnchorPointStateOfSourceNode } = this;
const sourceNode = e.item as Node;
const sourceAnchorPoints = sourceNode.getAnchorPoints() as AnchorPoint[];
const sourceAnchorPointsState = [];
sourceAnchorPoints.forEach(sourceAnchorPoint => {
sourceAnchorPointsState.push(getAnchorPointStateOfSourceNode(sourceNode, sourceAnchorPoint));
});
sourceNode.set('anchorPointsState', sourceAnchorPointsState);
graph.setItemState(sourceNode, ItemState.ActiveAnchorPoints, true);
},
handleNodeMouseLeave(e) {
const { graph, edge } = this;
const { item } = e;
if (!edge) {
item.set('anchorPointsState', []);
graph.setItemState(item, ItemState.ActiveAnchorPoints, false);
}
},
handleNodeMouseDown(e) {
if (!this.shouldBegin(e) || !this.shouldAddDelegateEdge(e)) {
return;
}
const { graph, edgeType, getAnchorPointStateOfTargetNode } = this;
const { target } = e;
const sourceNode = e.item as Node;
const sourceNodeId = sourceNode.getModel().id;
const sourceAnchorPointIndex = target.get('anchorPointIndex');
const sourceAnchorPoint = sourceNode.getAnchorPoints()[sourceAnchorPointIndex] as AnchorPoint;
const model: EdgeModel = {
id: guid(),
type: edgeType,
source: sourceNodeId,
sourceAnchor: sourceAnchorPointIndex,
target: {
x: e.x,
y: e.y,
} as any,
};
this.edge = graph.addItem(ItemType.Edge, model);
graph.getNodes().forEach(targetNode => {
if (targetNode.getModel().id === sourceNodeId) {
return;
}
const targetAnchorPoints = targetNode.getAnchorPoints() as AnchorPoint[];
const targetAnchorPointsState = [];
targetAnchorPoints.forEach(targetAnchorPoint => {
targetAnchorPointsState.push(
getAnchorPointStateOfTargetNode(sourceNode, sourceAnchorPoint, targetNode, targetAnchorPoint),
);
});
targetNode.set('anchorPointsState', targetAnchorPointsState);
graph.setItemState(targetNode, ItemState.ActiveAnchorPoints, true);
});
},
handleMouseMove(e) {
const { graph, edge } = this;
if (!edge) {
return;
}
if (this.canFindTargetAnchorPoint(e)) {
const { item, target } = e;
const targetId = item.getModel().id;
const targetAnchor = target.get('anchorPointIndex');
graph.updateItem(edge, {
target: targetId,
targetAnchor,
});
} else {
graph.updateItem(edge, {
target: {
x: e.x,
y: e.y,
} as any,
targetAnchor: undefined,
});
}
},
handleMouseUp() {
const { graph, edge } = this;
if (!edge) {
return;
}
if (!this.shouldAddRealEdge()) {
graph.removeItem(this.edge);
}
graph.emit(GraphCustomEvent.onAfterConnect, {
edge: this.edge,
});
this.edge = null;
graph.getNodes().forEach(node => {
node.set('anchorPointsState', []);
graph.setItemState(node, ItemState.ActiveAnchorPoints, false);
});
},
};
behaviorManager.register('drag-add-edge', dragAddEdgeBehavior);