@logicflow/extension
Version:
LogicFlow Extensions
442 lines (441 loc) • 19.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 __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.GroupNode = exports.GroupNodeModel = void 0;
var core_1 = require("@logicflow/core");
var lodash_es_1 = require("lodash-es");
var NodeResize_1 = require("../../NodeResize");
var defaultWidth = 500;
var defaultHeight = 300;
var DEFAULT_BOTTOM_Z_INDEX = -10000;
var GroupNodeModel = /** @class */ (function (_super) {
__extends(GroupNodeModel, _super);
function GroupNodeModel() {
var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this;
_this.isGroup = true;
/**
* 分组折叠状态
*/
_this.isFolded = false;
_this.unfoldedWidth = defaultWidth;
_this.unfoldedHeight = defaultHeight;
/**
* children元素上一次折叠的状态缓存
*/
_this.childrenLastFoldStatus = {};
return _this;
}
GroupNodeModel.prototype.initNodeData = function (data) {
var _this = this;
_super.prototype.initNodeData.call(this, data);
var children = [];
if ((0, lodash_es_1.isArray)(data.children)) {
children = data.children;
}
// 初始化组的子节点
this.children = new Set(children);
this.width = defaultWidth;
this.height = defaultHeight;
this.foldedWidth = 80;
this.foldedHeight = 60;
this.zIndex = DEFAULT_BOTTOM_Z_INDEX;
this.radius = 0;
this.text.editable = false;
this.text.draggable = false;
this.isRestrict = false;
this.resizable = false;
this.autoToFront = false;
this.foldable = false;
if (this.properties.isFolded === undefined) {
this.properties.isFolded = false;
}
this.isFolded = !!this.properties.isFolded;
// fixme: 虽然默认保存的分组不会收起,但是如果重写保存数据分组了,
// 此处代码会导致多一个history记录
setTimeout(function () {
_this.isFolded && _this.foldGroup(_this.isFolded);
});
// this.foldGroup(this.isFolded);
};
GroupNodeModel.prototype.getResizeOutlineStyle = function () {
var style = _super.prototype.getResizeOutlineStyle.call(this);
style.stroke = 'none';
return style;
};
/**
* 折叠分组
* 1. 折叠分组的宽高
* 2. 处理分组子节点
* 3. 处理连线
*/
GroupNodeModel.prototype.foldGroup = function (isFolded) {
var _this = this;
if (isFolded === this.isFolded) {
// 防止多次调用同样的状态设置
// 如果this.isFolded=false,同时触发foldGroup(false),会导致下面的childrenLastFoldStatus状态错乱
return;
}
this.setProperty('isFolded', isFolded);
this.isFolded = isFolded;
// step 1
if (isFolded) {
this.x = this.x - this.width / 2 + this.foldedWidth / 2;
this.y = this.y - this.height / 2 + this.foldedHeight / 2;
this.unfoldedWidth = this.width;
this.unfoldedHeight = this.height;
this.width = this.foldedWidth;
this.height = this.foldedHeight;
}
else {
this.width = this.unfoldedWidth;
this.height = this.unfoldedHeight;
this.x = this.x + this.width / 2 - this.foldedWidth / 2;
this.y = this.y + this.height / 2 - this.foldedHeight / 2;
}
// step 2
var allEdges = __spreadArray(__spreadArray([], __read(this.incoming.edges), false), __read(this.outgoing.edges), false);
this.children.forEach(function (elementId) {
var nodeModel = _this.graphModel.getElement(elementId);
if (nodeModel) {
var foldStatus = nodeModel.isFolded;
// FIX: https://github.com/didi/LogicFlow/issues/1007
if (nodeModel.isGroup && !nodeModel.isFolded) {
// 正常情况下,parent折叠后,children应该折叠
// 因此当parent准备展开时,children的值目前肯定是折叠状态,也就是nodeModel.isFolded=true,这个代码块不会触发
// 只有当parent准备折叠时,children目前状态才有可能是展开,
// 即nodeModel.isFolded=false,这个代码块触发,此时isFolded=true,触发children也进行折叠
;
nodeModel.foldGroup(isFolded);
}
if (nodeModel.isGroup && !isFolded) {
// 当parent准备展开时,children的值应该恢复到折叠前的状态
var lastFoldStatus = _this.childrenLastFoldStatus[elementId];
if (lastFoldStatus !== undefined &&
lastFoldStatus !== nodeModel.isFolded) {
// https://github.com/didi/LogicFlow/issues/1145
// 当parent准备展开时,children的值肯定是折叠,也就是nodeModel.isFolded=true
// 当parent准备展开时,如果children之前的状态是展开,则恢复展开状态
;
nodeModel.foldGroup(lastFoldStatus);
}
}
// 存储parent触发children改变折叠状态前的状态
_this.childrenLastFoldStatus[elementId] = !!foldStatus;
nodeModel.visible = !isFolded;
var incomingEdges = nodeModel.incoming.edges;
var outgoingEdges = nodeModel.outgoing.edges;
allEdges = __spreadArray(__spreadArray(__spreadArray([], __read(allEdges), false), __read(incomingEdges), false), __read(outgoingEdges), false);
}
});
// step 3
this.foldEdge(isFolded, allEdges);
};
GroupNodeModel.prototype.getAnchorStyle = function (anchorInfo) {
var style = _super.prototype.getAnchorStyle.call(this, anchorInfo);
style.stroke = 'transparent';
style.fill = 'transparent';
style.hover.fill = 'transparent'; // TODO: 确认这种情况如何解决,style.hover 为 undefined 时该如何处理
style.hover.stroke = 'transparent';
return style;
};
/**
* 折叠分组的时候,处理分组自身的连线和分组内部子节点上的连线
* 边的分类:
* - 虚拟边:分组被收起时,表示分组本身与外部节点关系的边。
* - 真实边:分组本身或者分组内部节点与外部节点节点(非收起分组)关系的边。
* 如果一个分组,本身与外部节点有M条连线,且内部N个子节点与外部节点有连线,那么这个分组收起时会生成M+N条连线。
* 折叠分组时:
* - 原有的虚拟边删除;
* - 创建一个虚拟边;
* - 真实边则隐藏;
* 展开分组是:
* - 原有的虚拟边删除;
* - 如果目外部点是收起的分组,则创建虚拟边;
* - 如果外部节点是普通节点,则显示真实边;
*/
GroupNodeModel.prototype.foldEdge = function (isFolded, allEdges) {
var _this = this;
allEdges.forEach(function (edgeModel, index) {
var id = edgeModel.id, sourceNodeId = edgeModel.sourceNodeId, targetNodeId = edgeModel.targetNodeId, startPoint = edgeModel.startPoint, endPoint = edgeModel.endPoint, type = edgeModel.type, text = edgeModel.text;
var properties = edgeModel.getProperties();
var data = {
id: "".concat(id, "__").concat(index),
sourceNodeId: sourceNodeId,
targetNodeId: targetNodeId,
startPoint: startPoint,
endPoint: endPoint,
type: type,
properties: properties,
text: text === null || text === void 0 ? void 0 : text.value,
};
if (edgeModel.virtual) {
_this.graphModel.deleteEdgeById(edgeModel.id);
}
var targetNodeIdGroup = _this.graphModel.group.getNodeGroup(targetNodeId);
// 考虑目标节点本来就是分组的情况
if (!targetNodeIdGroup) {
targetNodeIdGroup = _this.graphModel.getNodeModelById(targetNodeId);
}
var sourceNodeIdGroup = _this.graphModel.group.getNodeGroup(sourceNodeId);
if (!sourceNodeIdGroup) {
sourceNodeIdGroup = _this.graphModel.getNodeModelById(sourceNodeId);
}
// 折叠时,处理未被隐藏的边的逻辑
if (isFolded && edgeModel.visible !== false) {
// 需要确认此分组节点是新连线的起点还是终点
// 创建一个虚拟边,虚拟边相对真实边,起点或者终点从一起分组内部的节点成为了分组,
// 如果需要被隐藏的边的起点在需要折叠的分组中,那么设置虚拟边的开始节点为此分组
if (_this.children.has(sourceNodeId) || _this.id === sourceNodeId) {
data.startPoint = undefined;
data.sourceNodeId = _this.id;
}
else {
data.endPoint = undefined;
data.targetNodeId = _this.id;
}
// 如果边的起点和终点都在分组内部,则不创建新的虚拟边
if (targetNodeIdGroup.id !== _this.id ||
sourceNodeIdGroup.id !== _this.id) {
_this.createVirtualEdge(data);
}
edgeModel.visible = false;
}
// 展开时,处理被隐藏的边的逻辑
if (!isFolded && edgeModel.visible === false) {
// 展开分组时:判断真实边的起点和终点是否有任一节点在已折叠分组中,如果不是,则显示真实边。如果是,这修改这个边的对应目标节点id来创建虚拟边。
if (targetNodeIdGroup &&
targetNodeIdGroup.isGroup &&
targetNodeIdGroup.isFolded) {
data.targetNodeId = targetNodeIdGroup.id;
data.endPoint = undefined;
_this.createVirtualEdge(data);
}
else if (sourceNodeIdGroup &&
sourceNodeIdGroup.isGroup &&
sourceNodeIdGroup.isFolded) {
data.sourceNodeId = sourceNodeIdGroup.id;
data.startPoint = undefined;
_this.createVirtualEdge(data);
}
else {
edgeModel.visible = true;
}
}
});
};
GroupNodeModel.prototype.createVirtualEdge = function (edgeData) {
edgeData.pointsList = undefined;
var model = this.graphModel.addEdge(edgeData);
model.virtual = true;
// 强制不保存group连线数据
// model.getData = () => null;
model.text.editable = false;
model.isFoldedEdge = true;
};
GroupNodeModel.prototype.isInRange = function (_a) {
var minX = _a.minX, minY = _a.minY, maxX = _a.maxX, maxY = _a.maxY;
return (minX >= this.x - this.width / 2 &&
maxX <= this.x + this.width / 2 &&
minY >= this.y - this.height / 2 &&
maxY <= this.y + this.height / 2);
};
GroupNodeModel.prototype.isAllowMoveTo = function (_a) {
var minX = _a.minX, minY = _a.minY, maxX = _a.maxX, maxY = _a.maxY;
return {
x: minX >= this.x - this.width / 2 && maxX <= this.x + this.width / 2,
y: minY >= this.y - this.height / 2 && maxY <= this.y + this.height / 2,
};
};
GroupNodeModel.prototype.setAllowAppendChild = function (isAllow) {
this.setProperty('groupAddable', isAllow);
};
/**
* 添加分组子节点
* @param id 节点id
*/
GroupNodeModel.prototype.addChild = function (id) {
this.children.add(id);
this.graphModel.eventCenter.emit('group:add-node', { data: this.getData() });
};
/**
* 删除分组子节点
* @param id 节点id
*/
GroupNodeModel.prototype.removeChild = function (id) {
this.children.delete(id);
this.graphModel.eventCenter.emit('group:remove-node', {
data: this.getData(),
});
};
GroupNodeModel.prototype.getAddableOutlineStyle = function () {
return {
stroke: '#FEB663',
strokeWidth: 2,
strokeDasharray: '4 4',
fill: 'transparent',
};
};
GroupNodeModel.prototype.getData = function () {
var _this = this;
var data = _super.prototype.getData.call(this);
data.children = [];
this.children.forEach(function (childId) {
var model = _this.graphModel.getNodeModelById(childId);
if (model && !model.virtual) {
;
data.children.push(childId);
}
});
var properties = data.properties;
properties === null || properties === void 0 ? true : delete properties.groupAddable;
properties === null || properties === void 0 ? true : delete properties.isFolded;
return data;
};
GroupNodeModel.prototype.getHistoryData = function () {
var data = _super.prototype.getData.call(this);
data.children = __spreadArray([], __read(this.children), false);
data.isGroup = true;
var properties = data.properties;
properties === null || properties === void 0 ? true : delete properties.groupAddable;
if (properties === null || properties === void 0 ? void 0 : properties.isFolded) {
// 如果分组被折叠
data.x = data.x + this.unfoldedWidth / 2 - this.foldedWidth / 2;
data.y = data.y + this.unfoldedHeight / 2 - this.foldedHeight / 2;
}
return data;
};
/**
* 是否允许此节点添加到此分组中
*/
GroupNodeModel.prototype.isAllowAppendIn = function (_nodeData) {
console.info('_nodeData', _nodeData);
return true;
};
/**
* 当groupA被添加到groupB中时,将groupB及groupB所属的group的zIndex减1
*/
GroupNodeModel.prototype.toBack = function () {
this.zIndex--;
};
return GroupNodeModel;
}(NodeResize_1.RectResizeModel));
exports.GroupNodeModel = GroupNodeModel;
var GroupNode = /** @class */ (function (_super) {
__extends(GroupNode, _super);
function GroupNode() {
return _super !== null && _super.apply(this, arguments) || this;
}
GroupNode.prototype.getControlGroup = function () {
var _a = this.props.model, resizable = _a.resizable, properties = _a.properties;
return resizable && !properties.isFolded ? _super.prototype.getControlGroup.call(this) : null;
};
GroupNode.prototype.getAddableShape = function () {
var _a = this.props.model, width = _a.width, height = _a.height, x = _a.x, y = _a.y, radius = _a.radius, properties = _a.properties, getAddableOutlineStyle = _a.getAddableOutlineStyle;
if (!properties.groupAddable)
return null;
var _b = this.props.model.getNodeStyle().strokeWidth, strokeWidth = _b === void 0 ? 0 : _b;
var style = getAddableOutlineStyle();
var newWidth = width + strokeWidth + 8;
var newHeight = height + strokeWidth + 8;
return (0, core_1.h)('rect', __assign(__assign({}, style), { width: newWidth, height: newHeight, x: x - newWidth / 2, y: y - newHeight / 2, rx: radius, ry: radius }));
};
GroupNode.prototype.getFoldIcon = function () {
var model = this.props.model;
var foldX = model.x - model.width / 2 + 5;
var foldY = model.y - model.height / 2 + 5;
if (!model.foldable)
return null;
var iconIcon = (0, core_1.h)('path', {
fill: 'none',
stroke: '#818281',
strokeWidth: 2,
'pointer-events': 'none',
d: model.properties.isFolded
? "M ".concat(foldX + 3, ",").concat(foldY + 6, " ").concat(foldX + 11, ",").concat(foldY + 6, " M").concat(foldX + 7, ",").concat(foldY + 2, " ").concat(foldX + 7, ",").concat(foldY + 10)
: "M ".concat(foldX + 3, ",").concat(foldY + 6, " ").concat(foldX + 11, ",").concat(foldY + 6, " "),
});
return (0, core_1.h)('g', {}, [
(0, core_1.h)('rect', {
height: 12,
width: 14,
rx: 2,
ry: 2,
strokeWidth: 1,
fill: '#F4F5F6',
stroke: '#CECECE',
cursor: 'pointer',
x: model.x - model.width / 2 + 5,
y: model.y - model.height / 2 + 5,
onClick: function () {
;
model.foldGroup(!model.properties.isFolded);
},
}),
iconIcon,
]);
};
GroupNode.prototype.getResizeShape = function () {
return (0, core_1.h)('g', {}, [
this.getAddableShape(),
_super.prototype.getResizeShape.call(this),
this.getFoldIcon(),
]);
};
return GroupNode;
}(NodeResize_1.RectResizeView));
exports.GroupNode = GroupNode;
exports.default = {
type: 'group',
view: GroupNode,
model: GroupNodeModel,
};