@logicflow/extension
Version:
LogicFlow Extensions
359 lines (358 loc) • 19.4 kB
JavaScript
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Label = void 0;
var core_1 = require("@logicflow/core");
var lodash_es_1 = require("lodash-es");
var LabelOverlay_1 = __importDefault(require("./LabelOverlay"));
var utils_1 = require("./utils");
var Label = /** @class */ (function () {
function Label(_a) {
var lf = _a.lf, options = _a.options;
var _b, _c, _d;
this.labelInitPositionMap = new Map();
this.lf = lf;
// DONE: 根据 options 初始化一些插件配置,比如是否支持多个 label 等,生效在所有 label 中
this.options = options !== null && options !== void 0 ? options : {};
this.textOverflowMode = (_b = options.textOverflowMode) !== null && _b !== void 0 ? _b : 'default';
this.isMultiple = (_c = options.isMultiple) !== null && _c !== void 0 ? _c : true;
this.labelWidth = options.labelWidth;
this.maxCount = (_d = options.maxCount) !== null && _d !== void 0 ? _d : Infinity;
// DONE: 1. 启用插件时,将当前画布的 textMode 更新为 TextMode.LABEL。
// 如果将其又重新设置为 TextModel.TEXT,则需要 disable 掉 Label 工具,enable TextEditTool
lf.graphModel.editConfigModel.updateTextMode(core_1.TextMode.LABEL);
// TODO: 2. 做一些插件需要的事件监听
this.addEventListeners();
// TODO: 3. 自定义快捷键,比如 delete,选中 label 时,移除 label
this.rewriteShortcut();
// 插件中注册 LabelOverlay 工具,用于 label 的编辑
lf.tool.registerTool(LabelOverlay_1.default.toolName, LabelOverlay_1.default);
// LabelOverlay 和 TextEditTool 互斥,所以将它 disable 掉
lf.tool.disableTool('text-edit-tool');
}
/**
* 格式化元素的 Label 配置,后续初始化 Label 用统一的数据格式
* 主要是将 _label 类型 string | LabelConfig | LabelConfig[] 统一转换为 LabelConfig[]
* @param graphModel 当前图的 model
* @param element 当前元素 model
* @return LabelConfig[]
*/
Label.prototype.formatConfig = function (graphModel, element) {
var _a = graphModel.editConfigModel, nodeTextEdit = _a.nodeTextEdit, edgeTextEdit = _a.edgeTextEdit, nodeTextDraggable = _a.nodeTextDraggable, edgeTextDraggable = _a.edgeTextDraggable;
var _b = this, textOverflowMode = _b.textOverflowMode, isMultiple = _b.isMultiple, maxCount = _b.maxCount, labelWidth = _b.labelWidth;
var text = element.text, zIndex = element.zIndex, _c = element.properties, _label = _c._label, _d = _c._labelOption, _labelOption = _d === void 0 ? {} : _d;
// 当前元素的 Label 相关配置
var curLabelConfig = _label;
var _e = _labelOption, curIsMultiple = _e.isMultiple, curMaxCount = _e.maxCount;
// REMIND: 对 3 种可能得数据类型进行处理
var formatConfig = []; // 保存格式化后的 LabelConfig
if ((0, lodash_es_1.isArray)(curLabelConfig)) {
// 1. 数组的话就是 LabelConfig[] 类型
// 判断是否开启 isMultiple, 如果开启了,判断是否超过最大数量。超出就截取
var size = curMaxCount !== null && curMaxCount !== void 0 ? curMaxCount : maxCount; // 优先级,当设置 multiple 时,元素的 maxCount 优先级高于插件的 maxCount
if (isMultiple && curIsMultiple) {
if (curLabelConfig.length > size) {
formatConfig = curLabelConfig.slice(0, size);
}
else {
formatConfig = curLabelConfig;
}
}
else {
formatConfig = [curLabelConfig[0]];
}
}
else if ((0, lodash_es_1.isObject)(curLabelConfig)) {
// 2. 对象的话就是 LabelConfig 类型
formatConfig = [curLabelConfig];
}
else if (typeof curLabelConfig === 'string' || !curLabelConfig) {
// 3. 字符串或者为空的话就是 string 类型,基于 text 的数据合成 LabelConfig 信息(主要复用 text 的 x,y 信息)
var config = __assign(__assign({}, text), { content: curLabelConfig || (text === null || text === void 0 ? void 0 : text.value), draggable: element.BaseType === 'edge' ? edgeTextDraggable : nodeTextDraggable });
formatConfig = config.value ? [config] : [];
}
// TODO: 针对 Edge,在 edge 更新时 重新计算 Label 的位置
if (element.BaseType === 'edge') {
// 判断当前 label,是否在 edge 的路径上,如果不在,就重新计算位置
formatConfig = (0, lodash_es_1.map)(formatConfig, function (config) {
return config;
});
}
// DONE: 再根据一些全局配置,比如是否支持垂直显示等,对 LabelConfig 进行二次处理
// 优先级:全局配置 > 元素配置。比如全局设置 isMultiple 为 true 时,才可以使用 局部的 isMultiple 设置才生效
// 当全局 isMultiple 为 false 时,局部的 isMultiple 不生效
return (0, lodash_es_1.map)(formatConfig, function (config) {
if (!config.id) {
config.id = (0, core_1.createUuid)();
}
var value = config.value, content = config.content, vertical = config.vertical, editable = config.editable, draggable = config.draggable, labelTextOverflowMode = config.textOverflowMode;
var textEdit = element.BaseType === 'node' ? nodeTextEdit : edgeTextEdit;
var textDraggable = element.BaseType === 'node' ? nodeTextDraggable : edgeTextDraggable;
return __assign(__assign({}, config), { zIndex: zIndex, labelWidth: labelWidth, content: content !== null && content !== void 0 ? content : value, vertical: vertical !== null && vertical !== void 0 ? vertical : false, editable: Boolean(textEdit) && editable, draggable: Boolean(textDraggable) && draggable, textOverflowMode: labelTextOverflowMode !== null && labelTextOverflowMode !== void 0 ? labelTextOverflowMode : textOverflowMode });
});
};
/**
* 根据初始化的数据,格式化 Label 的数据格式后,统一更新到元素的 properties._label 中,保证后续的渲染以这个数据格式进行
* @param graphModel
*/
Label.prototype.setupLabels = function (graphModel) {
var _this = this;
// const elements = [...graphModel.nodes, ...graphModel.edges]
var elements = graphModel.sortElements;
// TODO: 1. 筛选出当前画布上,textMode 为 TextMode.LABEL 的元素(在支持元素级别的 textMode 时,需要做这个筛选)
// REMIND: 本期先只支持全局配置,所以判断全局的 textMode 即可
(0, lodash_es_1.forEach)(elements, function (element) {
// DONE: 2. 在此处做数据的转换
// 输入:NodeConfig.properties._label: string | LabelConfig | LabelConfig[]
// 输出:NodeData.properties._label: LabelData | LabelData[]
// 是否需要根据 isMultiple 控制是否返回数组或对象 or 直接全部返回数组 ❓❓❓ -> 目前直接全部返回数组
_this.rewriteInnerMethods(element);
var formatLabelConfig = _this.formatConfig(graphModel, element);
// FIX: BUG Here: 格式化后的 labelConfig 没有同步到 element 上,导致每次重新渲染时,都会重新格式化,且重新生成 id
// 但如果在此处通过 setProperty 更新元素的 _label 时,又会导致死循环
element.setProperty('_label', formatLabelConfig);
});
};
/**
* 给元素添加一个 label
* @param element
* @param position
*/
Label.prototype.addLabel = function (element, position) {
var _a, _b, _c;
var _d = this, isMultiple = _d.isMultiple, maxCount = _d.maxCount;
var _e = element.properties, _label = _e._label, _labelOption = _e._labelOption;
var curLabelConfig = (_a = _label) !== null && _a !== void 0 ? _a : [];
var curLabelOption = (_b = _labelOption) !== null && _b !== void 0 ? _b : {};
var len = curLabelConfig.length;
var newLabel = {
id: (0, core_1.createUuid)(),
x: position.x,
y: position.y,
content: "Label".concat(len + 1),
value: "Label".concat(len + 1),
style: {},
draggable: true,
editable: true,
vertical: false,
};
// 全局的isMultiple为false,或全局isMultiple为true但局部isMultiple指明是false,或当前label长度已经达到上线时,不允许添加多个 label
if (!isMultiple ||
(isMultiple && curLabelOption.isMultiple === false) ||
len >= ((_c = curLabelOption === null || curLabelOption === void 0 ? void 0 : curLabelOption.maxCount) !== null && _c !== void 0 ? _c : maxCount)) {
return;
}
curLabelConfig.push(newLabel);
element.setProperty('_label', curLabelConfig);
};
/**
* 移除元素的某个 label
* @private
*/
// private removeLabel() {}
Label.prototype.addEventListeners = function () {
var _this = this;
var graphModel = this.lf.graphModel;
var eventCenter = graphModel.eventCenter, editConfigModel = graphModel.editConfigModel;
eventCenter.on('graph:rendered', function (_a) {
var graphModel = _a.graphModel;
_this.setupLabels(graphModel);
});
// 监听元素双击事件,给元素增加 Label
eventCenter.on('node:dbclick,edge:dbclick', function (_a) {
var e = _a.e, data = _a.data;
// DONE: 增加 label 的数据信息到 element model
var target = graphModel.getElement(data.id);
// DONE: 将 clientX 和 clientY 转换为画布坐标
var _b = graphModel.getPointByClient({
x: e.clientX,
y: e.clientY,
}).canvasOverlayPosition, x1 = _b.x, y1 = _b.y;
var point = {
x: x1,
y: y1,
};
if (target && editConfigModel.textMode === core_1.TextMode.LABEL) {
_this.addLabel(target, point);
}
});
// 监听 node:resize 事件,在 resize 时,重新计算 label 的位置信息
eventCenter.on('node:resize', function (_a) {
var _b, _c;
var preData = _a.preData, data = _a.data, model = _a.model;
var _d = (_b = preData.properties) !== null && _b !== void 0 ? _b : {}, preWidth = _d.width, preHeight = _d.height, _e = _d._label, _label = _e === void 0 ? [] : _e;
var _f = (_c = data.properties) !== null && _c !== void 0 ? _c : {}, curWidth = _f.width, curHeight = _f.height;
if (preWidth && preHeight && curWidth && curHeight) {
var origin_1 = {
x: preData.x,
y: preData.y,
width: preWidth,
height: preHeight,
};
var scaled_1 = {
x: data.x,
y: data.y,
width: curWidth,
height: curHeight,
};
var newLabelConfig = (0, lodash_es_1.map)(_label, function (label) {
var x = label.x, y = label.y;
var newPoint = (0, utils_1.calcPointAfterResize)(origin_1, scaled_1, { x: x, y: y });
return __assign(__assign({}, label), newPoint);
});
model.setProperty('_label', newLabelConfig);
}
});
// 监听 node:rotate 事件,在 rotate 时,重新计算 Label 的位置信息
eventCenter.on('node:rotate', function (_a) {
var model = _a.model;
var x = model.x, y = model.y, rotate = model.rotate, _b = model.properties._label, _label = _b === void 0 ? [] : _b;
var center = { x: x, y: y };
var newLabelConfig = (0, lodash_es_1.map)(_label, function (label) {
if (!label.id)
return label;
var point = { x: label.x, y: label.y };
if (_this.labelInitPositionMap.has(label.id)) {
point = _this.labelInitPositionMap.get(label.id);
}
else {
_this.labelInitPositionMap.set(label.id, point);
}
// 弧度转角度
var theta = rotate * (180 / Math.PI);
if (theta < 0)
theta += 360;
var radian = theta * (Math.PI / 180);
var newPoint = (0, utils_1.rotatePointAroundCenter)(point, center, radian);
return __assign(__assign(__assign({}, label), newPoint), { rotate: theta });
});
model.setProperty('_label', newLabelConfig);
});
// 监听元素新增事件,元素label格式化
eventCenter.on('node:dnd-add,node:add,edge:add', function (_a) {
var data = _a.data;
var element = graphModel.getElement(data.id);
if (element) {
_this.rewriteInnerMethods(element);
// 检查_label是否已经是格式化后的数组格式,如果是则跳过格式化
// 这样可以避免在复制粘贴时重复处理已经格式化好的label数据
var currentLabel = element.properties._label;
if (!(0, lodash_es_1.isArray)(currentLabel) ||
currentLabel.length === 0 ||
!currentLabel[0].id) {
var formatedLabel = _this.formatConfig(graphModel, data);
element.setProperty('_label', formatedLabel);
}
}
});
};
/**
* 重写元素的一些方法,以支持 Label 的拖拽、编辑等
* @param element
*/
Label.prototype.rewriteInnerMethods = function (element) {
// 重写 edgeModel/nodeModel moveText 方法,在 move text 时,以相同的逻辑移动 label
element.moveText = function (deltaX, deltaY) {
if (!element.text)
return;
var _a = element.text, x = _a.x, y = _a.y, value = _a.value, draggable = _a.draggable, editable = _a.editable;
element.text = {
value: value,
editable: editable,
draggable: draggable,
x: x + deltaX,
y: y + deltaY,
};
var properties = (0, lodash_es_1.cloneDeep)(element.getProperties());
// 重新计算新的 label 位置信息
if ((0, lodash_es_1.isArray)(properties._label)) {
var nextLabel = (0, lodash_es_1.map)(properties._label, function (label) {
return __assign(__assign({}, label), { x: label.x + deltaX, y: label.y + deltaY });
});
element === null || element === void 0 ? void 0 : element.setProperty('_label', nextLabel);
}
};
// TODO: others methods ???
};
Label.prototype.rewriteShortcut = function () {
var _this = this;
var _a = this.lf, keyboard = _a.keyboard, graphModel = _a.graphModel;
var keyboardOptions = keyboard.options.keyboard;
keyboard.off(['backspace']);
keyboard.on(['backspace'], function () {
if (!(keyboardOptions === null || keyboardOptions === void 0 ? void 0 : keyboardOptions.enabled))
return true;
if (graphModel.textEditElement)
return true;
var elements = graphModel.getSelectElements(true);
_this.lf.clearSelectElements();
var editConfigModel = _this.lf.graphModel.editConfigModel;
elements.edges.forEach(function (edge) {
var properties = edge.properties;
if (properties &&
!(0, lodash_es_1.isEmpty)(properties._label) &&
editConfigModel.textMode === core_1.TextMode.LABEL) {
var newLabelList = properties._label.filter(function (label) { return !label.isSelected; });
// 如果两个labelList长度不一致,说明有选中的元素,此时backspace做的动作是删除label
if (!(0, lodash_es_1.isEqual)(newLabelList.length, properties._label.length)) {
var edgeModel = graphModel.getEdgeModelById(edge.id);
edgeModel === null || edgeModel === void 0 ? void 0 : edgeModel.setProperty('_label', newLabelList);
return;
}
}
edge.id && _this.lf.deleteEdge(edge.id);
});
elements.nodes.forEach(function (node) {
var properties = node.properties;
if (properties &&
!(0, lodash_es_1.isEmpty)(properties._label) &&
editConfigModel.textMode === core_1.TextMode.LABEL) {
var newLabelList = properties._label.filter(function (label) { return !label.isSelected; });
if (!(0, lodash_es_1.isEqual)(newLabelList.length, properties._label.length)) {
var nodeModel = graphModel.getNodeModelById(node.id);
nodeModel === null || nodeModel === void 0 ? void 0 : nodeModel.setProperty('_label', newLabelList);
return;
}
}
node.id && _this.lf.deleteNode(node.id);
});
return false;
});
};
/**
* 更新当前渲染使用的 Text or Label 模式
*/
Label.prototype.updateTextMode = function (textMode) {
var editConfigModel = this.lf.graphModel.editConfigModel;
if (textMode === editConfigModel.textMode)
return;
editConfigModel.updateTextMode(textMode);
if (textMode === core_1.TextMode.LABEL) {
this.lf.tool.enableTool(LabelOverlay_1.default.toolName);
this.lf.tool.disableTool('text-edit-tool');
}
else if (textMode === core_1.TextMode.TEXT) {
this.lf.tool.enableTool('text-edit-tool');
this.lf.tool.disableTool(LabelOverlay_1.default.toolName);
}
};
Label.prototype.render = function () { };
Label.prototype.destroy = function () { };
Label.pluginName = 'label';
return Label;
}());
exports.Label = Label;
exports.default = Label;
;