@logicflow/extension 2.1.6 → 2.2.0-alpha.1

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.
Files changed (35) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/CHANGELOG.md +22 -0
  3. package/dist/index.min.js +1 -1
  4. package/dist/index.min.js.map +1 -1
  5. package/es/components/dnd-panel/index.js +6 -2
  6. package/es/components/mini-map/index.js +6 -6
  7. package/es/components/selection-select/index.js +18 -9
  8. package/es/insert-node-in-polyline/index.js +11 -35
  9. package/es/tools/label/Label.d.ts +2 -2
  10. package/es/tools/label/Label.js +1 -1
  11. package/es/tools/label/LabelOverlay.js +3 -3
  12. package/es/tools/label/mediumEditor.d.ts +1 -1
  13. package/es/tools/label/mediumEditor.js +89 -52
  14. package/es/tools/snapshot/index.js +26 -13
  15. package/lib/components/dnd-panel/index.js +6 -2
  16. package/lib/components/mini-map/index.js +6 -6
  17. package/lib/components/selection-select/index.js +18 -9
  18. package/lib/insert-node-in-polyline/index.js +10 -34
  19. package/lib/tools/label/Label.d.ts +2 -2
  20. package/lib/tools/label/Label.js +1 -1
  21. package/lib/tools/label/LabelOverlay.js +2 -2
  22. package/lib/tools/label/mediumEditor.d.ts +1 -1
  23. package/lib/tools/label/mediumEditor.js +91 -53
  24. package/lib/tools/snapshot/index.js +26 -13
  25. package/package.json +5 -5
  26. package/src/components/dnd-panel/index.ts +10 -2
  27. package/src/components/menu/index.ts +0 -1
  28. package/src/components/mini-map/index.ts +8 -8
  29. package/src/components/selection-select/index.ts +22 -14
  30. package/src/insert-node-in-polyline/index.ts +17 -11
  31. package/src/tools/label/Label.tsx +4 -4
  32. package/src/tools/label/LabelOverlay.tsx +7 -3
  33. package/src/tools/label/mediumEditor.ts +78 -51
  34. package/src/tools/snapshot/index.ts +12 -0
  35. package/stats.html +1 -1
@@ -1,29 +1,4 @@
1
1
  "use strict";
2
- var __read = (this && this.__read) || function (o, n) {
3
- var m = typeof Symbol === "function" && o[Symbol.iterator];
4
- if (!m) return o;
5
- var i = m.call(o), r, ar = [], e;
6
- try {
7
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
- }
9
- catch (error) { e = { error: error }; }
10
- finally {
11
- try {
12
- if (r && !r.done && (m = i["return"])) m.call(i);
13
- }
14
- finally { if (e) throw e.error; }
15
- }
16
- return ar;
17
- };
18
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
19
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
20
- if (ar || !(i in from)) {
21
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
22
- ar[i] = from[i];
23
- }
24
- }
25
- return to.concat(ar || Array.prototype.slice.call(from));
26
- };
27
2
  Object.defineProperty(exports, "__esModule", { value: true });
28
3
  exports.InsertNodeInPolyline = void 0;
29
4
  var core_1 = require("@logicflow/core");
