@logicflow/core
Version:
LogicFlow, help you quickly create flowcharts
403 lines (402 loc) • 19 kB
JavaScript
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 __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
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;
};
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
import { Component } from 'preact/compat';
import { find, forEach, map, cloneDeep } from 'lodash-es';
import { Rect } from './shape';
import { getNodeBBox, StepDrag, handleResize } from '../util';
import { EventType } from '../constant';
export var ResizeControlIndex;
(function (ResizeControlIndex) {
ResizeControlIndex[ResizeControlIndex["LEFT_TOP"] = 0] = "LEFT_TOP";
ResizeControlIndex[ResizeControlIndex["RIGHT_TOP"] = 1] = "RIGHT_TOP";
ResizeControlIndex[ResizeControlIndex["RIGHT_BOTTOM"] = 2] = "RIGHT_BOTTOM";
ResizeControlIndex[ResizeControlIndex["LEFT_BOTTOM"] = 3] = "LEFT_BOTTOM";
})(ResizeControlIndex || (ResizeControlIndex = {}));
var ResizeControl = /** @class */ (function (_super) {
__extends(ResizeControl, _super);
function ResizeControl(props) {
var _this = _super.call(this) || this;
//判断Shift键状态
_this.isShiftPressed = false;
//绑定键盘事件监听
_this.bindKeyboardEvents = function () {
document.addEventListener('keydown', _this.handleKeyDown);
document.addEventListener('keyup', _this.handleKeyUp);
};
//处理键盘按下事件
_this.handleKeyDown = function (event) {
if (event.key === 'Shift') {
_this.isShiftPressed = true;
}
};
_this.handleKeyUp = function (event) {
if (event.key === 'Shift') {
_this.isShiftPressed = false;
}
};
_this.updateEdgePointByAnchors = function () {
// https://github.com/didi/LogicFlow/issues/807
// https://github.com/didi/LogicFlow/issues/875
// 之前的做法,比如Rect是使用getRectResizeEdgePoint()计算边的point缩放后的位置
// getRectResizeEdgePoint()考虑了瞄点在四条边以及在4个圆角的情况
// 使用的是一种等比例缩放的模式,比如:
// const pct = (y - beforeNode.y) / (beforeNode.height / 2 - radius)
// afterPoint.y = afterNode.y + (afterNode.height / 2 - radius) * pct
// 但是用户自定义的getDefaultAnchor()不一定是按照比例编写的
// 它可能是 x: x + 20:每次缩放都会保持在x右边20的位置,因此用户自定义瞄点时,然后产生无法跟随的问题
// 现在的做法是:直接获取用户自定义瞄点的位置,然后用这个位置作为边的新的起点,而不是自己进行计算
var _a = _this.nodeModel, id = _a.id, anchors = _a.anchors;
var edges = _this.graphModel.getNodeEdges(id);
// 更新边
forEach(edges, function (edge) {
if (edge.sourceNodeId === id) {
// 边是以该节点为 sourceNode 时
var anchorItem = find(anchors, function (anchor) { return anchor.id === edge.sourceAnchorId; });
if (anchorItem) {
edge.updateStartPoint({
x: anchorItem.x,
y: anchorItem.y,
});
}
}
else if (edge.targetNodeId === id) {
// 边是以该节点为 targetNode 时
var anchorItem = find(anchors, function (anchor) { return anchor.id === edge.targetAnchorId; });
if (anchorItem) {
edge.updateEndPoint({
x: anchorItem.x,
y: anchorItem.y,
});
}
}
});
};
_this.triggerResizeEvent = function (preNodeData, curNodeData, deltaX, deltaY, index, nodeModel) {
_this.graphModel.eventCenter.emit(EventType.NODE_RESIZE, {
preData: preNodeData,
data: curNodeData,
deltaX: deltaX,
deltaY: deltaY,
index: index,
model: nodeModel,
});
};
/**
* 计算 Control 拖动后,节点的高度信息
* @param index
* @param resizeInfo
* @param pct
* @param freezeWidth
* @param freezeHeight
*/
_this.recalcResizeInfo = function (index, resizeInfo, pct, freezeWidth, freezeHeight) {
if (pct === void 0) { pct = 1; }
if (freezeWidth === void 0) { freezeWidth = false; }
if (freezeHeight === void 0) { freezeHeight = false; }
var nextResizeInfo = cloneDeep(resizeInfo);
var deltaX = nextResizeInfo.deltaX, deltaY = nextResizeInfo.deltaY;
var width = nextResizeInfo.width, height = nextResizeInfo.height, PCTResizeInfo = nextResizeInfo.PCTResizeInfo;
if (PCTResizeInfo) {
var sensitivity = 4; // 越低越灵敏
var deltaScale = 0;
var combineDelta = 0;
switch (index) {
case ResizeControlIndex.LEFT_TOP:
combineDelta = (deltaX * -1 - deltaY) / sensitivity;
break;
case ResizeControlIndex.RIGHT_TOP:
combineDelta = (deltaX - deltaY) / sensitivity;
break;
case ResizeControlIndex.RIGHT_BOTTOM:
combineDelta = (deltaX + deltaY) / sensitivity;
break;
case ResizeControlIndex.LEFT_BOTTOM:
combineDelta = (deltaX * -1 + deltaY) / sensitivity;
break;
default:
break;
}
if (combineDelta !== 0) {
deltaScale =
Math.round((combineDelta / PCTResizeInfo.ResizeBasis.basisHeight) * 100000) / 1000;
}
PCTResizeInfo.ResizePCT.widthPCT = Math.max(Math.min(PCTResizeInfo.ResizePCT.widthPCT + deltaScale, PCTResizeInfo.ScaleLimit.maxScaleLimit), PCTResizeInfo.ScaleLimit.minScaleLimit);
PCTResizeInfo.ResizePCT.heightPCT = Math.max(Math.min(PCTResizeInfo.ResizePCT.heightPCT + deltaScale, PCTResizeInfo.ScaleLimit.maxScaleLimit), PCTResizeInfo.ScaleLimit.minScaleLimit);
var spcWidth = Math.round((PCTResizeInfo.ResizePCT.widthPCT *
PCTResizeInfo.ResizeBasis.basisWidth) /
100);
var spcHeight = Math.round((PCTResizeInfo.ResizePCT.heightPCT *
PCTResizeInfo.ResizeBasis.basisHeight) /
100);
switch (index) {
case ResizeControlIndex.LEFT_TOP:
deltaX = width - spcWidth;
deltaY = height - spcHeight;
break;
case ResizeControlIndex.RIGHT_TOP:
deltaX = spcWidth - width;
deltaY = height - spcHeight;
break;
case ResizeControlIndex.RIGHT_BOTTOM:
deltaX = spcWidth - width;
deltaY = spcHeight - height;
break;
case ResizeControlIndex.LEFT_BOTTOM:
deltaX = width - spcWidth;
deltaY = spcHeight - height;
break;
default:
break;
}
return nextResizeInfo;
}
// 如果限制了宽/高不变,对应的 width/height 保持一致
switch (index) {
case ResizeControlIndex.LEFT_TOP:
nextResizeInfo.width = freezeWidth ? width : width - deltaX * pct;
nextResizeInfo.height = freezeHeight ? height : height - deltaY * pct;
break;
case ResizeControlIndex.RIGHT_TOP:
nextResizeInfo.width = freezeWidth ? width : width + deltaX * pct;
nextResizeInfo.height = freezeHeight ? height : height - deltaY * pct;
break;
case ResizeControlIndex.RIGHT_BOTTOM:
nextResizeInfo.width = freezeWidth ? width : width + deltaX * pct;
nextResizeInfo.height = freezeHeight ? height : height + deltaY * pct;
break;
case ResizeControlIndex.LEFT_BOTTOM:
nextResizeInfo.width = freezeWidth ? width : width - deltaX * pct;
nextResizeInfo.height = freezeHeight ? height : height + deltaY * pct;
break;
default:
break;
}
return nextResizeInfo;
};
_this.resizeNode = function (_a) {
var deltaX = _a.deltaX, deltaY = _a.deltaY;
var index = _this.index;
var _b = _this.props, model = _b.model, graphModel = _b.graphModel, x = _b.x, y = _b.y;
// DONE: 调用每个节点中更新缩放时的方法 updateNode 函数,用来各节点缩放的方法
handleResize({
x: x,
y: y,
deltaX: deltaX,
deltaY: deltaY,
index: index,
nodeModel: model,
graphModel: graphModel,
forceProportional: _this.isShiftPressed,
cancelCallback: function () {
_this.dragHandler.cancelDrag();
},
});
// 1. 计算当前 Control 的一些信息,
// const {
// r, // circle
// rx, // ellipse/diamond
// ry,
// width, // rect/html
// height,
// PCTResizeInfo,
//
// minWidth,
// minHeight,
// maxWidth,
// maxHeight,
// } = this.nodeModel
// const isFreezeWidth = minWidth === maxWidth
// const isFreezeHeight = minHeight === maxHeight
//
// const resizeInfo = {
// width: r || rx || width,
// height: r || ry || height,
// deltaX,
// deltaY,
// PCTResizeInfo,
// }
//
// const pct = r || (rx && ry) ? 1 / 2 : 1
// const nextSize = this.recalcResizeInfo(
// this.index,
// resizeInfo,
// pct,
// isFreezeWidth,
// isFreezeHeight,
// )
//
// // 限制放大缩小的最大最小范围
// if (
// nextSize.width < minWidth ||
// nextSize.width > maxWidth ||
// nextSize.height < minHeight ||
// nextSize.height > maxHeight
// ) {
// this.dragHandler.cancelDrag()
// return
// }
// // 如果限制了宽高不变,对应的 x/y 不产生位移
// nextSize.deltaX = isFreezeWidth ? 0 : nextSize.deltaX
// nextSize.deltaY = isFreezeWidth ? 0 : nextSize.deltaY
//
// const preNodeData = this.nodeModel.getData()
// const curNodeData = this.nodeModel.resize(nextSize)
//
// // 更新边
// this.updateEdgePointByAnchors()
// // 触发 resize 事件
// this.triggerResizeEvent(preNodeData, curNodeData, deltaX, deltaY, this.index, this.nodeModel)
};
_this.onDragStart = function () {
_this.graphModel.selectNodeById(_this.nodeModel.id);
};
_this.onDragging = function (_a) {
var deltaX = _a.deltaX, deltaY = _a.deltaY;
var transformModel = _this.graphModel.transformModel;
var _b = __read(transformModel.fixDeltaXY(deltaX, deltaY), 2), dx = _b[0], dy = _b[1];
_this.resizeNode({
deltaX: dx,
deltaY: dy,
});
};
// 由于将拖拽放大缩小改成丝滑模式,这个时候需要再拖拽结束的时候,将节点的位置更新到 grid 上。
_this.onDragEnd = function () {
// TODO: 确认下面该代码是否还需要(应该是默认让节点拖拽以 gridSize 为步长移动)
// const { gridSize = 1 } = this.graphModel
// const x = gridSize * Math.round(this.nodeModel.x / gridSize)
// const y = gridSize * Math.round(this.nodeModel.y / gridSize)
var x = _this.nodeModel.x;
var y = _this.nodeModel.y;
_this.nodeModel.moveTo(x, y);
// 先触发 onDragging() -> 更新边 -> 再触发用户自定义的 getDefaultAnchor(),所以 onDragging()
// 拿到的 anchors 是滞后的,为了正确的设置最终的位置,应该在拖拽结束的时候,再设置一次边的 Point 位置,
// 此时拿到的 anchors 是最新的
_this.updateEdgePointByAnchors();
};
var index = props.index, model = props.model, graphModel = props.graphModel;
_this.index = index;
_this.nodeModel = model;
_this.graphModel = graphModel;
_this.dragHandler = new StepDrag({
onDragStart: _this.onDragStart,
onDragging: _this.onDragging,
onDragEnd: _this.onDragEnd,
step: graphModel.gridSize,
});
_this.bindKeyboardEvents();
return _this;
}
ResizeControl.prototype.componentWillUnmount = function () {
this.dragHandler.destroy();
document.removeEventListener('keydown', this.handleKeyDown);
document.removeEventListener('keyup', this.handleKeyUp);
};
ResizeControl.prototype.render = function () {
var _a = this.props, x = _a.x, y = _a.y, direction = _a.direction, model = _a.model;
var _b = model.getResizeControlStyle(), width = _b.width, height = _b.height, restStyle = __rest(_b, ["width", "height"]);
return (_jsxs("g", { className: "lf-resize-control lf-resize-control-".concat(direction), children: [_jsx(Rect, __assign({ className: "lf-resize-control-content", x: x, y: y, width: width !== null && width !== void 0 ? width : 7, height: height !== null && height !== void 0 ? height : 7 }, restStyle)), _jsx(Rect, { className: "lf-resize-control-content", x: x, y: y, width: 25, height: 25, fill: "transparent", stroke: "transparent", onMouseDown: this.dragHandler.handleMouseDown })] }));
};
return ResizeControl;
}(Component));
export { ResizeControl };
var ResizeControlGroup = /** @class */ (function (_super) {
__extends(ResizeControlGroup, _super);
function ResizeControlGroup() {
return _super.call(this) || this;
}
ResizeControlGroup.prototype.getResizeControl = function () {
var _a = this.props, model = _a.model, graphModel = _a.graphModel;
var _b = getNodeBBox(model), minX = _b.minX, minY = _b.minY, maxX = _b.maxX, maxY = _b.maxY;
var controlList = [
{
index: ResizeControlIndex.LEFT_TOP,
direction: 'nw',
x: minX,
y: minY,
}, // 左上角
{
index: ResizeControlIndex.RIGHT_TOP,
direction: 'ne',
x: maxX,
y: minY,
}, // 右上角
{
index: ResizeControlIndex.RIGHT_BOTTOM,
direction: 'se',
x: maxX,
y: maxY,
}, // 右下角
{
index: ResizeControlIndex.LEFT_BOTTOM,
direction: 'sw',
x: minX,
y: maxY,
}, // 左下角
];
return map(controlList, function (control) { return (_jsx(ResizeControl, __assign({}, control, { model: model, graphModel: graphModel }))); });
};
ResizeControlGroup.prototype.getResizeOutline = function () {
var model = this.props.model;
var x = model.x, y = model.y, width = model.width, height = model.height;
var style = model.getResizeOutlineStyle();
return _jsx(Rect, __assign({}, style, { x: x, y: y, width: width, height: height }));
};
ResizeControlGroup.prototype.render = function () {
return (_jsxs("g", { className: "lf-resize-control-group", children: [this.getResizeOutline(), this.getResizeControl()] }));
};
return ResizeControlGroup;
}(Component));
export { ResizeControlGroup };
export default ResizeControlGroup;