@logicflow/extension
Version:
LogicFlow Extensions
349 lines (348 loc) • 14.9 kB
JavaScript
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));
};
import { cloneDeep } from 'lodash-es';
var SelectionSelect = /** @class */ (function () {
function SelectionSelect(_a) {
var lf = _a.lf, options = _a.options;
var _this = this;
var _b;
this.disabled = true;
this.isWholeNode = true;
this.isWholeEdge = true;
this.exclusiveMode = false; // 框选独占模式:true 表示只能进行框选操作,false 表示可以同时进行其他画布操作
// 用于区分选区和点击事件
this.mouseDownInfo = null;
// 记录原始的 stopMoveGraph 设置
this.originalStopMoveGraph = false;
/**
* 处理画布空白处鼠标按下事件(非独占模式)
*/
this.handleBlankMouseDown = function (_a) {
var e = _a.e;
_this.handleMouseDown(e);
};
this.draw = function (ev) {
var _a = _this.lf.getPointByClient(ev.clientX, ev.clientY).domOverlayPosition, x1 = _a.x, y1 = _a.y;
_this.endPoint = {
x: x1,
y: y1,
};
if (_this.startPoint) {
var _b = _this.startPoint, x = _b.x, y = _b.y;
var left = x;
var top_1 = y;
var width = x1 - x;
var height = y1 - y;
if (x1 < x) {
left = x1;
width = x - x1;
}
if (y1 < y) {
top_1 = y1;
height = y - y1;
}
if (_this.wrapper) {
_this.wrapper.style.left = "".concat(left, "px");
_this.wrapper.style.top = "".concat(top_1, "px");
_this.wrapper.style.width = "".concat(width, "px");
_this.wrapper.style.height = "".concat(height, "px");
}
}
};
this.drawOff = function (e) {
// 处理鼠标抬起事件
// 首先判断是否是点击,如果是,则清空框选
if (_this.mouseDownInfo) {
var _a = _this.mouseDownInfo, x = _a.x, y = _a.y, time = _a.time;
var isClick = Math.abs(e.clientX - x) < 5 &&
Math.abs(e.clientY - y) < 5 &&
Date.now() - time < 200;
if (isClick) {
_this.lf.clearSelectElements();
_this.cleanupSelectionState();
return;
}
}
var curStartPoint = cloneDeep(_this.startPoint);
var curEndPoint = cloneDeep(_this.endPoint);
document.removeEventListener('mousemove', _this.draw);
if (!_this.exclusiveMode) {
document.removeEventListener('mouseup', _this.drawOff);
}
// 恢复原始的 stopMoveGraph 设置
_this.lf.updateEditConfig({
stopMoveGraph: _this.originalStopMoveGraph,
});
if (curStartPoint && curEndPoint) {
var x = curStartPoint.x, y = curStartPoint.y;
var x1 = curEndPoint.x, y1 = curEndPoint.y;
// 返回框选范围,左上角和右下角的坐标
var lt = [Math.min(x, x1), Math.min(y, y1)];
var rb = [Math.max(x, x1), Math.max(y, y1)];
_this.lf.emit('selection:selected-area', {
topLeft: lt,
bottomRight: rb,
});
// 选区太小的情况就忽略
if (Math.abs(x1 - x) < 10 && Math.abs(y1 - y) < 10) {
if (_this.wrapper) {
_this.wrapper.oncontextmenu = null;
if (_this.container && _this.wrapper.parentNode === _this.container) {
_this.container.removeChild(_this.wrapper);
}
_this.wrapper = undefined;
}
return;
}
var elements_1 = _this.lf.graphModel.getAreaElement(lt, rb, _this.isWholeEdge, _this.isWholeNode, true);
var _b = _this.lf.graphModel, dynamicGroup_1 = _b.dynamicGroup, group_1 = _b.group;
var nonGroupedElements_1 = [];
var selectedElements = _this.lf.getSelectElements();
// 同时记录节点和边的ID
var selectedIds_1 = new Set(__spreadArray(__spreadArray([], __read(selectedElements.nodes.map(function (node) { return node.id; })), false), __read(selectedElements.edges.map(function (edge) { return edge.id; })), false));
elements_1.forEach(function (element) {
// 如果节点属于分组,则不选中节点,此处兼容旧版 Group 插件
if (group_1) {
var elementGroup = group_1.getNodeGroup(element.id);
if (elements_1.includes(elementGroup)) {
// 当被选中的元素的父分组被选中时,不选中该元素
return;
}
}
if (dynamicGroup_1) {
var elementGroup = dynamicGroup_1.getGroupByNodeId(element.id);
if (elements_1.includes(elementGroup)) {
// 当被选中的元素的父分组被选中时,不选中该元素
return;
}
}
// 在独占模式下,如果元素已经被选中,则取消选中
if (_this.exclusiveMode && selectedIds_1.has(element.id)) {
_this.lf.deselectElementById(element.id);
return;
}
// 非独占模式下,或者元素未被选中时,选中元素
_this.lf.selectElementById(element.id, true);
nonGroupedElements_1.push(element);
});
// 重置起始点和终点
// 注意:这两个值必须在触发closeSelectionSelect方法前充值,否则会导致独占模式下元素无法选中的问题
_this.startPoint = undefined;
_this.endPoint = undefined;
// 如果有选中的元素,触发 selection:drop 事件
if (nonGroupedElements_1.length > 0) {
_this.lf.emit('selection:drop', { e: e });
}
// 触发 selection:selected 事件
_this.lf.emit('selection:selected', {
elements: nonGroupedElements_1,
leftTopPoint: lt,
rightBottomPoint: rb,
});
}
if (_this.wrapper) {
_this.wrapper.oncontextmenu = null;
if (_this.container && _this.wrapper.parentNode === _this.container) {
_this.container.removeChild(_this.wrapper);
}
_this.wrapper = undefined;
}
};
this.lf = lf;
this.exclusiveMode = (_b = options === null || options === void 0 ? void 0 : options.exclusiveMode) !== null && _b !== void 0 ? _b : false;
// TODO: 有没有既能将方法挂载到lf上,又能提供类型提示的方法?
lf.openSelectionSelect = function () {
_this.openSelectionSelect();
};
lf.closeSelectionSelect = function () {
_this.closeSelectionSelect();
};
// 新增切换独占模式的方法
lf.setSelectionSelectMode = function (exclusive) {
_this.setExclusiveMode(exclusive);
};
// 绑定方法的 this 上下文
this.handleMouseDown = this.handleMouseDown.bind(this);
this.draw = this.draw.bind(this);
this.drawOff = this.drawOff.bind(this);
}
SelectionSelect.prototype.render = function (_, domContainer) {
this.container = domContainer;
};
/**
* 清理选区状态
*/
SelectionSelect.prototype.cleanupSelectionState = function () {
// 清理当前的选区状态
if (this.wrapper) {
this.wrapper.oncontextmenu = null;
if (this.container && this.wrapper.parentNode === this.container) {
this.container.removeChild(this.wrapper);
}
this.wrapper = undefined;
}
this.startPoint = undefined;
this.endPoint = undefined;
this.mouseDownInfo = null;
// 移除事件监听
document.removeEventListener('mousemove', this.draw);
document.removeEventListener('mouseup', this.drawOff);
};
/**
* 切换框选模式
* @param exclusive 是否为独占模式。true 表示只能进行框选操作,false 表示可以同时进行其他画布操作
*/
SelectionSelect.prototype.setExclusiveMode = function (exclusive) {
if (exclusive === void 0) { exclusive = false; }
if (this.exclusiveMode === exclusive)
return;
this.cleanupSelectionState();
this.exclusiveMode = exclusive;
if (this.container && !this.disabled) {
// 切换事件监听方式
this.removeEventListeners();
this.addEventListeners();
}
};
SelectionSelect.prototype.addEventListeners = function () {
if (!this.container)
return;
if (this.exclusiveMode) {
// 独占模式:监听 container 的 mousedown 事件
this.container.style.pointerEvents = 'auto';
this.container.addEventListener('mousedown', this.handleMouseDown);
}
else {
// 非独占模式:监听画布的 blank:mousedown 事件
this.container.style.pointerEvents = 'none';
// 使用实例方法而不是箭头函数,这样可以正确移除事件监听
this.lf.on('blank:mousedown', this.handleBlankMouseDown);
}
};
SelectionSelect.prototype.removeEventListeners = function () {
if (this.container) {
this.container.style.pointerEvents = 'none';
this.container.removeEventListener('mousedown', this.handleMouseDown);
}
// 移除 blank:mousedown 事件监听
this.lf.off('blank:mousedown', this.handleBlankMouseDown);
};
/**
* 处理鼠标按下事件
*/
SelectionSelect.prototype.handleMouseDown = function (e) {
var _a;
if (!this.container || this.disabled)
return;
// 禁用右键框选
var isRightClick = e.button === 2;
if (isRightClick)
return;
// 清理之前可能存在的选区状态
this.cleanupSelectionState();
// 记录鼠标按下时的位置和时间
this.mouseDownInfo = {
x: e.clientX,
y: e.clientY,
time: Date.now(),
};
// 记录原始设置并临时禁止画布移动
this.originalStopMoveGraph = this.lf.getEditConfig().stopMoveGraph;
this.lf.updateEditConfig({
stopMoveGraph: true,
});
var _b = this.lf.getPointByClient(e.clientX, e.clientY).domOverlayPosition, x = _b.x, y = _b.y;
this.startPoint = { x: x, y: y };
this.endPoint = { x: x, y: y };
var wrapper = document.createElement('div');
wrapper.className = 'lf-selection-select';
wrapper.oncontextmenu = function prevent(ev) {
ev.preventDefault();
};
wrapper.style.top = "".concat(this.startPoint.y, "px");
wrapper.style.left = "".concat(this.startPoint.x, "px");
(_a = this.container) === null || _a === void 0 ? void 0 : _a.appendChild(wrapper);
this.wrapper = wrapper;
document.addEventListener('mousemove', this.draw);
document.addEventListener('mouseup', this.drawOff);
};
/**
* 设置选中的灵敏度
* @param isWholeEdge 是否要边的起点终点都在选区范围才算选中。默认true
* @param isWholeNode 是否要节点的全部点都在选区范围才算选中。默认true
*/
SelectionSelect.prototype.setSelectionSense = function (isWholeEdge, isWholeNode) {
if (isWholeEdge === void 0) { isWholeEdge = true; }
if (isWholeNode === void 0) { isWholeNode = true; }
this.isWholeEdge = isWholeEdge;
this.isWholeNode = isWholeNode;
};
/**
* 开启选区
*/
SelectionSelect.prototype.openSelectionSelect = function () {
if (!this.disabled) {
this.closeSelectionSelect();
}
if (!this.container) {
return;
}
this.cleanupSelectionState();
this.addEventListeners();
this.open();
};
/**
* 关闭选区
*/
SelectionSelect.prototype.closeSelectionSelect = function () {
if (!this.container) {
return;
}
// 如果还有未完成的框选,先触发 drawOff 完成框选
if (this.wrapper && this.startPoint && this.endPoint) {
// 记录上一次的结束点,用于触发 mouseup 事件
var lastEndPoint = cloneDeep(this.endPoint);
var lastEvent = new MouseEvent('mouseup', {
clientX: lastEndPoint.x,
clientY: lastEndPoint.y,
});
this.drawOff(lastEvent);
}
this.cleanupSelectionState();
this.removeEventListeners();
this.close();
};
SelectionSelect.prototype.open = function () {
this.disabled = false;
};
SelectionSelect.prototype.close = function () {
this.disabled = true;
};
SelectionSelect.pluginName = 'selectionSelect';
return SelectionSelect;
}());
export { SelectionSelect };
export default SelectionSelect;