@logicflow/extension
Version:
LogicFlow Extensions
429 lines (428 loc) • 18.3 kB
JavaScript
"use strict";
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.Menu = void 0;
var DefaultNodeMenuKey = 'lf:defaultNodeMenu';
var DefaultEdgeMenuKey = 'lf:defaultEdgeMenu';
var DefaultGraphMenuKey = 'lf:defaultGraphMenu';
var DefaultSelectionMenuKey = 'lf:defaultSelectionMenu';
var Menu = /** @class */ (function () {
function Menu(_a) {
var lf = _a.lf;
var _this = this;
this.__currentData = null;
this.lf = lf;
var isSilentMode = lf.options.isSilentMode;
if (!isSilentMode) {
this.__menuDOM = document.createElement('ul');
this.menuTypeMap = new Map();
this.init();
this.lf.setMenuConfig = function (config) {
_this.setMenuConfig(config);
};
this.lf.addMenuConfig = function (config) {
_this.addMenuConfig(config);
};
this.lf.setMenuByType = function (config) {
_this.setMenuByType(config);
};
}
}
/**
* 初始化设置默认内置菜单栏
*/
Menu.prototype.init = function () {
var _this = this;
var _a, _b, _c, _d;
var defaultNodeMenu = [
{
text: '删除',
callback: function (node) {
_this.lf.deleteNode(node.id);
},
},
{
text: '编辑文本',
callback: function (node) {
_this.lf.graphModel.editText(node.id);
},
},
{
text: '复制',
callback: function (node) {
_this.lf.cloneNode(node.id);
},
},
];
(_a = this.menuTypeMap) === null || _a === void 0 ? void 0 : _a.set(DefaultNodeMenuKey, defaultNodeMenu);
var defaultEdgeMenu = [
{
text: '删除',
callback: function (edge) {
_this.lf.deleteEdge(edge.id);
},
},
{
text: '编辑文本',
callback: function (edge) {
_this.lf.graphModel.editText(edge.id);
},
},
];
(_b = this.menuTypeMap) === null || _b === void 0 ? void 0 : _b.set(DefaultEdgeMenuKey, defaultEdgeMenu);
(_c = this.menuTypeMap) === null || _c === void 0 ? void 0 : _c.set(DefaultGraphMenuKey, []);
var DefaultSelectionMenu = [
{
text: '删除',
callback: function (elements) {
_this.lf.clearSelectElements();
elements.edges.forEach(function (edge) { return _this.lf.deleteEdge(edge.id); });
elements.nodes.forEach(function (node) { return _this.lf.deleteNode(node.id); });
},
},
];
(_d = this.menuTypeMap) === null || _d === void 0 ? void 0 : _d.set(DefaultSelectionMenuKey, DefaultSelectionMenu);
};
Menu.prototype.render = function (lf, container) {
var _this = this;
if (lf.options.isSilentMode)
return;
this.__container = container;
this.__currentData = null; // 当前展示的菜单所属元素的model数据
if (this.__menuDOM) {
this.__menuDOM.className = 'lf-menu';
container.appendChild(this.__menuDOM);
// 将选项的click事件委托至menu容器
// 在捕获阶段拦截并执行
this.__menuDOM.addEventListener('click', function (event) {
event.stopPropagation();
var target = event.target;
// 菜单有多层dom,需要精确获取菜单项所对应的dom
// 除菜单项dom外,应考虑两种情况
// 1. 菜单项的子元素 2. 菜单外层容器
while (Array.from(target.classList).indexOf('lf-menu-item') === -1 &&
Array.from(target.classList).indexOf('lf-menu') === -1) {
target = target === null || target === void 0 ? void 0 : target.parentElement;
}
if (Array.from(target.classList).indexOf('lf-menu-item') > -1) {
// 如果点击区域在菜单项内
;
target.onclickCallback(_this.__currentData);
// 点击后隐藏menu
if (_this.__menuDOM) {
_this.__menuDOM.style.display = 'none';
}
_this.__currentData = null;
}
else {
// 如果点击区域不在菜单项内
console.warn('点击区域不在菜单项内,请检查代码!');
}
}, true);
}
// 通过事件控制菜单的显示和隐藏
this.lf.on('node:contextmenu', function (_a) {
var _b, _c;
var data = _a.data, position = _a.position, e = _a.e;
var _d = position.domOverlayPosition, x = _d.x, y = _d.y;
var id = data.id;
var model = _this.lf.graphModel.getNodeModelById(id);
if (!model)
return;
var menuList = [];
var typeMenus = (_b = _this.menuTypeMap) === null || _b === void 0 ? void 0 : _b.get(model.type);
// 1.如果单个节点自定义了菜单,以单个节点自定义为准
if (model && model.menu && Array.isArray(model.menu)) {
menuList = model.menu;
}
else if (typeMenus) {
// 2.如果当前节点类型定义了菜单,再取该配置
menuList = typeMenus;
}
else {
// 3.最后取全局默认
menuList = (_c = _this.menuTypeMap) === null || _c === void 0 ? void 0 : _c.get(DefaultNodeMenuKey);
}
_this.__currentData = data;
_this.showMenu(x, y, menuList, {
width: model.width,
height: model.height,
clientX: e.clientX,
clientY: e.clientY,
});
});
this.lf.on('edge:contextmenu', function (_a) {
var _b, _c, _d;
var data = _a.data, position = _a.position, e = _a.e;
var _e = position.domOverlayPosition, x = _e.x, y = _e.y;
var id = data.id;
var model = _this.lf.graphModel.getEdgeModelById(id);
if (!model)
return;
var menuList = [];
var typeMenus = (_b = _this.menuTypeMap) === null || _b === void 0 ? void 0 : _b.get(model.type);
// 菜单优先级: model.menu > typeMenus > defaultEdgeMenu,注释同上节点
if (model && model.menu && Array.isArray(model.menu)) {
menuList = model.menu;
}
else if (typeMenus) {
menuList = typeMenus;
}
else {
menuList = (_d = (_c = _this.menuTypeMap) === null || _c === void 0 ? void 0 : _c.get(DefaultEdgeMenuKey)) !== null && _d !== void 0 ? _d : [];
}
_this.__currentData = data;
_this.showMenu(x, y, menuList, {
width: model.width,
height: model.height,
clientX: e.clientX,
clientY: e.clientY,
});
});
this.lf.on('blank:contextmenu', function (_a) {
var _b, _c;
var position = _a.position;
var menuList = (_c = (_b = _this.menuTypeMap) === null || _b === void 0 ? void 0 : _b.get(DefaultGraphMenuKey)) !== null && _c !== void 0 ? _c : [];
var _d = position.domOverlayPosition, x = _d.x, y = _d.y;
_this.__currentData = __assign({}, position.canvasOverlayPosition);
_this.showMenu(x, y, menuList);
});
this.lf.on('selection:contextmenu', function (_a) {
var _b;
var data = _a.data, position = _a.position;
var menuList = (_b = _this.menuTypeMap) === null || _b === void 0 ? void 0 : _b.get(DefaultSelectionMenuKey);
var _c = position.domOverlayPosition, x = _c.x, y = _c.y;
_this.__currentData = data;
_this.showMenu(x, y, menuList);
});
this.lf.on('node:mousedown', function () {
_this.__menuDOM.style.display = 'none';
});
this.lf.on('edge:click', function () {
_this.__menuDOM.style.display = 'none';
});
this.lf.on('blank:click', function () {
_this.__menuDOM.style.display = 'none';
});
};
Menu.prototype.destroy = function () {
var _a;
if (this.__menuDOM) {
(_a = this === null || this === void 0 ? void 0 : this.__container) === null || _a === void 0 ? void 0 : _a.removeChild(this.__menuDOM);
this.__menuDOM = undefined;
}
};
Menu.prototype.showMenu = function (x, y, menuList, options) {
if (!menuList || !menuList.length)
return;
var menu = this.__menuDOM;
if (menu) {
// 菜单容器不变,需要先清空内部的菜单项
menu.innerHTML = '';
menu.append.apply(menu, __spreadArray([], __read(this.__getMenuDom(menuList)), false));
// 菜单中没有项,不显示
if (!menu.children.length)
return;
menu.style.display = 'block';
if (!options) {
menu.style.top = "".concat(y, "px");
menu.style.left = "".concat(x, "px");
return;
}
// https://github.com/didi/LogicFlow/issues/1019
// 根据边界判断菜单的left 和 top
var width = options.width, height = options.height, clientX = options.clientX, clientY = options.clientY;
var graphModel = this.lf.graphModel;
var menuWidth = menu.offsetWidth;
var menuIsRightShow = true;
// ======先进行可视屏幕范围的判断=======
// 浏览器窗口可视区域兼容性写法
// eslint-disable-next-line max-len
var windowMaxX = window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth;
var rightDistance = windowMaxX - clientX;
// ======先进行可视屏幕范围的判断=======
// ========再进行画布范围的判断========
var graphRect = graphModel.rootEl.getBoundingClientRect();
var graphMaxX = graphRect.left + graphRect.width;
if (graphMaxX < windowMaxX) {
// 画布右边小于可视屏幕范围的最右边,取画布右边作为极限值,计算出当前触摸点距离右边极限值的距离
rightDistance = graphMaxX - clientX;
}
// ========再进行画布范围的判断========
// 根据当前触摸点距离右边的距离 跟 menuWidth进行比较
if (rightDistance < menuWidth) {
// 空间不足够,显示在左边
menuIsRightShow = false;
}
if (menuIsRightShow) {
menu.style.left = "".concat(x, "px");
}
else {
menu.style.left = "".concat(x - width, "px");
}
var menuHeight = menu.offsetHeight;
var menuIsBottomShow = true;
// ======先进行可视屏幕范围的判断=======
// 浏览器窗口可视区域兼容性写法
// eslint-disable-next-line max-len
var windowMaxY = window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight;
var bottomDistance = windowMaxY - clientY;
// ======先进行可视屏幕范围的判断=======
// ========再进行画布范围的判断========
var graphMaxY = graphRect.top + graphRect.height;
if (graphMaxY < windowMaxY) {
// 画布底部小于可视屏幕范围的最底边,取画布底部作为极限值,计算出当前触摸点距离底部极限值的距离
bottomDistance = graphMaxY - clientY;
}
// ========再进行画布范围的判断========
if (bottomDistance < menuHeight) {
// 如果下边距离太小,无法显示menu,则向上显示
menuIsBottomShow = false;
}
if (menuIsBottomShow) {
menu.style.top = "".concat(y, "px");
}
else {
menu.style.top = "".concat(y - height, "px");
}
}
};
/**
* 设置指定类型元素的菜单
*/
Menu.prototype.setMenuByType = function (_a) {
var _b;
var type = _a.type, menu = _a.menu;
if (!type || !menu) {
return;
}
(_b = this.menuTypeMap) === null || _b === void 0 ? void 0 : _b.set(type, menu);
};
/**
* 获取 Menu DOM
* @param list 菜单项
* @return 菜单项 DOM
*/
Menu.prototype.__getMenuDom = function (list) {
var menuList = [];
list &&
list.length > 0 &&
list.forEach(function (item) {
var element = document.createElement('li');
if (item.className) {
element.className = "lf-menu-item ".concat(item.className);
}
else {
element.className = 'lf-menu-item';
}
if (item.icon === true) {
var icon = document.createElement('span');
icon.className = 'lf-menu-item-icon';
element.appendChild(icon);
}
var text = document.createElement('span');
text.className = 'lf-menu-item-text';
if (item.text) {
text.innerText = item.text;
}
element.appendChild(text);
element.onclickCallback = item.callback;
menuList.push(element);
});
return menuList;
};
// 复写菜单
Menu.prototype.setMenuConfig = function (config) {
var _a, _b, _c;
if (!config) {
return;
}
// node
config.nodeMenu !== undefined &&
((_a = this.menuTypeMap) === null || _a === void 0 ? void 0 : _a.set(DefaultNodeMenuKey, config.nodeMenu ? config.nodeMenu : []));
// edge
config.edgeMenu !== undefined &&
((_b = this.menuTypeMap) === null || _b === void 0 ? void 0 : _b.set(DefaultEdgeMenuKey, config.edgeMenu ? config.edgeMenu : []));
// graph
config.graphMenu !== undefined &&
((_c = this.menuTypeMap) === null || _c === void 0 ? void 0 : _c.set(DefaultGraphMenuKey, config.graphMenu ? config.graphMenu : []));
};
// 在默认菜单后面追加菜单项
Menu.prototype.addMenuConfig = function (config) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
if (!config) {
return;
}
// 追加项时,只支持数组类型,对false不做操作
if (Array.isArray(config.nodeMenu)) {
var menuList = (_b = (_a = this.menuTypeMap) === null || _a === void 0 ? void 0 : _a.get(DefaultNodeMenuKey)) !== null && _b !== void 0 ? _b : [];
(_c = this.menuTypeMap) === null || _c === void 0 ? void 0 : _c.set(DefaultNodeMenuKey, menuList.concat(config.nodeMenu));
}
if (Array.isArray(config.edgeMenu)) {
var menuList = (_e = (_d = this.menuTypeMap) === null || _d === void 0 ? void 0 : _d.get(DefaultEdgeMenuKey)) !== null && _e !== void 0 ? _e : [];
(_f = this.menuTypeMap) === null || _f === void 0 ? void 0 : _f.set(DefaultEdgeMenuKey, menuList.concat(config.edgeMenu));
}
if (Array.isArray(config.graphMenu)) {
var menuList = (_h = (_g = this.menuTypeMap) === null || _g === void 0 ? void 0 : _g.get(DefaultGraphMenuKey)) !== null && _h !== void 0 ? _h : [];
(_j = this.menuTypeMap) === null || _j === void 0 ? void 0 : _j.set(DefaultGraphMenuKey, menuList.concat(config.graphMenu));
}
};
/**
* @deprecated
* 复写添加
*/
Menu.prototype.changeMenuItem = function (type, config) {
if (type === 'add') {
this.addMenuConfig(config);
}
else if (type === 'reset') {
this.setMenuConfig(config);
}
else {
throw new Error("The first parameter of changeMenuConfig should be 'add' or 'reset'");
}
};
Menu.pluginName = 'menu';
return Menu;
}());
exports.Menu = Menu;
exports.default = Menu;