@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.
@@ -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);
@@ -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>;
@@ -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
- return getClosestAnchor(position, this);
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
@@ -23,7 +23,6 @@ var StepDrag = /** @class */ (function () {
23
23
  return;
24
24
  if (_this.isStopPropagation)
25
25
  e.stopPropagation();
26
- e.preventDefault();
27
26
  _this.isStartDragging = true;
28
27
  _this.startX = e.clientX;
29
28
  _this.startY = e.clientY;
@@ -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, _b;
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
- // 支持节点的每个锚点单独设置是否可连接,因此规则key去nodeId + anchorId作为唯一值
269
- var targetInfoId = "".concat(nodeModel.id, "_").concat(targetNode.id, "_").concat(anchorId, "_").concat(anchorData.id);
270
- // 查看鼠标是否进入过target,若有检验结果,表示进入过, 就不重复计算了。
271
- if (!this.targetRuleResults.has(targetInfoId)) {
272
- var targetAnchor = info.anchor;
273
- var sourceRuleResult = nodeModel.isAllowConnectedAsSource(targetNode, anchorData, targetAnchor);
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>;
@@ -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
- return (0, util_1.getClosestAnchor)(position, this);
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
@@ -26,7 +26,6 @@ var StepDrag = /** @class */ (function () {
26
26
  return;
27
27
  if (_this.isStopPropagation)
28
28
  e.stopPropagation();
29
- e.preventDefault();
30
29
  _this.isStartDragging = true;
31
30
  _this.startX = e.clientX;
32
31
  _this.startY = e.clientY;
@@ -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
  }
@@ -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, _b;
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
- // 支持节点的每个锚点单独设置是否可连接,因此规则key去nodeId + anchorId作为唯一值
271
- var targetInfoId = "".concat(nodeModel.id, "_").concat(targetNode.id, "_").concat(anchorId, "_").concat(anchorData.id);
272
- // 查看鼠标是否进入过target,若有检验结果,表示进入过, 就不重复计算了。
273
- if (!this.targetRuleResults.has(targetInfoId)) {
274
- var targetAnchor = info.anchor;
275
- var sourceRuleResult = nodeModel.isAllowConnectedAsSource(targetNode, anchorData, targetAnchor);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logicflow/core",
3
- "version": "2.2.0-alpha.0",
3
+ "version": "2.2.0-alpha.2",
4
4
  "description": "LogicFlow, help you quickly create flowcharts",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -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
  ********************************************************/
@@ -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
- return getClosestAnchor(position, this)
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
@@ -314,41 +314,21 @@ class Anchor extends Component<IProps, IState> {
314
314
  return
315
315
  }
316
316
  this.preTargetNode = targetNode
317
- // 支持节点的每个锚点单独设置是否可连接,因此规则key去nodeId + anchorId作为唯一值
318
- const targetInfoId = `${nodeModel.id}_${targetNode.id}_${anchorId}_${anchorData.id}`
319
-
320
- // 查看鼠标是否进入过target,若有检验结果,表示进入过, 就不重复计算了。
321
- if (!this.targetRuleResults.has(targetInfoId)) {
322
- const targetAnchor = info.anchor
323
- const sourceRuleResult = nodeModel.isAllowConnectedAsSource(
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
- anchorData,
326
- targetAnchor,
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)