@logicflow/core 2.2.0-alpha.0 → 2.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$dev.log +2 -2
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +15 -0
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/es/model/EditConfigModel.d.ts +3 -1
- package/es/model/EditConfigModel.js +5 -0
- package/es/model/GraphModel.d.ts +1 -0
- package/es/model/GraphModel.js +2 -1
- package/es/model/node/BaseNodeModel.js +3 -1
- package/es/options.d.ts +3 -1
- package/es/util/drag.js +0 -1
- package/es/view/Anchor.d.ts +1 -0
- package/es/view/Anchor.js +29 -20
- package/lib/model/EditConfigModel.d.ts +3 -1
- package/lib/model/EditConfigModel.js +5 -0
- package/lib/model/GraphModel.d.ts +1 -0
- package/lib/model/GraphModel.js +2 -1
- package/lib/model/node/BaseNodeModel.js +3 -1
- package/lib/options.d.ts +3 -1
- package/lib/util/drag.js +0 -1
- package/lib/view/Anchor.d.ts +1 -0
- package/lib/view/Anchor.js +29 -20
- package/package.json +1 -1
- package/src/model/EditConfigModel.ts +3 -0
- package/src/model/GraphModel.ts +4 -0
- package/src/model/node/BaseNodeModel.ts +4 -1
- package/src/options.ts +6 -1
- package/src/util/drag.ts +0 -1
- package/src/view/Anchor.tsx +53 -30
- package/stats.html +1 -1
|
@@ -115,6 +115,7 @@ export interface IEditConfigType {
|
|
|
115
115
|
edgeTextMode: TextMode;
|
|
116
116
|
snapGrid: boolean;
|
|
117
117
|
isPinching: boolean;
|
|
118
|
+
anchorProximityValidate: boolean;
|
|
118
119
|
}
|
|
119
120
|
export type IConfigKeys = keyof IEditConfigType;
|
|
120
121
|
/**
|
|
@@ -147,6 +148,7 @@ export declare class EditConfigModel {
|
|
|
147
148
|
edgeTextDraggable: boolean;
|
|
148
149
|
edgeTextMultiple: boolean;
|
|
149
150
|
edgeTextVertical: boolean;
|
|
151
|
+
anchorProximityValidate: boolean;
|
|
150
152
|
/*********************************************************
|
|
151
153
|
* 节点相关配置
|
|
152
154
|
********************************************************/
|
|
@@ -172,7 +174,7 @@ export declare class EditConfigModel {
|
|
|
172
174
|
multipleSelectKey: string;
|
|
173
175
|
constructor(config: Partial<IEditConfigType>);
|
|
174
176
|
updateEditConfig(config: Partial<IEditConfigType>): void;
|
|
175
|
-
computeConfig(config: Partial<IEditConfigType>): Partial<IEditConfigType> & Pick<Partial<IEditConfigType>, "textMode" | "adjustEdgeStartAndEnd" | "edgeTextDraggable" | "edgeTextEdit" | "nodeTextDraggable" | "nodeTextEdit" | "adjustEdge" | "edgeTextMode" | "nodeTextMode" | "allowRotate" | "allowResize" | "isSilentMode" | "stopScrollGraph" | "stopZoomGraph" | "stopMoveGraph" | "textEdit" | "snapGrid" | "adjustEdgeMiddle" | "adjustEdgeStart" | "adjustEdgeEnd" | "adjustNodePosition" | "hideAnchors" | "autoExpand" | "hoverOutline" | "nodeSelectedOutline" | "edgeSelectedOutline" | "textDraggable" | "multipleSelectKey" | "nodeTextMultiple" | "edgeTextMultiple" | "nodeTextVertical" | "edgeTextVertical" | "isPinching">;
|
|
177
|
+
computeConfig(config: Partial<IEditConfigType>): Partial<IEditConfigType> & Pick<Partial<IEditConfigType>, "textMode" | "adjustEdgeStartAndEnd" | "edgeTextDraggable" | "edgeTextEdit" | "nodeTextDraggable" | "nodeTextEdit" | "adjustEdge" | "edgeTextMode" | "nodeTextMode" | "allowRotate" | "allowResize" | "isSilentMode" | "stopScrollGraph" | "stopZoomGraph" | "stopMoveGraph" | "textEdit" | "snapGrid" | "adjustEdgeMiddle" | "adjustEdgeStart" | "adjustEdgeEnd" | "adjustNodePosition" | "hideAnchors" | "autoExpand" | "hoverOutline" | "nodeSelectedOutline" | "edgeSelectedOutline" | "textDraggable" | "multipleSelectKey" | "nodeTextMultiple" | "edgeTextMultiple" | "nodeTextVertical" | "edgeTextVertical" | "isPinching" | "anchorProximityValidate">;
|
|
176
178
|
updateTextMode(textMode: TextMode): void;
|
|
177
179
|
getConfig(): IEditConfigType;
|
|
178
180
|
}
|
|
@@ -63,6 +63,7 @@ var allKeys = [
|
|
|
63
63
|
'nodeTextVertical', // 节点文本是否纵向显示
|
|
64
64
|
'edgeTextVertical', // 边文本是否纵向显示
|
|
65
65
|
'isPinching', //是否是双指捏合态
|
|
66
|
+
'anchorProximityValidate', // 仅在靠近锚点时触发连接校验
|
|
66
67
|
];
|
|
67
68
|
/**
|
|
68
69
|
* 页面编辑配置
|
|
@@ -96,6 +97,7 @@ var EditConfigModel = /** @class */ (function () {
|
|
|
96
97
|
this.edgeTextDraggable = false;
|
|
97
98
|
this.edgeTextMultiple = false; // 是否支持多个边文本
|
|
98
99
|
this.edgeTextVertical = false; // 边文本朝向是否是纵向
|
|
100
|
+
this.anchorProximityValidate = false; // 仅在靠近锚点时触发连接校验
|
|
99
101
|
/*********************************************************
|
|
100
102
|
* 节点相关配置
|
|
101
103
|
********************************************************/
|
|
@@ -237,6 +239,9 @@ var EditConfigModel = /** @class */ (function () {
|
|
|
237
239
|
__decorate([
|
|
238
240
|
observable
|
|
239
241
|
], EditConfigModel.prototype, "edgeTextVertical", void 0);
|
|
242
|
+
__decorate([
|
|
243
|
+
observable
|
|
244
|
+
], EditConfigModel.prototype, "anchorProximityValidate", void 0);
|
|
240
245
|
__decorate([
|
|
241
246
|
observable
|
|
242
247
|
], EditConfigModel.prototype, "hideAnchors", void 0);
|
package/es/model/GraphModel.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export declare class GraphModel {
|
|
|
30
30
|
animation?: boolean | LFOptions.AnimationConfig;
|
|
31
31
|
idGenerator?: (type?: string) => string | undefined;
|
|
32
32
|
edgeGenerator: LFOptions.Definition['edgeGenerator'];
|
|
33
|
+
customTargetAnchor?: LFOptions.Definition['customTargetAnchor'];
|
|
33
34
|
nodeModelMap: Map<string, BaseNodeModel>;
|
|
34
35
|
edgeModelMap: Map<string, BaseEdgeModel>;
|
|
35
36
|
elementsModelMap: Map<string, BaseNodeModel | BaseEdgeModel>;
|
package/es/model/GraphModel.js
CHANGED
|
@@ -96,7 +96,7 @@ var GraphModel = /** @class */ (function () {
|
|
|
96
96
|
// 控制是否开启局部渲染
|
|
97
97
|
this.partial = false;
|
|
98
98
|
this.waitCleanEffects = [];
|
|
99
|
-
var container = options.container, partial = options.partial, _c = options.background, background = _c === void 0 ? {} : _c, grid = options.grid, idGenerator = options.idGenerator, edgeGenerator = options.edgeGenerator, animation = options.animation, customTrajectory = options.customTrajectory;
|
|
99
|
+
var container = options.container, partial = options.partial, _c = options.background, background = _c === void 0 ? {} : _c, grid = options.grid, idGenerator = options.idGenerator, edgeGenerator = options.edgeGenerator, animation = options.animation, customTrajectory = options.customTrajectory, customTargetAnchor = options.customTargetAnchor;
|
|
100
100
|
this.rootEl = container;
|
|
101
101
|
this.partial = !!partial;
|
|
102
102
|
this.background = background;
|
|
@@ -151,6 +151,7 @@ var GraphModel = /** @class */ (function () {
|
|
|
151
151
|
this.idGenerator = idGenerator;
|
|
152
152
|
this.edgeGenerator = createEdgeGenerator(this, edgeGenerator);
|
|
153
153
|
this.customTrajectory = customTrajectory;
|
|
154
|
+
this.customTargetAnchor = customTargetAnchor;
|
|
154
155
|
}
|
|
155
156
|
Object.defineProperty(GraphModel.prototype, "nodesMap", {
|
|
156
157
|
get: function () {
|
|
@@ -549,7 +549,9 @@ var BaseNodeModel = /** @class */ (function () {
|
|
|
549
549
|
* 手动连接边到节点时,需要连接的锚点
|
|
550
550
|
*/
|
|
551
551
|
BaseNodeModel.prototype.getTargetAnchor = function (position) {
|
|
552
|
-
|
|
552
|
+
var _a;
|
|
553
|
+
var customTargetAnchor = this.graphModel.customTargetAnchor;
|
|
554
|
+
return ((_a = customTargetAnchor === null || customTargetAnchor === void 0 ? void 0 : customTargetAnchor(this, position)) !== null && _a !== void 0 ? _a : getClosestAnchor(position, this));
|
|
553
555
|
};
|
|
554
556
|
/**
|
|
555
557
|
* 获取节点BBox
|
package/es/options.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TransformModel } from './model';
|
|
1
|
+
import type { TransformModel, BaseNodeModel, Model } from './model';
|
|
2
2
|
import { createElement as h } from 'preact/compat';
|
|
3
3
|
import LogicFlow from './LogicFlow';
|
|
4
4
|
import { KeyboardDef } from './keyboard';
|
|
@@ -21,6 +21,7 @@ export declare namespace Options {
|
|
|
21
21
|
edge: boolean;
|
|
22
22
|
};
|
|
23
23
|
type EdgeGeneratorType = (sourceNode: LogicFlow.NodeData, targetNode: LogicFlow.NodeData, currentEdge?: Partial<LogicFlow.EdgeConfig>) => any;
|
|
24
|
+
type customTargetAnchorType = (nodeModel: BaseNodeModel, position: LogicFlow.Point) => Model.AnchorInfo | undefined;
|
|
24
25
|
interface CustomAnchorLineProps {
|
|
25
26
|
sourcePoint: LogicFlow.Point;
|
|
26
27
|
targetPoint: LogicFlow.Point;
|
|
@@ -67,6 +68,7 @@ export declare namespace Options {
|
|
|
67
68
|
disabledTools?: string[];
|
|
68
69
|
idGenerator?: (type?: string) => string;
|
|
69
70
|
edgeGenerator?: EdgeGeneratorType;
|
|
71
|
+
customTargetAnchor?: customTargetAnchorType;
|
|
70
72
|
customTrajectory?: (props: CustomAnchorLineProps) => h.JSX.Element;
|
|
71
73
|
themeMode?: 'radius' | 'dark' | 'colorful';
|
|
72
74
|
parentTransform?: TransformModel;
|
package/es/util/drag.js
CHANGED
package/es/view/Anchor.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ declare class Anchor extends Component<IProps, IState> {
|
|
|
40
40
|
};
|
|
41
41
|
checkEnd: (event: PointerEvent | null | undefined) => import("../model").BaseEdgeModel<LogicFlow.PropertiesType> | null | undefined;
|
|
42
42
|
moveAnchorEnd(endX: number, endY: number, event?: PointerEvent): void;
|
|
43
|
+
validateAndSetState(targetNode: BaseNodeModel, anchorId: string | undefined, targetAnchor: AnchorConfig, nodeModel: BaseNodeModel, anchorData: AnchorConfig): void;
|
|
43
44
|
isShowLine(): boolean;
|
|
44
45
|
render(): import("preact/compat").JSX.Element;
|
|
45
46
|
}
|
package/es/view/Anchor.js
CHANGED
|
@@ -248,8 +248,7 @@ var Anchor = /** @class */ (function (_super) {
|
|
|
248
248
|
configurable: true
|
|
249
249
|
});
|
|
250
250
|
Anchor.prototype.moveAnchorEnd = function (endX, endY, event) {
|
|
251
|
-
var _a,
|
|
252
|
-
var _c = this.props, graphModel = _c.graphModel, nodeModel = _c.nodeModel, anchorData = _c.anchorData;
|
|
251
|
+
var _a = this.props, graphModel = _a.graphModel, nodeModel = _a.nodeModel, anchorData = _a.anchorData;
|
|
253
252
|
var info = targetNodeInfo({
|
|
254
253
|
x: endX,
|
|
255
254
|
y: endY,
|
|
@@ -265,24 +264,12 @@ var Anchor = /** @class */ (function (_super) {
|
|
|
265
264
|
return;
|
|
266
265
|
}
|
|
267
266
|
this.preTargetNode = targetNode;
|
|
268
|
-
|
|
269
|
-
var
|
|
270
|
-
|
|
271
|
-
if (!
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
var targetRuleResult = targetNode.isAllowConnectedAsTarget(nodeModel, anchorData, targetAnchor);
|
|
275
|
-
this.sourceRuleResults.set(targetInfoId, formatAnchorConnectValidateData(sourceRuleResult));
|
|
276
|
-
this.targetRuleResults.set(targetInfoId, formatAnchorConnectValidateData(targetRuleResult));
|
|
277
|
-
}
|
|
278
|
-
var isSourcePass = ((_a = this.sourceRuleResults.get(targetInfoId)) !== null && _a !== void 0 ? _a : {}).isAllPass;
|
|
279
|
-
var isTargetPass = ((_b = this.targetRuleResults.get(targetInfoId)) !== null && _b !== void 0 ? _b : {}).isAllPass;
|
|
280
|
-
// 实时提示出即将链接的锚点
|
|
281
|
-
if (isSourcePass && isTargetPass) {
|
|
282
|
-
targetNode.setElementState(ElementState.ALLOW_CONNECT);
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT);
|
|
267
|
+
var anchorDist = distance(endX, endY, info.anchor.x, info.anchor.y);
|
|
268
|
+
var validateDistance = 10;
|
|
269
|
+
var editConfigModel = graphModel.editConfigModel;
|
|
270
|
+
if (!editConfigModel.anchorProximityValidate ||
|
|
271
|
+
anchorDist <= validateDistance) {
|
|
272
|
+
this.validateAndSetState(targetNode, anchorId, info.anchor, nodeModel, anchorData);
|
|
286
273
|
}
|
|
287
274
|
// 人工触发进入目标节点事件,同步设置 hovered 以驱动锚点显隐和样式
|
|
288
275
|
if (!targetNode.isHovered) {
|
|
@@ -312,6 +299,28 @@ var Anchor = /** @class */ (function (_super) {
|
|
|
312
299
|
this.preTargetNode = undefined;
|
|
313
300
|
}
|
|
314
301
|
};
|
|
302
|
+
// 校验 source/target 连接规则并设置目标节点状态
|
|
303
|
+
Anchor.prototype.validateAndSetState = function (targetNode, anchorId, targetAnchor, nodeModel, anchorData) {
|
|
304
|
+
var _a, _b;
|
|
305
|
+
var targetInfoId = "".concat(nodeModel.id, "_").concat(targetNode.id, "_").concat(anchorId, "_").concat(anchorData.id);
|
|
306
|
+
if (!this.targetRuleResults.has(targetInfoId)) {
|
|
307
|
+
// 首次计算并缓存源/目标两侧的规则校验结果
|
|
308
|
+
var sourceRuleResult = nodeModel.isAllowConnectedAsSource(targetNode, anchorData, targetAnchor);
|
|
309
|
+
var targetRuleResult = targetNode.isAllowConnectedAsTarget(nodeModel, anchorData, targetAnchor);
|
|
310
|
+
this.sourceRuleResults.set(targetInfoId, formatAnchorConnectValidateData(sourceRuleResult));
|
|
311
|
+
this.targetRuleResults.set(targetInfoId, formatAnchorConnectValidateData(targetRuleResult));
|
|
312
|
+
}
|
|
313
|
+
// 读取缓存的校验结果
|
|
314
|
+
var isSourcePass = ((_a = this.sourceRuleResults.get(targetInfoId)) !== null && _a !== void 0 ? _a : {}).isAllPass;
|
|
315
|
+
var isTargetPass = ((_b = this.targetRuleResults.get(targetInfoId)) !== null && _b !== void 0 ? _b : {}).isAllPass;
|
|
316
|
+
// 两侧都通过则允许连接,否则标记为不允许连接
|
|
317
|
+
if (isSourcePass && isTargetPass) {
|
|
318
|
+
targetNode.setElementState(ElementState.ALLOW_CONNECT);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
315
324
|
Anchor.prototype.isShowLine = function () {
|
|
316
325
|
var _a = this.state, startX = _a.startX, startY = _a.startY, endX = _a.endX, endY = _a.endY;
|
|
317
326
|
var v = distance(startX, startY, endX, endY);
|
|
@@ -115,6 +115,7 @@ export interface IEditConfigType {
|
|
|
115
115
|
edgeTextMode: TextMode;
|
|
116
116
|
snapGrid: boolean;
|
|
117
117
|
isPinching: boolean;
|
|
118
|
+
anchorProximityValidate: boolean;
|
|
118
119
|
}
|
|
119
120
|
export type IConfigKeys = keyof IEditConfigType;
|
|
120
121
|
/**
|
|
@@ -147,6 +148,7 @@ export declare class EditConfigModel {
|
|
|
147
148
|
edgeTextDraggable: boolean;
|
|
148
149
|
edgeTextMultiple: boolean;
|
|
149
150
|
edgeTextVertical: boolean;
|
|
151
|
+
anchorProximityValidate: boolean;
|
|
150
152
|
/*********************************************************
|
|
151
153
|
* 节点相关配置
|
|
152
154
|
********************************************************/
|
|
@@ -172,7 +174,7 @@ export declare class EditConfigModel {
|
|
|
172
174
|
multipleSelectKey: string;
|
|
173
175
|
constructor(config: Partial<IEditConfigType>);
|
|
174
176
|
updateEditConfig(config: Partial<IEditConfigType>): void;
|
|
175
|
-
computeConfig(config: Partial<IEditConfigType>): Partial<IEditConfigType> & Pick<Partial<IEditConfigType>, "textMode" | "adjustEdgeStartAndEnd" | "edgeTextDraggable" | "edgeTextEdit" | "nodeTextDraggable" | "nodeTextEdit" | "adjustEdge" | "edgeTextMode" | "nodeTextMode" | "allowRotate" | "allowResize" | "isSilentMode" | "stopScrollGraph" | "stopZoomGraph" | "stopMoveGraph" | "textEdit" | "snapGrid" | "adjustEdgeMiddle" | "adjustEdgeStart" | "adjustEdgeEnd" | "adjustNodePosition" | "hideAnchors" | "autoExpand" | "hoverOutline" | "nodeSelectedOutline" | "edgeSelectedOutline" | "textDraggable" | "multipleSelectKey" | "nodeTextMultiple" | "edgeTextMultiple" | "nodeTextVertical" | "edgeTextVertical" | "isPinching">;
|
|
177
|
+
computeConfig(config: Partial<IEditConfigType>): Partial<IEditConfigType> & Pick<Partial<IEditConfigType>, "textMode" | "adjustEdgeStartAndEnd" | "edgeTextDraggable" | "edgeTextEdit" | "nodeTextDraggable" | "nodeTextEdit" | "adjustEdge" | "edgeTextMode" | "nodeTextMode" | "allowRotate" | "allowResize" | "isSilentMode" | "stopScrollGraph" | "stopZoomGraph" | "stopMoveGraph" | "textEdit" | "snapGrid" | "adjustEdgeMiddle" | "adjustEdgeStart" | "adjustEdgeEnd" | "adjustNodePosition" | "hideAnchors" | "autoExpand" | "hoverOutline" | "nodeSelectedOutline" | "edgeSelectedOutline" | "textDraggable" | "multipleSelectKey" | "nodeTextMultiple" | "edgeTextMultiple" | "nodeTextVertical" | "edgeTextVertical" | "isPinching" | "anchorProximityValidate">;
|
|
176
178
|
updateTextMode(textMode: TextMode): void;
|
|
177
179
|
getConfig(): IEditConfigType;
|
|
178
180
|
}
|
|
@@ -66,6 +66,7 @@ var allKeys = [
|
|
|
66
66
|
'nodeTextVertical', // 节点文本是否纵向显示
|
|
67
67
|
'edgeTextVertical', // 边文本是否纵向显示
|
|
68
68
|
'isPinching', //是否是双指捏合态
|
|
69
|
+
'anchorProximityValidate', // 仅在靠近锚点时触发连接校验
|
|
69
70
|
];
|
|
70
71
|
/**
|
|
71
72
|
* 页面编辑配置
|
|
@@ -99,6 +100,7 @@ var EditConfigModel = /** @class */ (function () {
|
|
|
99
100
|
this.edgeTextDraggable = false;
|
|
100
101
|
this.edgeTextMultiple = false; // 是否支持多个边文本
|
|
101
102
|
this.edgeTextVertical = false; // 边文本朝向是否是纵向
|
|
103
|
+
this.anchorProximityValidate = false; // 仅在靠近锚点时触发连接校验
|
|
102
104
|
/*********************************************************
|
|
103
105
|
* 节点相关配置
|
|
104
106
|
********************************************************/
|
|
@@ -240,6 +242,9 @@ var EditConfigModel = /** @class */ (function () {
|
|
|
240
242
|
__decorate([
|
|
241
243
|
mobx_1.observable
|
|
242
244
|
], EditConfigModel.prototype, "edgeTextVertical", void 0);
|
|
245
|
+
__decorate([
|
|
246
|
+
mobx_1.observable
|
|
247
|
+
], EditConfigModel.prototype, "anchorProximityValidate", void 0);
|
|
243
248
|
__decorate([
|
|
244
249
|
mobx_1.observable
|
|
245
250
|
], EditConfigModel.prototype, "hideAnchors", void 0);
|
|
@@ -30,6 +30,7 @@ export declare class GraphModel {
|
|
|
30
30
|
animation?: boolean | LFOptions.AnimationConfig;
|
|
31
31
|
idGenerator?: (type?: string) => string | undefined;
|
|
32
32
|
edgeGenerator: LFOptions.Definition['edgeGenerator'];
|
|
33
|
+
customTargetAnchor?: LFOptions.Definition['customTargetAnchor'];
|
|
33
34
|
nodeModelMap: Map<string, BaseNodeModel>;
|
|
34
35
|
edgeModelMap: Map<string, BaseEdgeModel>;
|
|
35
36
|
elementsModelMap: Map<string, BaseNodeModel | BaseEdgeModel>;
|
package/lib/model/GraphModel.js
CHANGED
|
@@ -102,7 +102,7 @@ var GraphModel = /** @class */ (function () {
|
|
|
102
102
|
// 控制是否开启局部渲染
|
|
103
103
|
this.partial = false;
|
|
104
104
|
this.waitCleanEffects = [];
|
|
105
|
-
var container = options.container, partial = options.partial, _c = options.background, background = _c === void 0 ? {} : _c, grid = options.grid, idGenerator = options.idGenerator, edgeGenerator = options.edgeGenerator, animation = options.animation, customTrajectory = options.customTrajectory;
|
|
105
|
+
var container = options.container, partial = options.partial, _c = options.background, background = _c === void 0 ? {} : _c, grid = options.grid, idGenerator = options.idGenerator, edgeGenerator = options.edgeGenerator, animation = options.animation, customTrajectory = options.customTrajectory, customTargetAnchor = options.customTargetAnchor;
|
|
106
106
|
this.rootEl = container;
|
|
107
107
|
this.partial = !!partial;
|
|
108
108
|
this.background = background;
|
|
@@ -157,6 +157,7 @@ var GraphModel = /** @class */ (function () {
|
|
|
157
157
|
this.idGenerator = idGenerator;
|
|
158
158
|
this.edgeGenerator = (0, util_1.createEdgeGenerator)(this, edgeGenerator);
|
|
159
159
|
this.customTrajectory = customTrajectory;
|
|
160
|
+
this.customTargetAnchor = customTargetAnchor;
|
|
160
161
|
}
|
|
161
162
|
Object.defineProperty(GraphModel.prototype, "nodesMap", {
|
|
162
163
|
get: function () {
|
|
@@ -552,7 +552,9 @@ var BaseNodeModel = /** @class */ (function () {
|
|
|
552
552
|
* 手动连接边到节点时,需要连接的锚点
|
|
553
553
|
*/
|
|
554
554
|
BaseNodeModel.prototype.getTargetAnchor = function (position) {
|
|
555
|
-
|
|
555
|
+
var _a;
|
|
556
|
+
var customTargetAnchor = this.graphModel.customTargetAnchor;
|
|
557
|
+
return ((_a = customTargetAnchor === null || customTargetAnchor === void 0 ? void 0 : customTargetAnchor(this, position)) !== null && _a !== void 0 ? _a : (0, util_1.getClosestAnchor)(position, this));
|
|
556
558
|
};
|
|
557
559
|
/**
|
|
558
560
|
* 获取节点BBox
|
package/lib/options.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TransformModel } from './model';
|
|
1
|
+
import type { TransformModel, BaseNodeModel, Model } from './model';
|
|
2
2
|
import { createElement as h } from 'preact/compat';
|
|
3
3
|
import LogicFlow from './LogicFlow';
|
|
4
4
|
import { KeyboardDef } from './keyboard';
|
|
@@ -21,6 +21,7 @@ export declare namespace Options {
|
|
|
21
21
|
edge: boolean;
|
|
22
22
|
};
|
|
23
23
|
type EdgeGeneratorType = (sourceNode: LogicFlow.NodeData, targetNode: LogicFlow.NodeData, currentEdge?: Partial<LogicFlow.EdgeConfig>) => any;
|
|
24
|
+
type customTargetAnchorType = (nodeModel: BaseNodeModel, position: LogicFlow.Point) => Model.AnchorInfo | undefined;
|
|
24
25
|
interface CustomAnchorLineProps {
|
|
25
26
|
sourcePoint: LogicFlow.Point;
|
|
26
27
|
targetPoint: LogicFlow.Point;
|
|
@@ -67,6 +68,7 @@ export declare namespace Options {
|
|
|
67
68
|
disabledTools?: string[];
|
|
68
69
|
idGenerator?: (type?: string) => string;
|
|
69
70
|
edgeGenerator?: EdgeGeneratorType;
|
|
71
|
+
customTargetAnchor?: customTargetAnchorType;
|
|
70
72
|
customTrajectory?: (props: CustomAnchorLineProps) => h.JSX.Element;
|
|
71
73
|
themeMode?: 'radius' | 'dark' | 'colorful';
|
|
72
74
|
parentTransform?: TransformModel;
|
package/lib/util/drag.js
CHANGED
package/lib/view/Anchor.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ declare class Anchor extends Component<IProps, IState> {
|
|
|
40
40
|
};
|
|
41
41
|
checkEnd: (event: PointerEvent | null | undefined) => import("../model").BaseEdgeModel<LogicFlow.PropertiesType> | null | undefined;
|
|
42
42
|
moveAnchorEnd(endX: number, endY: number, event?: PointerEvent): void;
|
|
43
|
+
validateAndSetState(targetNode: BaseNodeModel, anchorId: string | undefined, targetAnchor: AnchorConfig, nodeModel: BaseNodeModel, anchorData: AnchorConfig): void;
|
|
43
44
|
isShowLine(): boolean;
|
|
44
45
|
render(): import("preact/compat").JSX.Element;
|
|
45
46
|
}
|
package/lib/view/Anchor.js
CHANGED
|
@@ -250,8 +250,7 @@ var Anchor = /** @class */ (function (_super) {
|
|
|
250
250
|
configurable: true
|
|
251
251
|
});
|
|
252
252
|
Anchor.prototype.moveAnchorEnd = function (endX, endY, event) {
|
|
253
|
-
var _a,
|
|
254
|
-
var _c = this.props, graphModel = _c.graphModel, nodeModel = _c.nodeModel, anchorData = _c.anchorData;
|
|
253
|
+
var _a = this.props, graphModel = _a.graphModel, nodeModel = _a.nodeModel, anchorData = _a.anchorData;
|
|
255
254
|
var info = (0, util_1.targetNodeInfo)({
|
|
256
255
|
x: endX,
|
|
257
256
|
y: endY,
|
|
@@ -267,24 +266,12 @@ var Anchor = /** @class */ (function (_super) {
|
|
|
267
266
|
return;
|
|
268
267
|
}
|
|
269
268
|
this.preTargetNode = targetNode;
|
|
270
|
-
|
|
271
|
-
var
|
|
272
|
-
|
|
273
|
-
if (!
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
var targetRuleResult = targetNode.isAllowConnectedAsTarget(nodeModel, anchorData, targetAnchor);
|
|
277
|
-
this.sourceRuleResults.set(targetInfoId, (0, util_1.formatAnchorConnectValidateData)(sourceRuleResult));
|
|
278
|
-
this.targetRuleResults.set(targetInfoId, (0, util_1.formatAnchorConnectValidateData)(targetRuleResult));
|
|
279
|
-
}
|
|
280
|
-
var isSourcePass = ((_a = this.sourceRuleResults.get(targetInfoId)) !== null && _a !== void 0 ? _a : {}).isAllPass;
|
|
281
|
-
var isTargetPass = ((_b = this.targetRuleResults.get(targetInfoId)) !== null && _b !== void 0 ? _b : {}).isAllPass;
|
|
282
|
-
// 实时提示出即将链接的锚点
|
|
283
|
-
if (isSourcePass && isTargetPass) {
|
|
284
|
-
targetNode.setElementState(constant_1.ElementState.ALLOW_CONNECT);
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
targetNode.setElementState(constant_1.ElementState.NOT_ALLOW_CONNECT);
|
|
269
|
+
var anchorDist = (0, util_1.distance)(endX, endY, info.anchor.x, info.anchor.y);
|
|
270
|
+
var validateDistance = 10;
|
|
271
|
+
var editConfigModel = graphModel.editConfigModel;
|
|
272
|
+
if (!editConfigModel.anchorProximityValidate ||
|
|
273
|
+
anchorDist <= validateDistance) {
|
|
274
|
+
this.validateAndSetState(targetNode, anchorId, info.anchor, nodeModel, anchorData);
|
|
288
275
|
}
|
|
289
276
|
// 人工触发进入目标节点事件,同步设置 hovered 以驱动锚点显隐和样式
|
|
290
277
|
if (!targetNode.isHovered) {
|
|
@@ -314,6 +301,28 @@ var Anchor = /** @class */ (function (_super) {
|
|
|
314
301
|
this.preTargetNode = undefined;
|
|
315
302
|
}
|
|
316
303
|
};
|
|
304
|
+
// 校验 source/target 连接规则并设置目标节点状态
|
|
305
|
+
Anchor.prototype.validateAndSetState = function (targetNode, anchorId, targetAnchor, nodeModel, anchorData) {
|
|
306
|
+
var _a, _b;
|
|
307
|
+
var targetInfoId = "".concat(nodeModel.id, "_").concat(targetNode.id, "_").concat(anchorId, "_").concat(anchorData.id);
|
|
308
|
+
if (!this.targetRuleResults.has(targetInfoId)) {
|
|
309
|
+
// 首次计算并缓存源/目标两侧的规则校验结果
|
|
310
|
+
var sourceRuleResult = nodeModel.isAllowConnectedAsSource(targetNode, anchorData, targetAnchor);
|
|
311
|
+
var targetRuleResult = targetNode.isAllowConnectedAsTarget(nodeModel, anchorData, targetAnchor);
|
|
312
|
+
this.sourceRuleResults.set(targetInfoId, (0, util_1.formatAnchorConnectValidateData)(sourceRuleResult));
|
|
313
|
+
this.targetRuleResults.set(targetInfoId, (0, util_1.formatAnchorConnectValidateData)(targetRuleResult));
|
|
314
|
+
}
|
|
315
|
+
// 读取缓存的校验结果
|
|
316
|
+
var isSourcePass = ((_a = this.sourceRuleResults.get(targetInfoId)) !== null && _a !== void 0 ? _a : {}).isAllPass;
|
|
317
|
+
var isTargetPass = ((_b = this.targetRuleResults.get(targetInfoId)) !== null && _b !== void 0 ? _b : {}).isAllPass;
|
|
318
|
+
// 两侧都通过则允许连接,否则标记为不允许连接
|
|
319
|
+
if (isSourcePass && isTargetPass) {
|
|
320
|
+
targetNode.setElementState(constant_1.ElementState.ALLOW_CONNECT);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
targetNode.setElementState(constant_1.ElementState.NOT_ALLOW_CONNECT);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
317
326
|
Anchor.prototype.isShowLine = function () {
|
|
318
327
|
var _a = this.state, startX = _a.startX, startY = _a.startY, endX = _a.endX, endY = _a.endY;
|
|
319
328
|
var v = (0, util_1.distance)(startX, startY, endX, endY);
|
package/package.json
CHANGED
|
@@ -126,6 +126,7 @@ export interface IEditConfigType {
|
|
|
126
126
|
// 开启网格对齐
|
|
127
127
|
snapGrid: boolean
|
|
128
128
|
isPinching: boolean
|
|
129
|
+
anchorProximityValidate: boolean
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
export type IConfigKeys = keyof IEditConfigType
|
|
@@ -188,6 +189,7 @@ const allKeys = [
|
|
|
188
189
|
'nodeTextVertical', // 节点文本是否纵向显示
|
|
189
190
|
'edgeTextVertical', // 边文本是否纵向显示
|
|
190
191
|
'isPinching', //是否是双指捏合态
|
|
192
|
+
'anchorProximityValidate', // 仅在靠近锚点时触发连接校验
|
|
191
193
|
] as const
|
|
192
194
|
|
|
193
195
|
/**
|
|
@@ -223,6 +225,7 @@ export class EditConfigModel {
|
|
|
223
225
|
@observable edgeTextDraggable = false
|
|
224
226
|
@observable edgeTextMultiple = false // 是否支持多个边文本
|
|
225
227
|
@observable edgeTextVertical = false // 边文本朝向是否是纵向
|
|
228
|
+
@observable anchorProximityValidate = false // 仅在靠近锚点时触发连接校验
|
|
226
229
|
/*********************************************************
|
|
227
230
|
* 节点相关配置
|
|
228
231
|
********************************************************/
|
package/src/model/GraphModel.ts
CHANGED
|
@@ -89,6 +89,8 @@ export class GraphModel {
|
|
|
89
89
|
idGenerator?: (type?: string) => string | undefined
|
|
90
90
|
// 节点间连线、连线变更时的边的生成规则
|
|
91
91
|
edgeGenerator: LFOptions.Definition['edgeGenerator']
|
|
92
|
+
// 自定义目标锚点连接规则
|
|
93
|
+
customTargetAnchor?: LFOptions.Definition['customTargetAnchor']
|
|
92
94
|
|
|
93
95
|
// Remind:用于记录当前画布上所有节点和边的 model 的 Map
|
|
94
96
|
// 现在的处理方式,用 this.nodes.map 生成的方式,如果在 new Model 的过程中依赖于其它节点的 model,会出现找不到的情况
|
|
@@ -163,6 +165,7 @@ export class GraphModel {
|
|
|
163
165
|
edgeGenerator,
|
|
164
166
|
animation,
|
|
165
167
|
customTrajectory,
|
|
168
|
+
customTargetAnchor,
|
|
166
169
|
} = options
|
|
167
170
|
this.rootEl = container
|
|
168
171
|
this.partial = !!partial
|
|
@@ -216,6 +219,7 @@ export class GraphModel {
|
|
|
216
219
|
this.idGenerator = idGenerator
|
|
217
220
|
this.edgeGenerator = createEdgeGenerator(this, edgeGenerator)
|
|
218
221
|
this.customTrajectory = customTrajectory
|
|
222
|
+
this.customTargetAnchor = customTargetAnchor
|
|
219
223
|
}
|
|
220
224
|
|
|
221
225
|
@computed get nodesMap(): GraphModel.NodesMapType {
|
|
@@ -635,7 +635,10 @@ export class BaseNodeModel<P extends PropertiesType = PropertiesType>
|
|
|
635
635
|
* 手动连接边到节点时,需要连接的锚点
|
|
636
636
|
*/
|
|
637
637
|
public getTargetAnchor(position: Point): Model.AnchorInfo {
|
|
638
|
-
|
|
638
|
+
const { customTargetAnchor } = this.graphModel
|
|
639
|
+
return (
|
|
640
|
+
customTargetAnchor?.(this, position) ?? getClosestAnchor(position, this)
|
|
641
|
+
)
|
|
639
642
|
}
|
|
640
643
|
|
|
641
644
|
/**
|
package/src/options.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TransformModel } from './model'
|
|
1
|
+
import type { TransformModel, BaseNodeModel, Model } from './model'
|
|
2
2
|
|
|
3
3
|
import { assign } from 'lodash-es'
|
|
4
4
|
import { createElement as h } from 'preact/compat'
|
|
@@ -41,6 +41,10 @@ export namespace Options {
|
|
|
41
41
|
currentEdge?: Partial<LogicFlow.EdgeConfig>,
|
|
42
42
|
) => any
|
|
43
43
|
|
|
44
|
+
export type customTargetAnchorType = (
|
|
45
|
+
nodeModel: BaseNodeModel,
|
|
46
|
+
position: LogicFlow.Point,
|
|
47
|
+
) => Model.AnchorInfo | undefined
|
|
44
48
|
export interface CustomAnchorLineProps {
|
|
45
49
|
sourcePoint: LogicFlow.Point
|
|
46
50
|
targetPoint: LogicFlow.Point
|
|
@@ -104,6 +108,7 @@ export namespace Options {
|
|
|
104
108
|
idGenerator?: (type?: string) => string
|
|
105
109
|
edgeGenerator?: EdgeGeneratorType
|
|
106
110
|
|
|
111
|
+
customTargetAnchor?: customTargetAnchorType
|
|
107
112
|
customTrajectory?: (props: CustomAnchorLineProps) => h.JSX.Element
|
|
108
113
|
themeMode?: 'radius' | 'dark' | 'colorful' // 主题模式
|
|
109
114
|
|
package/src/util/drag.ts
CHANGED
|
@@ -103,7 +103,6 @@ export class StepDrag {
|
|
|
103
103
|
const DOC: any = window?.document
|
|
104
104
|
if (e.button !== LEFT_MOUSE_BUTTON_CODE) return
|
|
105
105
|
if (this.isStopPropagation) e.stopPropagation()
|
|
106
|
-
e.preventDefault()
|
|
107
106
|
this.isStartDragging = true
|
|
108
107
|
this.startX = e.clientX
|
|
109
108
|
this.startY = e.clientY
|
package/src/view/Anchor.tsx
CHANGED
|
@@ -314,41 +314,21 @@ class Anchor extends Component<IProps, IState> {
|
|
|
314
314
|
return
|
|
315
315
|
}
|
|
316
316
|
this.preTargetNode = targetNode
|
|
317
|
-
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
317
|
+
const anchorDist = distance(endX, endY, info.anchor.x, info.anchor.y)
|
|
318
|
+
const validateDistance = 10
|
|
319
|
+
const { editConfigModel } = graphModel
|
|
320
|
+
if (
|
|
321
|
+
!editConfigModel.anchorProximityValidate ||
|
|
322
|
+
anchorDist <= validateDistance
|
|
323
|
+
) {
|
|
324
|
+
this.validateAndSetState(
|
|
324
325
|
targetNode,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
)
|
|
328
|
-
const targetRuleResult = targetNode.isAllowConnectedAsTarget(
|
|
326
|
+
anchorId,
|
|
327
|
+
info.anchor,
|
|
329
328
|
nodeModel,
|
|
330
329
|
anchorData,
|
|
331
|
-
targetAnchor,
|
|
332
|
-
)
|
|
333
|
-
this.sourceRuleResults.set(
|
|
334
|
-
targetInfoId,
|
|
335
|
-
formatAnchorConnectValidateData(sourceRuleResult),
|
|
336
|
-
)
|
|
337
|
-
this.targetRuleResults.set(
|
|
338
|
-
targetInfoId,
|
|
339
|
-
formatAnchorConnectValidateData(targetRuleResult),
|
|
340
330
|
)
|
|
341
331
|
}
|
|
342
|
-
const { isAllPass: isSourcePass } =
|
|
343
|
-
this.sourceRuleResults.get(targetInfoId) ?? {}
|
|
344
|
-
const { isAllPass: isTargetPass } =
|
|
345
|
-
this.targetRuleResults.get(targetInfoId) ?? {}
|
|
346
|
-
// 实时提示出即将链接的锚点
|
|
347
|
-
if (isSourcePass && isTargetPass) {
|
|
348
|
-
targetNode.setElementState(ElementState.ALLOW_CONNECT)
|
|
349
|
-
} else {
|
|
350
|
-
targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT)
|
|
351
|
-
}
|
|
352
332
|
// 人工触发进入目标节点事件,同步设置 hovered 以驱动锚点显隐和样式
|
|
353
333
|
if (!targetNode.isHovered) {
|
|
354
334
|
const nodeData = targetNode.getData()
|
|
@@ -379,6 +359,49 @@ class Anchor extends Component<IProps, IState> {
|
|
|
379
359
|
}
|
|
380
360
|
}
|
|
381
361
|
|
|
362
|
+
// 校验 source/target 连接规则并设置目标节点状态
|
|
363
|
+
validateAndSetState(
|
|
364
|
+
targetNode: BaseNodeModel,
|
|
365
|
+
anchorId: string | undefined,
|
|
366
|
+
targetAnchor: AnchorConfig,
|
|
367
|
+
nodeModel: BaseNodeModel,
|
|
368
|
+
anchorData: AnchorConfig,
|
|
369
|
+
) {
|
|
370
|
+
const targetInfoId = `${nodeModel.id}_${targetNode.id}_${anchorId}_${anchorData.id}`
|
|
371
|
+
if (!this.targetRuleResults.has(targetInfoId)) {
|
|
372
|
+
// 首次计算并缓存源/目标两侧的规则校验结果
|
|
373
|
+
const sourceRuleResult = nodeModel.isAllowConnectedAsSource(
|
|
374
|
+
targetNode,
|
|
375
|
+
anchorData,
|
|
376
|
+
targetAnchor,
|
|
377
|
+
)
|
|
378
|
+
const targetRuleResult = targetNode.isAllowConnectedAsTarget(
|
|
379
|
+
nodeModel,
|
|
380
|
+
anchorData,
|
|
381
|
+
targetAnchor,
|
|
382
|
+
)
|
|
383
|
+
this.sourceRuleResults.set(
|
|
384
|
+
targetInfoId,
|
|
385
|
+
formatAnchorConnectValidateData(sourceRuleResult),
|
|
386
|
+
)
|
|
387
|
+
this.targetRuleResults.set(
|
|
388
|
+
targetInfoId,
|
|
389
|
+
formatAnchorConnectValidateData(targetRuleResult),
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
// 读取缓存的校验结果
|
|
393
|
+
const { isAllPass: isSourcePass } =
|
|
394
|
+
this.sourceRuleResults.get(targetInfoId) ?? {}
|
|
395
|
+
const { isAllPass: isTargetPass } =
|
|
396
|
+
this.targetRuleResults.get(targetInfoId) ?? {}
|
|
397
|
+
// 两侧都通过则允许连接,否则标记为不允许连接
|
|
398
|
+
if (isSourcePass && isTargetPass) {
|
|
399
|
+
targetNode.setElementState(ElementState.ALLOW_CONNECT)
|
|
400
|
+
} else {
|
|
401
|
+
targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
382
405
|
isShowLine() {
|
|
383
406
|
const { startX, startY, endX, endY } = this.state
|
|
384
407
|
const v = distance(startX, startY, endX, endY)
|