@logicflow/extension
Version:
LogicFlow Extensions
456 lines (455 loc) • 22.7 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamicGroupNodeModel = void 0;
var core_1 = require("@logicflow/core");
var lodash_es_1 = require("lodash-es");
// 分组节点默认展开时的大小
var DEFAULT_GROUP_EXPAND_WIDTH = 400;
var DEFAULT_GROUP_EXPAND_HEIGHT = 230;
// 分组节点默认收起时的大小
var DEFAULT_GROUP_COLLAPSE_WIDTH = 80;
var DEFAULT_GROUP_COLLAPSE_HEIGHT = 60;
var DEFAULT_BOTTOM_Z_INDEX = -10000;
var DynamicGroupNodeModel = /** @class */ (function (_super) {
__extends(DynamicGroupNodeModel, _super);
function DynamicGroupNodeModel(data, graphModel) {
var _this = _super.call(this, data, graphModel) || this;
_this.isGroup = true;
// 是否限制组内节点的移动范围。默认不限制 TODO: 完善该功能
_this.isRestrict = false;
// isRestrict 模式启用时,如果同时设置 autoResize 为 true,那么子节点在父节点中移动时,父节点会自动调整大小
_this.autoResize = false;
// 分组节点是否可以折叠
_this.collapsible = true;
// 当前组是否收起状态
_this.isCollapsed = false;
// 当前分组是否在可添加状态 - 实时状态
_this.groupAddable = false;
// 缩放或旋转容器时,是否缩放或旋转组内节点
_this.transformWithContainer = false;
_this.childrenLastCollapseStateDict = new Map();
_this.childrenLastCollapseStateDict = new Map();
_this.initNodeData(data);
_this.setAttributes();
return _this;
}
DynamicGroupNodeModel.prototype.initNodeData = function (data) {
var _a;
_super.prototype.initNodeData.call(this, data);
var _b = (_a = data.properties) !== null && _a !== void 0 ? _a : {}, children = _b.children, width = _b.width, height = _b.height, collapsedWidth = _b.collapsedWidth, collapsedHeight = _b.collapsedHeight, collapsible = _b.collapsible, isCollapsed = _b.isCollapsed, zIndex = _b.zIndex, isRestrict = _b.isRestrict, autoResize = _b.autoResize, autoToFront = _b.autoToFront, transformWithContainer = _b.transformWithContainer;
this.children = children ? new Set(children) : new Set();
this.zIndex = zIndex !== null && zIndex !== void 0 ? zIndex : DEFAULT_BOTTOM_Z_INDEX;
this.isCollapsed = isCollapsed !== null && isCollapsed !== void 0 ? isCollapsed : false;
this.setProperties({ isCollapsed: isCollapsed !== null && isCollapsed !== void 0 ? isCollapsed : false });
var expandWidth = width !== null && width !== void 0 ? width : DEFAULT_GROUP_EXPAND_WIDTH;
var expandHeight = height !== null && height !== void 0 ? height : DEFAULT_GROUP_EXPAND_HEIGHT;
// 初始化分组节点的宽高数据
this.width = expandWidth;
this.height = expandHeight;
this.expandWidth = expandWidth;
this.expandHeight = expandHeight;
this.collapsedWidth = collapsedWidth !== null && collapsedWidth !== void 0 ? collapsedWidth : DEFAULT_GROUP_COLLAPSE_WIDTH;
this.collapsedHeight = collapsedHeight !== null && collapsedHeight !== void 0 ? collapsedHeight : DEFAULT_GROUP_COLLAPSE_HEIGHT;
this.isRestrict = isRestrict !== null && isRestrict !== void 0 ? isRestrict : false;
this.transformWithContainer = transformWithContainer !== null && transformWithContainer !== void 0 ? transformWithContainer : false;
this.autoResize = autoResize !== null && autoResize !== void 0 ? autoResize : false;
this.collapsible = collapsible !== null && collapsible !== void 0 ? collapsible : true;
this.autoToFront = autoToFront !== null && autoToFront !== void 0 ? autoToFront : false;
// 禁用掉 Group 节点的文本编辑能力
this.text.editable = false;
this.text.draggable = false;
};
DynamicGroupNodeModel.prototype.setAttributes = function () {
_super.prototype.setAttributes.call(this);
};
DynamicGroupNodeModel.prototype.getData = function () {
var _this = this;
var data = _super.prototype.getData.call(this);
var children = [];
(0, lodash_es_1.forEach)(Array.from(this.children), function (childId) {
var model = _this.graphModel.getNodeModelById(childId);
if (model && !model.virtual) {
children.push(childId);
}
});
data.children = children;
if (data.properties) {
data.properties.children = children;
data.properties.isCollapsed = this.isCollapsed;
}
return data;
};
/**
* 获取分组内的节点
* @param groupModel
*/
DynamicGroupNodeModel.prototype.getNodesInGroup = function (groupModel) {
var nodeIds = [];
if (groupModel.isGroup) {
(0, lodash_es_1.forEach)(Array.from(groupModel.children), function (nodeId) {
nodeIds.push(nodeId);
});
}
return nodeIds;
};
DynamicGroupNodeModel.prototype.getMoveDistance = function (deltaX, deltaY, isIgnoreRule) {
if (isIgnoreRule === void 0) { isIgnoreRule = false; }
var _a = __read(_super.prototype.getMoveDistance.call(this, deltaX, deltaY, isIgnoreRule), 2), moveDeltaX = _a[0], moveDeltaY = _a[1];
var nodeIds = this.getNodesInGroup(this);
this.graphModel.moveNodes(nodeIds, deltaX, deltaY, isIgnoreRule);
return [moveDeltaX, moveDeltaY];
};
/**
* 重写 getHistoryData 方法
*/
DynamicGroupNodeModel.prototype.getHistoryData = function () {
var data = _super.prototype.getHistoryData.call(this);
data.children = Array.from(this.children);
data.isGroup = true;
var _a = this, x = _a.x, y = _a.y, collapsedWidth = _a.collapsedWidth, collapsedHeight = _a.collapsedHeight, expandWidth = _a.expandWidth, expandHeight = _a.expandHeight, isCollapsed = _a.isCollapsed;
if (isCollapsed) {
// 如果当前是折叠模式
// 存入history的时候,将坐标恢复到未折叠前的坐标数据
// 因为拿出history数据的时候,会触发collapse()进行坐标的折叠计算
data.x = x + expandWidth / 2 - collapsedWidth / 2;
data.y = y + expandHeight / 2 - collapsedHeight / 2;
if (data.text) {
data.text.x = data.text.x + expandWidth / 2 - collapsedWidth / 2;
data.text.y = data.text.y + expandHeight / 2 - collapsedHeight / 2;
}
}
return data;
};
/**
* 触发分组节点的「折叠 or 展开」动作
* 1. 折叠分组的宽高
* 2. 处理分组子节点
* 3. 处理连线
* @param collapse {boolean} true -> 折叠;false -> 展开
*/
DynamicGroupNodeModel.prototype.toggleCollapse = function (collapse) {
var _this = this;
var graphModel = this.graphModel;
var nextCollapseState = !!collapse;
// DONE: 通过 setProperty 设置 isCollapsed 的值 -> 否则无法触发 node:properties-changed 事件
this.isCollapsed = nextCollapseState;
this.setProperties({ isCollapsed: nextCollapseState });
graphModel.eventCenter.emit('dynamicGroup:collapse', {
collapse: nextCollapseState,
nodeModel: this,
});
// step 1
if (nextCollapseState) {
this.collapse();
}
else {
this.expand();
}
// step 2
var allRelatedEdges = __spreadArray(__spreadArray([], __read(this.incoming.edges), false), __read(this.outgoing.edges), false);
var childrenArr = Array.from(this.children);
(0, lodash_es_1.forEach)(childrenArr, function (elementId) {
var _a, _b;
// FIX: 当使用 graphModel.getElement 获取元素时,会因为
// const model = this.graphModel.getElement(elementId)
var model = _this.graphModel.elementsModelMap.get(elementId);
if (model) {
// TODO: ??? 普通节点有这个属性吗?确认这个代码的意义
var collapseStatus = model.isCollapsed;
// FIX: https://github.com/didi/LogicFlow/issues/1007
// 下面代码片段,针对 Group 节点执行
if (model.isGroup) {
var groupModel = model;
if (!groupModel.isCollapsed) {
// 正常情况下,parent 折叠后,children 也应该折叠
// 因此当前 parent 准备展开时,children 的目前状态肯定是折叠状态,也就是 model.isCollapsed 为 true,这个代码块不会触发
// 只有当 parent 准备折叠时,children 目前状态才有可能是展开
// 即 model.isCollapsed 为 false,这个代码块触发, 此时 isCollapse 为 true,触发 children 也进行折叠
groupModel.toggleCollapse(collapse);
}
if (!collapse) {
// 当 parent 准备展开时,children 的值应该恢复到折叠前的状态
var lastCollapseStatus = (_a = _this.childrenLastCollapseStateDict) === null || _a === void 0 ? void 0 : _a.get(elementId);
if (lastCollapseStatus !== undefined &&
lastCollapseStatus !== model.isCollapsed) {
// https://github.com/didi/LogicFlow/issues/1145
// 当parent准备展开时,children的值肯定是折叠,也就是nodeModel.isCollapsed=true
// 当parent准备展开时,如果children之前的状态是展开,则恢复展开状态
groupModel.toggleCollapse(lastCollapseStatus);
}
}
}
(_b = _this.childrenLastCollapseStateDict) === null || _b === void 0 ? void 0 : _b.set(elementId, !!collapseStatus);
model.visible = !collapse;
// 判断,如果是节点时,才去读取节点的 incoming 和 outgoing
if (model.BaseType === core_1.ElementType.NODE) {
var incomingEdges = model.incoming.edges;
var outgoingEdges = model.outgoing.edges;
allRelatedEdges = __spreadArray(__spreadArray(__spreadArray([], __read(allRelatedEdges), false), __read(incomingEdges), false), __read(outgoingEdges), false);
}
}
});
// step 3
this.collapseEdge(nextCollapseState, allRelatedEdges);
};
// 折叠操作
DynamicGroupNodeModel.prototype.collapse = function () {
var _a = this, x = _a.x, y = _a.y, text = _a.text, width = _a.width, height = _a.height, collapsedWidth = _a.collapsedWidth, collapsedHeight = _a.collapsedHeight;
this.x = x - width / 2 + collapsedWidth / 2;
this.y = y - height / 2 + collapsedHeight / 2;
this.text.x = text.x - width / 2 + collapsedWidth / 2;
this.text.y = text.y - height / 2 + collapsedHeight / 2;
// 记录折叠前的节点大小,并将其记录到 expandWidth 中
this.expandWidth = width;
this.expandHeight = height;
this.width = collapsedWidth;
this.height = collapsedHeight;
};
// 展开操作
DynamicGroupNodeModel.prototype.expand = function () {
var _a = this, x = _a.x, y = _a.y, text = _a.text, expandWidth = _a.expandWidth, expandHeight = _a.expandHeight, collapsedWidth = _a.collapsedWidth, collapsedHeight = _a.collapsedHeight;
this.width = expandWidth;
this.height = expandHeight;
// 重新计算节点及文本的坐标
this.x = x + this.width / 2 - collapsedWidth / 2;
this.y = y + this.height / 2 - collapsedHeight / 2;
this.text.x = text.x + this.width / 2 - collapsedWidth / 2;
this.text.y = text.y + this.height / 2 - collapsedHeight / 2;
};
DynamicGroupNodeModel.prototype.createVirtualEdge = function (edgeConfig) {
edgeConfig.pointsList = undefined;
var virtualEdge = this.graphModel.addEdge(edgeConfig);
virtualEdge.virtual = true;
// TODO: 强制不保存 group 连线数据???-> 为什么注释掉?是不是不能强制设置为 null,会导致无法回填
// virtualEdge.getData = () => null
virtualEdge.text.editable = false;
virtualEdge.isCollapsedEdge = true; // 这一行干啥的,TODO: 项目中没搜到应用的地方,是否应该移除
};
/**
* 折叠分组的时候,需要处理分组自身的连线和分组内部子节点上的连线
* 边的分类:
* - 虚拟边:分组被收起时,标识分组本身与外部节点关系的边
* - 真实边:分组本身或者分组内部节点与外部节点(非收起分组)关系的边
* 如果一个分组,本身与外部节点有 M 条连线,且内部 N 个子节点与外部节点有连线,那么这个分组收起时会生成 M+N 条连线
* 折叠分组时:
* - 原有的虚拟边删除
* - 创建一个虚拟边
* - 真实边则隐藏
* 展开分组时:
* - 当前的虚拟边删除
* - 如果外部节点是收起的分组,则创建虚拟边
* - 如果外部节点是普通节点,则显示真实边
*
* @param collapse
* @param edges
*/
DynamicGroupNodeModel.prototype.collapseEdge = function (collapse, edges) {
var _this = this;
var graphModel = this.graphModel;
(0, lodash_es_1.forEach)(edges, function (edge, idx) {
var _a;
var edgeData = edge.getData();
var targetNodeId = edgeData.targetNodeId, sourceNodeId = edgeData.sourceNodeId;
var edgeConfig = __assign(__assign({}, edgeData), { id: "".concat(edgeData.id, "__").concat(idx), text: (_a = edgeData.text) === null || _a === void 0 ? void 0 : _a.value });
if (edge.virtual) {
graphModel.deleteEdgeById(edge.id);
}
// 考虑目标节点也属于分组的情况
var targetNodeGroup = graphModel.dynamicGroup.getGroupByNodeId(targetNodeId);
if (!targetNodeGroup) {
targetNodeGroup = graphModel.getNodeModelById(targetNodeId);
}
// 考虑源节点也属于分组的情况
var sourceNodeGroup = graphModel.dynamicGroup.getGroupByNodeId(sourceNodeId);
if (!sourceNodeGroup) {
sourceNodeGroup = graphModel.getNodeModelById(sourceNodeId);
}
// 折叠时,处理未被隐藏的边的逻辑 -> collapse
if (collapse && edge.visible) {
// 需要确认此分组节点是新连线的起点还是终点
// 创建一个虚拟边,虚拟边相对于真实边,起点或者终点从一起分组节点的中心点开始 TODO:??? 确认什么意思
// 如果需要被隐藏的边的起点在需要折叠的分组中,那么设置虚拟边的开始节点为此分组
if (_this.children.has(sourceNodeId) || _this.id === sourceNodeId) {
edgeConfig.startPoint = undefined;
edgeConfig.sourceNodeId = _this.id;
}
else {
edgeConfig.endPoint = undefined;
edgeConfig.targetNodeId = _this.id;
}
// 如果边的起点和终点都在分组内部,则不创建新的虚拟边
if (targetNodeGroup.id !== _this.id || sourceNodeGroup.id !== _this.id) {
_this.createVirtualEdge(edgeConfig);
}
edge.visible = false;
}
// 展开时,处理被隐藏的边的逻辑 -> expand
if (!collapse && !edge.visible) {
// 展开分组时:判断真实边的起点和中带你是否有任一节点在已折叠分组中,如果不是,则显示真实边
// 如果是,则修改这个边的对应目标节点 id 来创建虚拟边
if (targetNodeGroup &&
targetNodeGroup.isGroup &&
targetNodeGroup.isCollapsed) {
edgeConfig.targetNodeId = targetNodeGroup.id;
edgeConfig.endPoint = undefined;
_this.createVirtualEdge(edgeConfig);
}
else if (sourceNodeGroup &&
sourceNodeGroup.isGroup &&
sourceNodeGroup.isCollapsed) {
edgeConfig.sourceNodeId = sourceNodeGroup.id;
edgeConfig.startPoint = undefined;
_this.createVirtualEdge(edgeConfig);
}
else {
edge.visible = true;
}
}
});
};
/**
* 是否允许此节点添加到该分组
* TODO: 如何重写该方法呢?
* @param _nodeData
*/
// eslint-disable-next-line
DynamicGroupNodeModel.prototype.isAllowAppendIn = function (_nodeData) {
// TODO: 此处使用 this.properties.groupAddable 还是 this.groupAddable
// this.groupAddable 是否存在更新不及时的问题
return true;
};
/**
* 更新分组节点是否允许添加节点的属性
* @param isAllow
*/
DynamicGroupNodeModel.prototype.setAllowAppendChild = function (isAllow) {
// this.setProperty('groupAddable', isAllow)
this.groupAddable = isAllow;
};
/**
* 添加节点至分组中
* @param id
*/
DynamicGroupNodeModel.prototype.addChild = function (id) {
this.children.add(id);
var groupData = this.getData();
this.graphModel.eventCenter.emit('group:add-node', {
data: groupData,
childId: id,
});
};
/**
* 从分组中移除某节点
* @param id
*/
DynamicGroupNodeModel.prototype.removeChild = function (id) {
this.children.delete(id);
var groupData = this.getData();
this.graphModel.eventCenter.emit('group:remove-node', { data: groupData });
};
/**
* 当 groupA 被添加到 groupB 中时,将 groupB 及 groupB 所属的 group zIndex 减 1
*/
DynamicGroupNodeModel.prototype.toBack = function () {
this.zIndex--;
};
/**
* 重写 Group 节点的 Resize Outline
*/
DynamicGroupNodeModel.prototype.getResizeOutlineStyle = function () {
var style = _super.prototype.getResizeOutlineStyle.call(this);
style.stroke = 'none';
return style;
};
// TODO: 是否是设置 group 节点没有锚点,而不是设置成透明???
DynamicGroupNodeModel.prototype.getAnchorStyle = function () {
var style = _super.prototype.getAnchorStyle.call(this);
style.stroke = 'transparent';
style.fill = 'transparent';
if (style.hover) {
style.hover.fill = 'transparent';
style.hover.stroke = 'transparent';
}
return style;
};
/**
* 设置分组节点 drop 区域的样式
*/
DynamicGroupNodeModel.prototype.getAddableOutlineStyle = function () {
return {
stroke: '#feb663',
strokeWidth: 2,
strokeDasharray: '4 4',
fill: 'transparent',
};
};
__decorate([
core_1.observable
], DynamicGroupNodeModel.prototype, "isCollapsed", void 0);
__decorate([
core_1.observable
], DynamicGroupNodeModel.prototype, "groupAddable", void 0);
__decorate([
core_1.observable
], DynamicGroupNodeModel.prototype, "transformWithContainer", void 0);
return DynamicGroupNodeModel;
}(core_1.RectNodeModel));
exports.DynamicGroupNodeModel = DynamicGroupNodeModel;
exports.default = DynamicGroupNodeModel;