@@ -116,28 +91,29 @@ var InsertNodeInPolyline = /** @class */ (function () {
116
91
  var _b = edges[i], sourceNodeId = _b.sourceNodeId, targetNodeId = _b.targetNodeId, id = _b.id, type = _b.type, pointsList = _b.pointsList, sourceAnchorId = _b.sourceAnchorId, targetAnchorId = _b.targetAnchorId;
117
92
  // fix https://github.com/didi/LogicFlow/issues/996
118
93
  var startPoint = (0, lodash_es_1.cloneDeep)(pointsList[0]);
119
- var endPoint = (0, lodash_es_1.cloneDeep)(crossPoints.startCrossPoint);
120
94
  this._lf.deleteEdge(id);
121
95
  var checkResult = this.checkRuleBeforeInsetNode(sourceNodeId, targetNodeId, sourceAnchorId, targetAnchorId, nodeData);
96
+ // 基于插入节点的进入交点计算出最近的“进入锚点”,用于重连原边的前半段
97
+ var entryAnchorInfo = (0, core_1.getClosestAnchor)(crossPoints.startCrossPoint, nodeModel);
98
+ var entryAnchor = entryAnchorInfo.anchor;
99
+ // 构造第一条边:原 source → 插入节点(终点为进入锚点)
122
100
  this._lf.addEdge({
123
101
  type: type,
124
102
  sourceNodeId: sourceNodeId,
125
103
  targetNodeId: nodeData.id,
126
104
  startPoint: startPoint,
127
- endPoint: endPoint,
128
- pointsList: __spreadArray(__spreadArray([], __read(pointsList.slice(0, crossIndex)), false), [
129
- crossPoints.startCrossPoint,
130
- ], false),
105
+ endPoint: entryAnchor,
131
106
  });
107
+ // 基于插入节点的离开交点计算出最近的“离开锚点”,用于重连原边的后半段
108
+ var exitAnchorInfo = (0, core_1.getClosestAnchor)(crossPoints.endCrossPoint, nodeModel);
109
+ var exitAnchor = exitAnchorInfo.anchor;
110
+ // 构造第二条边:插入节点 → 原 target(起点为离开锚点)
132
111
  this._lf.addEdge({
133
112
  type: type,
134
113
  sourceNodeId: nodeData.id,
135
114
  targetNodeId: targetNodeId,
136
- startPoint: (0, lodash_es_1.cloneDeep)(crossPoints.endCrossPoint),
115
+ startPoint: (0, lodash_es_1.cloneDeep)(exitAnchor),
137
116
  endPoint: (0, lodash_es_1.cloneDeep)(pointsList[pointsList.length - 1]),
138
- pointsList: __spreadArray([
139
- crossPoints.endCrossPoint
140
- ], __read(pointsList.slice(crossIndex)), false),
141
117
  });
142
118
  if (!checkResult.isPass) {
143
119
  this._lf.graphModel.eventCenter.emit(core_1.EventType.CONNECTION_NOT_ALLOWED, {
@@ -17,8 +17,8 @@ export declare class Label extends Component<ILabelProps, ILabelState> {
17
17
  constructor(props: ILabelProps);
18
18
  setHoverOn: () => void;
19
19
  setHoverOff: () => void;
20
- handleMouseDown: (e: MouseEvent) => void;
21
- handleMouseUp: (e: MouseEvent) => void;
20
+ handleMouseDown: (e: PointerEvent) => void;
21
+ handleMouseUp: (e: PointerEvent) => void;
22
22
  handleDragging: ({ deltaX, deltaY }: IDragParams) => void;
23
23
  handleDragEnd: () => void;
24
24
  handleClick: (e: MouseEvent) => void;
@@ -280,7 +280,7 @@ var Label = /** @class */ (function (_super) {
280
280
  ? "".concat(transform, " rotate(").concat(rotate, "deg)")
281
281
  : "".concat(transform, " rotate(").concat(vertical ? -0.25 : 0, "turn)"),
282
282
  };
283
- return ((0, jsx_runtime_1.jsx)("div", { id: "element-container-".concat(id), className: (0, classnames_1.default)('lf-label-editor-container'), style: containerStyle, onMouseDown: this.handleMouseDown, onMouseUp: this.handleMouseUp, onClick: this.handleClick, onDblClick: this.handleDbClick, onBlur: this.handleBlur, onMouseEnter: this.setHoverOn, onMouseOver: this.setHoverOn, onMouseLeave: this.setHoverOff, children: (0, jsx_runtime_1.jsx)("div", { ref: this.textRef, id: "editor-container-".concat(id), className: (0, classnames_1.default)('lf-label-editor', (_a = {
283
+ return ((0, jsx_runtime_1.jsx)("div", { id: "element-container-".concat(id), className: (0, classnames_1.default)('lf-label-editor-container'), style: containerStyle, onPointerDown: this.handleMouseDown, onPointerUp: this.handleMouseUp, onClick: this.handleClick, onDblClick: this.handleDbClick, onBlur: this.handleBlur, onMouseEnter: this.setHoverOn, onMouseOver: this.setHoverOn, onMouseLeave: this.setHoverOff, children: (0, jsx_runtime_1.jsx)("div", { ref: this.textRef, id: "editor-container-".concat(id), className: (0, classnames_1.default)('lf-label-editor', (_a = {
284
284
  'lf-label-editor-dragging': isDragging,
285
285
  'lf-label-editor-editing': isEditing,
286
286
  'lf-label-editor-hover': !isEditing && (isHovered || isSelected)
@@ -80,7 +80,7 @@ var LabelOverlay = /** @class */ (function (_super) {
80
80
  this.editor = new mediumEditor_1.MediumEditor('.lf-label-editor', (0, lodash_es_1.merge)(mediumEditor_1.defaultOptions, {
81
81
  autoLink: true,
82
82
  extensions: {
83
- colorPicker: new mediumEditor_1.ColorPickerButton(),
83
+ colorPicker: new ((0, mediumEditor_1.createColorPickerButtonClass)(mediumEditor_1.MediumEditor))(),
84
84
  },
85
85
  }));
86
86
  // TODO: 2. 在此处监听一些事件,当 node、edge 数据变化时,主动触发重新渲染,保证同步更新
@@ -105,7 +105,7 @@ var LabelOverlay = /** @class */ (function (_super) {
105
105
  this.editor = new mediumEditor_1.MediumEditor('.lf-label-editor', (0, lodash_es_1.merge)(mediumEditor_1.defaultOptions, {
106
106
  autoLink: true,
107
107
  extensions: {
108
- colorPicker: new mediumEditor_1.ColorPickerButton(),
108
+ colorPicker: new ((0, mediumEditor_1.createColorPickerButtonClass)(mediumEditor_1.MediumEditor))(),
109
109
  },
110
110
  }));
111
111
  }
@@ -13,5 +13,5 @@ export declare const defaultOptions: {
13
13
  };
14
14
  disableEditing: boolean;
15
15
  };
16
- export declare const ColorPickerButton: any;
16
+ export declare function createColorPickerButtonClass(MediumEditor?: any): any;
17
17
  export { MediumEditor };
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.MediumEditor = exports.ColorPickerButton = exports.defaultOptions = void 0;
6
+ exports.MediumEditor = exports.createColorPickerButtonClass = exports.defaultOptions = void 0;
7
7
  var medium_editor_1 = __importDefault(require("medium-editor"));
8
8
  exports.MediumEditor = medium_editor_1.default;
9
9
  var vanilla_picker_1 = __importDefault(require("vanilla-picker"));
@@ -43,55 +43,93 @@ exports.defaultOptions = {
43
43
  },
44
44
  disableEditing: true,
45
45
  };
46
- exports.ColorPickerButton = medium_editor_1.default.extensions.button.extend({
47
- name: 'colorpicker',
48
- tagNames: ['mark'],
49
- contentDefault: '<b>Color</b>',
50
- aria: 'Color Picker',
51
- action: 'colorPicker',
52
- init: function () {
53
- var _this = this;
54
- rangy_1.default.init();
55
- medium_editor_1.default.extensions.button.prototype.init.call(this);
56
- this.colorPicker = new vanilla_picker_1.default({
57
- parent: this.button,
58
- color: '#000',
59
- onDone: function (res) {
60
- if (_this.coloredText && _this.coloredText.isAppliedToSelection()) {
61
- _this.coloredText.undoToSelection();
62
- }
63
- _this.coloredText = rangy_1.default.createClassApplier('colored', {
64
- elementTagName: 'span',
65
- elementProperties: {
66
- style: {
67
- color: res.hex,
68
- },
69
- },
70
- normalize: true,
71
- });
72
- _this.coloredText.toggleSelection();
73
- _this.base.checkContentChanged();
74
- _this.setInactive();
75
- },
76
- });
77
- },
78
- getButton: function () {
79
- return this.button;
80
- },
81
- handleClick: function () {
82
- this.setActive();
83
- this.colorPicker.show();
84
- },
85
- isAlreadyApplied: function (node) {
86
- return node.nodeName.toLowerCase() === 'mark';
87
- },
88
- isActive: function () {
89
- return this.button.classList.contains('medium-editor-button-active');
90
- },
91
- setInactive: function () {
92
- this.button.classList.remove('medium-editor-button-active');
93
- },
94
- setActive: function () {
95
- this.button.classList.add('medium-editor-button-active');
96
- },
97
- });
46
+ function createColorPickerButtonClass(MediumEditor) {
47
+ var _a, _b;
48
+ var ButtonBase = ((_a = MediumEditor === null || MediumEditor === void 0 ? void 0 : MediumEditor.extensions) === null || _a === void 0 ? void 0 : _a.button) || ((_b = MediumEditor === null || MediumEditor === void 0 ? void 0 : MediumEditor.extensions) === null || _b === void 0 ? void 0 : _b.button);
49
+ var ExtensionBase = (MediumEditor === null || MediumEditor === void 0 ? void 0 : MediumEditor.Extension) || (MediumEditor === null || MediumEditor === void 0 ? void 0 : MediumEditor.Extension);
50
+ // Button 扩展基类不可用时,回退到 Extension 基类,避免在模块加载阶段抛错
51
+ var Base = ButtonBase || ExtensionBase;
52
+ if (!Base) {
53
+ console.warn('MediumEditor button/extension base not available; using noop extension');
54
+ return /** @class */ (function () {
55
+ function class_1() {
56
+ }
57
+ return class_1;
58
+ }());
59
+ }
60
+ return Base.extend({
61
+ name: 'colorpicker',
62
+ tagNames: ['mark'],
63
+ contentDefault: '<b>Color</b>',
64
+ aria: 'Color Picker',
65
+ action: 'colorPicker',
66
+ init: function () {
67
+ var _this = this;
68
+ var _a, _b;
69
+ try {
70
+ rangy_1.default.init();
71
+ }
72
+ catch (_c) {
73
+ console.error('rangy.init failed');
74
+ }
75
+ // 初始化按钮(ButtonBase 才有 prototype.init)
76
+ try {
77
+ ;
78
+ (_b = (_a = ButtonBase === null || ButtonBase === void 0 ? void 0 : ButtonBase.prototype) === null || _a === void 0 ? void 0 : _a.init) === null || _b === void 0 ? void 0 : _b.call(this);
79
+ }
80
+ catch (_d) {
81
+ console.error('ButtonBase.init failed');
82
+ }
83
+ this.colorPicker = new vanilla_picker_1.default({
84
+ parent: this.button || undefined,
85
+ color: '#000',
86
+ onDone: function (res) {
87
+ var _a, _b, _c, _d, _e;
88
+ try {
89
+ if (_this.coloredText && ((_b = (_a = _this.coloredText).isAppliedToSelection) === null || _b === void 0 ? void 0 : _b.call(_a))) {
90
+ _this.coloredText.undoToSelection();
91
+ }
92
+ _this.coloredText = rangy_1.default.createClassApplier('colored', {
93
+ elementTagName: 'span',
94
+ elementProperties: { style: { color: res.hex } },
95
+ normalize: true,
96
+ });
97
+ _this.coloredText.toggleSelection();
98
+ (_d = (_c = _this.base) === null || _c === void 0 ? void 0 : _c.checkContentChanged) === null || _d === void 0 ? void 0 : _d.call(_c);
99
+ (_e = _this.setInactive) === null || _e === void 0 ? void 0 : _e.call(_this);
100
+ }
101
+ catch (_f) {
102
+ console.error('Picker.onDone failed');
103
+ }
104
+ },
105
+ });
106
+ },
107
+ getButton: function () {
108
+ return this.button;
109
+ },
110
+ handleClick: function () {
111
+ var _a, _b, _c;
112
+ (_a = this.setActive) === null || _a === void 0 ? void 0 : _a.call(this);
113
+ (_c = (_b = this.colorPicker) === null || _b === void 0 ? void 0 : _b.show) === null || _c === void 0 ? void 0 : _c.call(_b);
114
+ },
115
+ isAlreadyApplied: function (node) {
116
+ var _a, _b;
117
+ return ((_b = (_a = node === null || node === void 0 ? void 0 : node.nodeName) === null || _a === void 0 ? void 0 : _a.toLowerCase) === null || _b === void 0 ? void 0 : _b.call(_a)) === 'mark';
118
+ },
119
+ isActive: function () {
120
+ var _a, _b;
121
+ return (_b = (_a = this.button) === null || _a === void 0 ? void 0 : _a.classList) === null || _b === void 0 ? void 0 : _b.contains('medium-editor-button-active');
122
+ },
123
+ setInactive: function () {
124
+ var _a, _b;
125
+ ;
126
+ (_b = (_a = this.button) === null || _a === void 0 ? void 0 : _a.classList) === null || _b === void 0 ? void 0 : _b.remove('medium-editor-button-active');
127
+ },
128
+ setActive: function () {
129
+ var _a, _b;
130
+ ;
131
+ (_b = (_a = this.button) === null || _a === void 0 ? void 0 : _a.classList) === null || _b === void 0 ? void 0 : _b.add('medium-editor-button-active');
132
+ },
133
+ });
134
+ }
135
+ exports.createColorPickerButtonClass = createColorPickerButtonClass;
@@ -669,7 +669,7 @@ var Snapshot = /** @class */ (function () {
669
669
  // 内部方法处理blob转换
670
670
  Snapshot.prototype.snapshotBlob = function (toImageOptions, baseFileType, backgroundColor) {
671
671
  return __awaiter(this, void 0, void 0, function () {
672
- var _a, fileType, svg;
672
+ var _a, fileType, svg, copy, svgString, blob;
673
673
  var _this = this;
674
674
  return __generator(this, function (_b) {
675
675
  switch (_b.label) {
@@ -679,18 +679,31 @@ var Snapshot = /** @class */ (function () {
679
679
  return [4 /*yield*/, (0, utils_1.updateImageSource)(svg)];
680
680
  case 1:
681
681
  _b.sent();
682
- return [2 /*return*/, new Promise(function (resolve) {
683
- _this.getCanvasData(svg, __assign({ backgroundColor: backgroundColor }, (toImageOptions !== null && toImageOptions !== void 0 ? toImageOptions : {}))).then(function (canvas) {
684
- canvas.toBlob(function (blob) {
685
- // 输出图片数据以及图片宽高
686
- resolve({
687
- data: blob,
688
- width: canvas.width,
689
- height: canvas.height,
690
- });
691
- }, "image/".concat(fileType !== null && fileType !== void 0 ? fileType : 'png'));
692
- });
693
- })];
682
+ if (!(fileType === 'svg')) return [3 /*break*/, 3];
683
+ return [4 /*yield*/, this.cloneSvg(svg)];
684
+ case 2:
685
+ copy = _b.sent();
686
+ svgString = new XMLSerializer().serializeToString(copy);
687
+ blob = new Blob([svgString], {
688
+ type: 'image/svg+xml;charset=utf-8',
689
+ });
690
+ return [2 /*return*/, {
691
+ data: blob,
692
+ width: 0,
693
+ height: 0,
694
+ }];
695
+ case 3: return [2 /*return*/, new Promise(function (resolve) {
696
+ _this.getCanvasData(svg, __assign({ backgroundColor: backgroundColor }, (toImageOptions !== null && toImageOptions !== void 0 ? toImageOptions : {}))).then(function (canvas) {
697
+ canvas.toBlob(function (blob) {
698
+ // 输出图片数据以及图片宽高
699
+ resolve({
700
+ data: blob,
701
+ width: canvas.width,
702
+ height: canvas.height,
703
+ });
704
+ }, "image/".concat(fileType !== null && fileType !== void 0 ? fileType : 'png'));
705
+ });
706
+ })];
694
707
  }
695
708
  });
696
709
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logicflow/extension",
3
- "version": "2.1.6",
3
+ "version": "2.2.0-alpha.1",
4
4
  "description": "LogicFlow Extensions",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -20,8 +20,8 @@
20
20
  "author": "Logicflow-Team",
21
21
  "license": "Apache-2.0",
22
22
  "peerDependencies": {
23
- "@logicflow/core": "2.1.4",
24
- "@logicflow/vue-node-registry": "1.1.5"
23
+ "@logicflow/vue-node-registry": "1.2.0-alpha.1",
24
+ "@logicflow/core": "2.2.0-alpha.1"
25
25
  },
26
26
  "dependencies": {
27
27
  "@antv/hierarchy": "^0.6.11",
@@ -32,8 +32,8 @@
32
32
  "preact": "^10.17.1",
33
33
  "rangy": "^1.3.1",
34
34
  "vanilla-picker": "^2.12.3",
35
- "@logicflow/core": "2.1.4",
36
- "@logicflow/vue-node-registry": "1.1.5"
35
+ "@logicflow/core": "2.2.0-alpha.1",
36
+ "@logicflow/vue-node-registry": "1.2.0-alpha.1"
37
37
  },
38
38
  "devDependencies": {
39
39
  "less": "^4.1.1",
@@ -36,6 +36,13 @@ export class DndPanel {
36
36
  }
37
37
  this.panelEl = document.createElement('div')
38
38
  this.panelEl.className = 'lf-dndpanel'
39
+ this.panelEl.addEventListener(
40
+ 'touchmove',
41
+ (e) => {
42
+ e.preventDefault()
43
+ },
44
+ { passive: false },
45
+ )
39
46
  this.shapeList.forEach((shapeItem) => {
40
47
  this.panelEl?.appendChild(this.createDndItem(shapeItem))
41
48
  })
@@ -85,14 +92,14 @@ export class DndPanel {
85
92
  if (shapeItem.disabled) {
86
93
  el.classList.add('disabled')
87
94
  // 保留callback的执行,可用于界面提示当前shapeItem的禁用状态
88
- el.onmousedown = () => {
95
+ el.onpointerdown = () => {
89
96
  if (shapeItem.callback) {
90
97
  shapeItem.callback(this.lf, this.domContainer)
91
98
  }
92
99
  }
93
100
  return el
94
101
  }
95
- el.onmousedown = () => {
102
+ el.onpointerdown = (e: PointerEvent) => {
96
103
  if (shapeItem.type) {
97
104
  this.lf.dnd.startDrag({
98
105
  type: shapeItem.type,
@@ -103,6 +110,7 @@ export class DndPanel {
103
110
  if (shapeItem.callback) {
104
111
  shapeItem.callback(this.lf, this.domContainer)
105
112
  }
113
+ e.preventDefault()
106
114
  }
107
115
  el.ondblclick = (e) => {
108
116
  this.lf.graphModel.eventCenter.emit('dnd:panel-dbclick', {
@@ -603,7 +603,6 @@ class Menu {
603
603
  if (lf.graphModel.editConfigModel.isSilentMode) return
604
604
  this.__container = container
605
605
  this.__currentData = null // 当前展示的菜单所属元素的model数据
606
-
607
606
  // 监听 isSilentMode 变化
608
607
  this.setupSilentModeListener()
609
608
 
@@ -616,8 +616,8 @@ export class MiniMap {
616
616
  const div = document.createElement('div')
617
617
  div.className = 'lf-minimap-viewport'
618
618
 
619
- // 拖拽预览视窗,主画布视口跟随移动
620
- div.addEventListener('mousedown', this.startDrag)
619
+ div.style.touchAction = 'none'
620
+ div.addEventListener('pointerdown', this.startDrag)
621
621
 
622
622
  // 禁止预览视窗的点击事件冒泡
623
623
  div.addEventListener('click', (e: MouseEvent) => {
@@ -626,9 +626,9 @@ export class MiniMap {
626
626
  this.viewport = div
627
627
  }
628
628
 
629
- private startDrag = (e: MouseEvent) => {
630
- document.addEventListener('mousemove', this.drag)
631
- document.addEventListener('mouseup', this.drop)
629
+ private startDrag = (e: PointerEvent) => {
630
+ document.addEventListener('pointermove', this.drag)
631
+ document.addEventListener('pointerup', this.drop)
632
632
  const { x, y } = e
633
633
  this.startPosition = { x, y }
634
634
  }
@@ -636,7 +636,7 @@ export class MiniMap {
636
636
  /**
637
637
  * 拖拽预览视窗过程中,更新主画布视口
638
638
  */
639
- private drag = (e: MouseEvent) => {
639
+ private drag = (e: PointerEvent) => {
640
640
  const { x, y } = e
641
641
  const translateX = (x - this.startPosition.x) / this.scale
642
642
  const translateY = (y - this.startPosition.y) / this.scale
@@ -659,8 +659,8 @@ export class MiniMap {
659
659
  * 拖拽预览视窗结束,移除拖拽事件
660
660
  */
661
661
  private drop = () => {
662
- document.removeEventListener('mousemove', this.drag)
663
- document.removeEventListener('mouseup', this.drop)
662
+ document.removeEventListener('pointermove', this.drag)
663
+ document.removeEventListener('pointerup', this.drop)
664
664
  }
665
665
 
666
666
  /**
@@ -76,8 +76,8 @@ export class SelectionSelect {
76
76
  this.mouseDownInfo = null
77
77
 
78
78
  // 移除事件监听
79
- document.removeEventListener('mousemove', this.draw)
80
- document.removeEventListener('mouseup', this.drawOff)
79
+ document.removeEventListener('pointermove', this.draw)
80
+ document.removeEventListener('pointerup', this.drawOff)
81
81
  }
82
82
 
83
83
  /**
@@ -101,7 +101,8 @@ export class SelectionSelect {
101
101
  if (this.exclusiveMode) {
102
102
  // 独占模式:监听 container 的 mousedown 事件
103
103
  this.container.style.pointerEvents = 'auto'
104
- this.container.addEventListener('mousedown', this.handleMouseDown)
104
+ this.container.style.touchAction = 'none'
105
+ this.container.addEventListener('pointerdown', this.handleMouseDown)
105
106
  } else {
106
107
  // 非独占模式:监听画布的 blank:mousedown 事件
107
108
  this.container.style.pointerEvents = 'none'
@@ -113,7 +114,7 @@ export class SelectionSelect {
113
114
  private removeEventListeners() {
114
115
  if (this.container) {
115
116
  this.container.style.pointerEvents = 'none'
116
- this.container.removeEventListener('mousedown', this.handleMouseDown)
117
+ this.container.removeEventListener('pointerdown', this.handleMouseDown)
117
118
  }
118
119
  // 移除 blank:mousedown 事件监听
119
120
  this.lf.off('blank:mousedown', this.handleBlankMouseDown)
@@ -122,15 +123,16 @@ export class SelectionSelect {
122
123
  /**
123
124
  * 处理画布空白处鼠标按下事件(非独占模式)
124
125
  */
125
- private handleBlankMouseDown = ({ e }: { e: MouseEvent }) => {
126
- this.handleMouseDown(e)
126
+ private handleBlankMouseDown = ({ e }: { e: MouseEvent | PointerEvent }) => {
127
+ this.handleMouseDown(e as PointerEvent)
127
128
  }
128
129
 
129
130
  /**
130
131
  * 处理鼠标按下事件
131
132
  */
132
- private handleMouseDown(e: MouseEvent) {
133
+ private handleMouseDown(e: PointerEvent) {
133
134
  if (!this.container || this.disabled) return
135
+ if (this.lf.graphModel.editConfigModel.isPinching) return
134
136
 
135
137
  // 禁用右键框选
136
138
  const isRightClick = e.button === 2
@@ -170,8 +172,8 @@ export class SelectionSelect {
170
172
  this.container?.appendChild(wrapper)
171
173
  this.wrapper = wrapper
172
174
 
173
- document.addEventListener('mousemove', this.draw)
174
- document.addEventListener('mouseup', this.drawOff)
175
+ document.addEventListener('pointermove', this.draw)
176
+ document.addEventListener('pointerup', this.drawOff)
175
177
  }
176
178
 
177
179
  /**
@@ -210,7 +212,7 @@ export class SelectionSelect {
210
212
  if (this.wrapper && this.startPoint && this.endPoint) {
211
213
  // 记录上一次的结束点,用于触发 mouseup 事件
212
214
  const lastEndPoint = cloneDeep(this.endPoint)
213
- const lastEvent = new MouseEvent('mouseup', {
215
+ const lastEvent = new PointerEvent('pointerup', {
214
216
  clientX: lastEndPoint.x,
215
217
  clientY: lastEndPoint.y,
216
218
  })
@@ -221,7 +223,13 @@ export class SelectionSelect {
221
223
  this.close()
222
224
  }
223
225
 
224
- private draw = (ev: MouseEvent) => {
226
+ private draw = (ev: PointerEvent) => {
227
+ if (this.lf.graphModel.editConfigModel.isPinching) {
228
+ this.lf.updateEditConfig({ stopMoveGraph: this.originalStopMoveGraph })
229
+ this.originStatusSaved = false
230
+ this.cleanupSelectionState()
231
+ return
232
+ }
225
233
  const {
226
234
  domOverlayPosition: { x: x1, y: y1 },
227
235
  } = this.lf.getPointByClient(ev.clientX, ev.clientY)
@@ -251,7 +259,7 @@ export class SelectionSelect {
251
259
  }
252
260
  }
253
261
  }
254
- private drawOff = (e: MouseEvent) => {
262
+ private drawOff = (e: PointerEvent) => {
255
263
  // 恢复原始的 stopMoveGraph 设置
256
264
  this.lf.updateEditConfig({
257
265
  stopMoveGraph: this.originalStopMoveGraph,
@@ -274,9 +282,9 @@ export class SelectionSelect {
274
282
 
275
283
  const curStartPoint = cloneDeep(this.startPoint)
276
284
  const curEndPoint = cloneDeep(this.endPoint)
277
- document.removeEventListener('mousemove', this.draw)
285
+ document.removeEventListener('pointermove', this.draw)
278
286
  if (!this.exclusiveMode) {
279
- document.removeEventListener('mouseup', this.drawOff)
287
+ document.removeEventListener('pointerup', this.drawOff)
280
288
  }
281
289
 
282
290
  if (curStartPoint && curEndPoint) {
@@ -3,6 +3,7 @@ import LogicFlow, {
3
3
  PolylineEdgeModel,
4
4
  EventType,
5
5
  formatAnchorConnectValidateData,
6
+ getClosestAnchor,
6
7
  } from '@logicflow/core'
7
8
  import { cloneDeep } from 'lodash-es'
8
9
  import { isNodeInSegment } from './edge'
@@ -132,7 +133,6 @@ export class InsertNodeInPolyline {
132
133
  } = edges[i]
133
134
  // fix https://github.com/didi/LogicFlow/issues/996
134
135
  const startPoint = cloneDeep(pointsList[0])
135
- const endPoint = cloneDeep(crossPoints.startCrossPoint)
136
136
  this._lf.deleteEdge(id)
137
137
  const checkResult = this.checkRuleBeforeInsetNode(
138
138
  sourceNodeId,
@@ -141,27 +141,33 @@ export class InsertNodeInPolyline {
141
141
  targetAnchorId!,
142
142
  nodeData,
143
143
  )
144
+ // 基于插入节点的进入交点计算出最近的“进入锚点”,用于重连原边的前半段
145
+ const entryAnchorInfo = getClosestAnchor(
146
+ crossPoints.startCrossPoint,
147
+ nodeModel,
148
+ )
149
+ const entryAnchor = entryAnchorInfo.anchor
150
+ // 构造第一条边:原 source → 插入节点(终点为进入锚点)
144
151
  this._lf.addEdge({
145
152
  type,
146
153
  sourceNodeId,
147
154
  targetNodeId: nodeData.id,
148
155
  startPoint,
149
- endPoint,
150
- pointsList: [
151
- ...pointsList.slice(0, crossIndex),
152
- crossPoints.startCrossPoint,
153
- ],
156
+ endPoint: entryAnchor,
154
157
  })
158
+ // 基于插入节点的离开交点计算出最近的“离开锚点”,用于重连原边的后半段
159
+ const exitAnchorInfo = getClosestAnchor(
160
+ crossPoints.endCrossPoint,
161
+ nodeModel,
162
+ )
163
+ const exitAnchor = exitAnchorInfo.anchor
164
+ // 构造第二条边:插入节点 → 原 target(起点为离开锚点)
155
165
  this._lf.addEdge({
156
166
  type,
157
167
  sourceNodeId: nodeData.id,
158
168
  targetNodeId,
159
- startPoint: cloneDeep(crossPoints.endCrossPoint),
169
+ startPoint: cloneDeep(exitAnchor),
160
170
  endPoint: cloneDeep(pointsList[pointsList.length - 1]),
161
- pointsList: [
162
- crossPoints.endCrossPoint,
163
- ...pointsList.slice(crossIndex),
164
- ],
165
171
  })
166
172
  if (!checkResult.isPass) {
167
173
  this._lf.graphModel.eventCenter.emit(
@@ -72,7 +72,7 @@ export class Label extends Component<ILabelProps, ILabelState> {
72
72
  element.setHovered(false)
73
73
  }
74
74
 
75
- handleMouseDown = (e: MouseEvent) => {
75
+ handleMouseDown = (e: PointerEvent) => {
76
76
  const { label, graphModel } = this.props
77
77
  const {
78
78
  editConfigModel: { nodeTextDraggable },
@@ -86,7 +86,7 @@ export class Label extends Component<ILabelProps, ILabelState> {
86
86
  this.stepDrag.handleMouseDown(e)
87
87
  }
88
88
  }
89
- handleMouseUp = (e: MouseEvent) => {
89
+ handleMouseUp = (e: PointerEvent) => {
90
90
  if (this.state.isDragging) {
91
91
  this.stepDrag.handleMouseUp(e)
92
92
  }
@@ -320,8 +320,8 @@ export class Label extends Component<ILabelProps, ILabelState> {
320
320
  id={`element-container-${id}`}
321
321
  className={classNames('lf-label-editor-container')}
322
322
  style={containerStyle}
323
- onMouseDown={this.handleMouseDown}
324
- onMouseUp={this.handleMouseUp}
323
+ onPointerDown={this.handleMouseDown}
324
+ onPointerUp={this.handleMouseUp}
325
325
  onClick={this.handleClick}
326
326
  onDblClick={this.handleDbClick}
327
327
  onBlur={this.handleBlur}