@flowgram.ai/free-snap-plugin 0.1.0
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/dist/esm/index.js +1035 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +103 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +1070 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1070 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
30
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
31
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
32
|
+
if (decorator = decorators[i])
|
|
33
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
34
|
+
if (kind && result) __defProp(target, key, result);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/index.ts
|
|
39
|
+
var src_exports = {};
|
|
40
|
+
__export(src_exports, {
|
|
41
|
+
WorkflowSnapService: () => WorkflowSnapService,
|
|
42
|
+
createFreeSnapPlugin: () => createFreeSnapPlugin
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(src_exports);
|
|
45
|
+
|
|
46
|
+
// src/create-plugin.ts
|
|
47
|
+
var import_core3 = require("@flowgram.ai/core");
|
|
48
|
+
|
|
49
|
+
// src/service.ts
|
|
50
|
+
var import_inversify = require("inversify");
|
|
51
|
+
var import_document = require("@flowgram.ai/document");
|
|
52
|
+
var import_document2 = require("@flowgram.ai/document");
|
|
53
|
+
var import_core = require("@flowgram.ai/core");
|
|
54
|
+
var import_free_layout_core = require("@flowgram.ai/free-layout-core");
|
|
55
|
+
var import_free_layout_core2 = require("@flowgram.ai/free-layout-core");
|
|
56
|
+
var import_utils = require("@flowgram.ai/utils");
|
|
57
|
+
|
|
58
|
+
// src/constant.ts
|
|
59
|
+
var SnapDefaultOptions = {
|
|
60
|
+
enableEdgeSnapping: true,
|
|
61
|
+
edgeThreshold: 7,
|
|
62
|
+
enableGridSnapping: false,
|
|
63
|
+
gridSize: 20,
|
|
64
|
+
enableMultiSnapping: false,
|
|
65
|
+
enableOnlyViewportSnapping: true,
|
|
66
|
+
edgeColor: "#4E40E5",
|
|
67
|
+
alignColor: "#4E40E5",
|
|
68
|
+
edgeLineWidth: 2,
|
|
69
|
+
alignLineWidth: 2,
|
|
70
|
+
alignCrossWidth: 16
|
|
71
|
+
};
|
|
72
|
+
var Epsilon = 1e-5;
|
|
73
|
+
|
|
74
|
+
// src/utils.ts
|
|
75
|
+
var isEqual = (a, b) => {
|
|
76
|
+
if (a === void 0 || b === void 0) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return Math.abs(a - b) < Epsilon;
|
|
80
|
+
};
|
|
81
|
+
var isLessThan = (a, b) => {
|
|
82
|
+
if (a === void 0 || b === void 0) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return b - a > Epsilon;
|
|
86
|
+
};
|
|
87
|
+
var isGreaterThan = (a, b) => {
|
|
88
|
+
if (a === void 0 || b === void 0) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return a - b > Epsilon;
|
|
92
|
+
};
|
|
93
|
+
var isLessThanOrEqual = (a, b) => isEqual(a, b) || isLessThan(a, b);
|
|
94
|
+
var isNumber = (value) => typeof value === "number" && !isNaN(value);
|
|
95
|
+
|
|
96
|
+
// src/service.ts
|
|
97
|
+
var WorkflowSnapService = class {
|
|
98
|
+
constructor() {
|
|
99
|
+
this.disposers = [];
|
|
100
|
+
this.snapEmitter = new import_utils.Emitter();
|
|
101
|
+
this.onSnap = this.snapEmitter.event;
|
|
102
|
+
}
|
|
103
|
+
init(params = {}) {
|
|
104
|
+
this.options = {
|
|
105
|
+
...SnapDefaultOptions,
|
|
106
|
+
...params
|
|
107
|
+
};
|
|
108
|
+
this.mountListener();
|
|
109
|
+
}
|
|
110
|
+
dispose() {
|
|
111
|
+
this.disposers.forEach((disposer) => disposer.dispose());
|
|
112
|
+
}
|
|
113
|
+
mountListener() {
|
|
114
|
+
const dragAdjusterDisposer = this.dragService.registerPosAdjuster(
|
|
115
|
+
(params) => this.snapping({
|
|
116
|
+
targetNodes: params.selectedNodes,
|
|
117
|
+
position: params.position
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
const dragEndDisposer = this.dragService.onNodesDrag((event) => {
|
|
121
|
+
if (event.type !== "onDragEnd") {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (this.options.enableGridSnapping) {
|
|
125
|
+
this.gridSnapping({
|
|
126
|
+
targetNodes: event.nodes,
|
|
127
|
+
gridSize: this.options.gridSize
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (this.options.enableEdgeSnapping) {
|
|
131
|
+
this.clear();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
this.disposers.push(dragAdjusterDisposer, dragEndDisposer);
|
|
135
|
+
}
|
|
136
|
+
snapping(params) {
|
|
137
|
+
const { targetNodes, position } = params;
|
|
138
|
+
const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;
|
|
139
|
+
if (!this.options.enableEdgeSnapping || isMultiSnapping) {
|
|
140
|
+
return {
|
|
141
|
+
x: 0,
|
|
142
|
+
y: 0
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const selectedBounds = this.getBounds(targetNodes);
|
|
146
|
+
const targetRect = new import_utils.Rectangle(
|
|
147
|
+
position.x,
|
|
148
|
+
position.y,
|
|
149
|
+
selectedBounds.width,
|
|
150
|
+
selectedBounds.height
|
|
151
|
+
);
|
|
152
|
+
const snapNodeRects = this.getSnapNodeRects({
|
|
153
|
+
targetNodes,
|
|
154
|
+
targetRect
|
|
155
|
+
});
|
|
156
|
+
const { alignOffset, alignRects, alignSpacing } = this.calcAlignOffset({
|
|
157
|
+
targetRect,
|
|
158
|
+
alignThreshold: this.options.edgeThreshold,
|
|
159
|
+
snapNodeRects
|
|
160
|
+
});
|
|
161
|
+
const { snapOffset, snapEdgeLines } = this.calcSnapOffset({
|
|
162
|
+
targetRect,
|
|
163
|
+
edgeThreshold: this.options.edgeThreshold,
|
|
164
|
+
snapNodeRects
|
|
165
|
+
});
|
|
166
|
+
const offset = {
|
|
167
|
+
x: snapOffset.x || alignOffset.x,
|
|
168
|
+
y: snapOffset.y || alignOffset.y
|
|
169
|
+
};
|
|
170
|
+
const snapRect = new import_utils.Rectangle(
|
|
171
|
+
position.x + offset.x,
|
|
172
|
+
position.y + offset.y,
|
|
173
|
+
targetRect.width,
|
|
174
|
+
targetRect.height
|
|
175
|
+
);
|
|
176
|
+
this.snapEmitter.fire({
|
|
177
|
+
snapRect,
|
|
178
|
+
snapEdgeLines,
|
|
179
|
+
alignRects,
|
|
180
|
+
alignSpacing
|
|
181
|
+
});
|
|
182
|
+
return offset;
|
|
183
|
+
}
|
|
184
|
+
calcSnapOffset(params) {
|
|
185
|
+
const { snapNodeRects, edgeThreshold, targetRect } = params;
|
|
186
|
+
const snapLines = this.getSnapLines({
|
|
187
|
+
snapNodeRects
|
|
188
|
+
});
|
|
189
|
+
const topYClosestLine = snapLines.horizontal.find(
|
|
190
|
+
(line) => isLessThanOrEqual(Math.abs(line.y - targetRect.top), edgeThreshold)
|
|
191
|
+
);
|
|
192
|
+
const bottomYClosestLine = snapLines.horizontal.find(
|
|
193
|
+
(line) => isLessThanOrEqual(Math.abs(line.y - targetRect.bottom), edgeThreshold)
|
|
194
|
+
);
|
|
195
|
+
const leftXClosestLine = snapLines.vertical.find(
|
|
196
|
+
(line) => isLessThanOrEqual(Math.abs(line.x - targetRect.left), edgeThreshold)
|
|
197
|
+
);
|
|
198
|
+
const rightXClosestLine = snapLines.vertical.find(
|
|
199
|
+
(line) => isLessThanOrEqual(Math.abs(line.x - targetRect.right), edgeThreshold)
|
|
200
|
+
);
|
|
201
|
+
const midYClosestLine = snapLines.midHorizontal.find(
|
|
202
|
+
(line) => isLessThanOrEqual(Math.abs(line.y - targetRect.center.y), edgeThreshold)
|
|
203
|
+
);
|
|
204
|
+
const midXClosestLine = snapLines.midVertical.find(
|
|
205
|
+
(line) => isLessThanOrEqual(Math.abs(line.x - targetRect.center.x), edgeThreshold)
|
|
206
|
+
);
|
|
207
|
+
const topYClosest = topYClosestLine?.y;
|
|
208
|
+
const bottomYClosest = isNumber(bottomYClosestLine?.y) ? bottomYClosestLine.y - targetRect.height : void 0;
|
|
209
|
+
const leftXClosest = leftXClosestLine?.x;
|
|
210
|
+
const rightXClosest = isNumber(rightXClosestLine?.x) ? rightXClosestLine.x - targetRect.width : void 0;
|
|
211
|
+
const midYClosest = isNumber(midYClosestLine?.y) ? midYClosestLine.y - targetRect.height / 2 : void 0;
|
|
212
|
+
const midXClosest = isNumber(midXClosestLine?.x) ? midXClosestLine.x - targetRect.width / 2 : void 0;
|
|
213
|
+
const snappingPosition = {
|
|
214
|
+
x: midXClosest ?? leftXClosest ?? rightXClosest ?? targetRect.x,
|
|
215
|
+
y: midYClosest ?? topYClosest ?? bottomYClosest ?? targetRect.y
|
|
216
|
+
};
|
|
217
|
+
const snapOffset = {
|
|
218
|
+
x: snappingPosition.x - targetRect.x,
|
|
219
|
+
y: snappingPosition.y - targetRect.y
|
|
220
|
+
};
|
|
221
|
+
const snapEdgeLines = {
|
|
222
|
+
top: isEqual(topYClosest, snappingPosition.y) ? topYClosestLine : void 0,
|
|
223
|
+
bottom: isEqual(bottomYClosest, snappingPosition.y) ? bottomYClosestLine : void 0,
|
|
224
|
+
left: isEqual(leftXClosest, snappingPosition.x) ? leftXClosestLine : void 0,
|
|
225
|
+
right: isEqual(rightXClosest, snappingPosition.x) ? rightXClosestLine : void 0,
|
|
226
|
+
midVertical: isEqual(midXClosest, snappingPosition.x) ? midXClosestLine : void 0,
|
|
227
|
+
midHorizontal: isEqual(midYClosest, snappingPosition.y) ? midYClosestLine : void 0
|
|
228
|
+
};
|
|
229
|
+
return { snapOffset, snapEdgeLines };
|
|
230
|
+
}
|
|
231
|
+
gridSnapping(params) {
|
|
232
|
+
const { gridSize, targetNodes } = params;
|
|
233
|
+
const rect = this.getBounds(targetNodes);
|
|
234
|
+
const snap = (value) => Math.round(value / gridSize) * gridSize;
|
|
235
|
+
const snappedPosition = {
|
|
236
|
+
x: snap(rect.x),
|
|
237
|
+
y: snap(rect.y)
|
|
238
|
+
};
|
|
239
|
+
const offset = {
|
|
240
|
+
x: snappedPosition.x - rect.x,
|
|
241
|
+
y: snappedPosition.y - rect.y
|
|
242
|
+
};
|
|
243
|
+
targetNodes.forEach(
|
|
244
|
+
(node) => this.updateNodePositionWithOffset({
|
|
245
|
+
node,
|
|
246
|
+
offset
|
|
247
|
+
})
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
clear() {
|
|
251
|
+
this.snapEmitter.fire({
|
|
252
|
+
snapEdgeLines: {},
|
|
253
|
+
snapRect: import_utils.Rectangle.EMPTY,
|
|
254
|
+
alignRects: {
|
|
255
|
+
top: [],
|
|
256
|
+
bottom: [],
|
|
257
|
+
left: [],
|
|
258
|
+
right: []
|
|
259
|
+
},
|
|
260
|
+
alignSpacing: {}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
getSnapLines(params) {
|
|
264
|
+
const { snapNodeRects } = params;
|
|
265
|
+
const horizontalLines = [];
|
|
266
|
+
const verticalLines = [];
|
|
267
|
+
const midHorizontalLines = [];
|
|
268
|
+
const midVerticalLines = [];
|
|
269
|
+
snapNodeRects.forEach((snapNodeRect) => {
|
|
270
|
+
const nodeBounds = snapNodeRect.rect;
|
|
271
|
+
const nodeCenter = nodeBounds.center;
|
|
272
|
+
const top = {
|
|
273
|
+
y: nodeBounds.top,
|
|
274
|
+
sourceNodeId: snapNodeRect.id
|
|
275
|
+
};
|
|
276
|
+
const bottom = {
|
|
277
|
+
y: nodeBounds.bottom,
|
|
278
|
+
sourceNodeId: snapNodeRect.id
|
|
279
|
+
};
|
|
280
|
+
const left = {
|
|
281
|
+
x: nodeBounds.left,
|
|
282
|
+
sourceNodeId: snapNodeRect.id
|
|
283
|
+
};
|
|
284
|
+
const right = {
|
|
285
|
+
x: nodeBounds.right,
|
|
286
|
+
sourceNodeId: snapNodeRect.id
|
|
287
|
+
};
|
|
288
|
+
const midHorizontal = {
|
|
289
|
+
y: nodeCenter.y,
|
|
290
|
+
sourceNodeId: snapNodeRect.id
|
|
291
|
+
};
|
|
292
|
+
const midVertical = {
|
|
293
|
+
x: nodeCenter.x,
|
|
294
|
+
sourceNodeId: snapNodeRect.id
|
|
295
|
+
};
|
|
296
|
+
horizontalLines.push(top, bottom);
|
|
297
|
+
verticalLines.push(left, right);
|
|
298
|
+
midHorizontalLines.push(midHorizontal);
|
|
299
|
+
midVerticalLines.push(midVertical);
|
|
300
|
+
});
|
|
301
|
+
return {
|
|
302
|
+
horizontal: horizontalLines,
|
|
303
|
+
vertical: verticalLines,
|
|
304
|
+
midHorizontal: midHorizontalLines,
|
|
305
|
+
midVertical: midVerticalLines
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
getAvailableNodes(params) {
|
|
309
|
+
const { targetNodes, targetRect } = params;
|
|
310
|
+
const targetCenter = targetRect.center;
|
|
311
|
+
const targetContainerId = targetNodes[0].parent?.id ?? this.document.root.id;
|
|
312
|
+
const disabledNodeIds = targetNodes.map((n) => n.id);
|
|
313
|
+
disabledNodeIds.push(import_document2.FlowNodeBaseType.ROOT);
|
|
314
|
+
const availableNodes = this.nodes.filter((n) => n.parent?.id === targetContainerId).filter((n) => !disabledNodeIds.includes(n.id)).sort((nodeA, nodeB) => {
|
|
315
|
+
const nodeCenterA = nodeA.getData(import_document.FlowNodeTransformData).bounds.center;
|
|
316
|
+
const nodeCenterB = nodeB.getData(import_document.FlowNodeTransformData).bounds.center;
|
|
317
|
+
const distanceA = Math.abs(nodeCenterA.x - targetCenter.x) + Math.abs(nodeCenterA.y - targetCenter.y);
|
|
318
|
+
const distanceB = Math.abs(nodeCenterB.x - targetCenter.x) + Math.abs(nodeCenterB.y - targetCenter.y);
|
|
319
|
+
return distanceA - distanceB;
|
|
320
|
+
});
|
|
321
|
+
return availableNodes;
|
|
322
|
+
}
|
|
323
|
+
viewRect() {
|
|
324
|
+
const { width, height, scrollX, scrollY, zoom } = this.playgroundConfig.config;
|
|
325
|
+
return new import_utils.Rectangle(scrollX / zoom, scrollY / zoom, width / zoom, height / zoom);
|
|
326
|
+
}
|
|
327
|
+
getSnapNodeRects(params) {
|
|
328
|
+
const availableNodes = this.getAvailableNodes(params);
|
|
329
|
+
const viewRect = this.viewRect();
|
|
330
|
+
return availableNodes.map((node) => {
|
|
331
|
+
const snapNodeRect = {
|
|
332
|
+
id: node.id,
|
|
333
|
+
rect: node.getData(import_document.FlowNodeTransformData).bounds,
|
|
334
|
+
entity: node
|
|
335
|
+
};
|
|
336
|
+
if (this.options.enableOnlyViewportSnapping && node.parent?.flowNodeType === import_document2.FlowNodeBaseType.ROOT && !import_utils.Rectangle.intersects(viewRect, snapNodeRect.rect)) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
return snapNodeRect;
|
|
340
|
+
}).filter(Boolean);
|
|
341
|
+
}
|
|
342
|
+
get nodes() {
|
|
343
|
+
return this.entityManager.getEntities(import_free_layout_core.WorkflowNodeEntity);
|
|
344
|
+
}
|
|
345
|
+
getBounds(nodes) {
|
|
346
|
+
if (nodes.length === 0) {
|
|
347
|
+
return import_utils.Rectangle.EMPTY;
|
|
348
|
+
}
|
|
349
|
+
return import_utils.Rectangle.enlarge(nodes.map((n) => n.getData(import_document.FlowNodeTransformData).bounds));
|
|
350
|
+
}
|
|
351
|
+
updateNodePositionWithOffset(params) {
|
|
352
|
+
const { node, offset } = params;
|
|
353
|
+
const transform = node.getData(import_core.TransformData);
|
|
354
|
+
const positionWithOffset = {
|
|
355
|
+
x: transform.position.x + offset.x,
|
|
356
|
+
y: transform.position.y + offset.y
|
|
357
|
+
};
|
|
358
|
+
if (node.collapsedChildren?.length > 0) {
|
|
359
|
+
node.collapsedChildren.forEach((childNode) => {
|
|
360
|
+
const childNodeTransformData = childNode.getData(import_document.FlowNodeTransformData);
|
|
361
|
+
childNodeTransformData.fireChange();
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
transform.update({
|
|
365
|
+
position: positionWithOffset
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
calcAlignOffset(params) {
|
|
369
|
+
const { snapNodeRects, targetRect, alignThreshold } = params;
|
|
370
|
+
const alignRects = this.getAlignRects({
|
|
371
|
+
targetRect,
|
|
372
|
+
snapNodeRects
|
|
373
|
+
});
|
|
374
|
+
const alignSpacing = this.calcAlignSpacing({
|
|
375
|
+
targetRect,
|
|
376
|
+
alignRects
|
|
377
|
+
});
|
|
378
|
+
let topY;
|
|
379
|
+
let bottomY;
|
|
380
|
+
let leftX;
|
|
381
|
+
let rightX;
|
|
382
|
+
let midY;
|
|
383
|
+
let midX;
|
|
384
|
+
if (alignSpacing.top) {
|
|
385
|
+
const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.top;
|
|
386
|
+
const isAlignTop = isLessThanOrEqual(Math.abs(targetRect.top - topAlignY), alignThreshold);
|
|
387
|
+
if (isAlignTop) {
|
|
388
|
+
topY = topAlignY;
|
|
389
|
+
} else {
|
|
390
|
+
alignSpacing.top = void 0;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (alignSpacing.bottom) {
|
|
394
|
+
const bottomAlignY = alignRects.bottom[0].rect.top - alignSpacing.bottom;
|
|
395
|
+
const isAlignBottom = isLessThan(Math.abs(targetRect.bottom - bottomAlignY), alignThreshold);
|
|
396
|
+
if (isAlignBottom) {
|
|
397
|
+
bottomY = bottomAlignY - targetRect.height;
|
|
398
|
+
} else {
|
|
399
|
+
alignSpacing.bottom = void 0;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (alignSpacing.left) {
|
|
403
|
+
const leftAlignX = alignRects.left[0].rect.right + alignSpacing.left;
|
|
404
|
+
const isAlignLeft = isLessThanOrEqual(Math.abs(targetRect.left - leftAlignX), alignThreshold);
|
|
405
|
+
if (isAlignLeft) {
|
|
406
|
+
leftX = leftAlignX;
|
|
407
|
+
} else {
|
|
408
|
+
alignSpacing.left = void 0;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (alignSpacing.right) {
|
|
412
|
+
const rightAlignX = alignRects.right[0].rect.left - alignSpacing.right;
|
|
413
|
+
const isAlignRight = isLessThanOrEqual(
|
|
414
|
+
Math.abs(targetRect.right - rightAlignX),
|
|
415
|
+
alignThreshold
|
|
416
|
+
);
|
|
417
|
+
if (isAlignRight) {
|
|
418
|
+
rightX = rightAlignX - targetRect.width;
|
|
419
|
+
} else {
|
|
420
|
+
alignSpacing.right = void 0;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (alignSpacing.midHorizontal) {
|
|
424
|
+
const leftAlignX = alignRects.left[0].rect.right + alignSpacing.midHorizontal;
|
|
425
|
+
const isAlignMidHorizontal = isLessThanOrEqual(
|
|
426
|
+
Math.abs(targetRect.left - leftAlignX),
|
|
427
|
+
alignThreshold
|
|
428
|
+
);
|
|
429
|
+
if (isAlignMidHorizontal) {
|
|
430
|
+
midX = leftAlignX;
|
|
431
|
+
} else {
|
|
432
|
+
alignSpacing.midHorizontal = void 0;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (alignSpacing.midVertical) {
|
|
436
|
+
const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.midVertical;
|
|
437
|
+
const isAlignMidVertical = isLessThanOrEqual(
|
|
438
|
+
Math.abs(targetRect.top - topAlignY),
|
|
439
|
+
alignThreshold
|
|
440
|
+
);
|
|
441
|
+
if (isAlignMidVertical) {
|
|
442
|
+
midY = topAlignY;
|
|
443
|
+
} else {
|
|
444
|
+
alignSpacing.midVertical = void 0;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const alignPosition = {
|
|
448
|
+
x: midX ?? leftX ?? rightX ?? targetRect.x,
|
|
449
|
+
y: midY ?? topY ?? bottomY ?? targetRect.y
|
|
450
|
+
};
|
|
451
|
+
const alignOffset = {
|
|
452
|
+
x: alignPosition.x - targetRect.x,
|
|
453
|
+
y: alignPosition.y - targetRect.y
|
|
454
|
+
};
|
|
455
|
+
return { alignOffset, alignRects, alignSpacing };
|
|
456
|
+
}
|
|
457
|
+
calcAlignSpacing(params) {
|
|
458
|
+
const { targetRect, alignRects } = params;
|
|
459
|
+
const topSpacing = this.getDirectionAlignSpacing({
|
|
460
|
+
rects: alignRects.top,
|
|
461
|
+
isHorizontal: false
|
|
462
|
+
});
|
|
463
|
+
const bottomSpacing = this.getDirectionAlignSpacing({
|
|
464
|
+
rects: alignRects.bottom,
|
|
465
|
+
isHorizontal: false
|
|
466
|
+
});
|
|
467
|
+
const leftSpacing = this.getDirectionAlignSpacing({
|
|
468
|
+
rects: alignRects.left,
|
|
469
|
+
isHorizontal: true
|
|
470
|
+
});
|
|
471
|
+
const rightSpacing = this.getDirectionAlignSpacing({
|
|
472
|
+
rects: alignRects.right,
|
|
473
|
+
isHorizontal: true
|
|
474
|
+
});
|
|
475
|
+
const midHorizontalSpacing = this.getMidAlignSpacing({
|
|
476
|
+
rectA: alignRects.left[0]?.rect,
|
|
477
|
+
rectB: alignRects.right[0]?.rect,
|
|
478
|
+
targetRect,
|
|
479
|
+
isHorizontal: true
|
|
480
|
+
});
|
|
481
|
+
const midVerticalSpacing = this.getMidAlignSpacing({
|
|
482
|
+
rectA: alignRects.top[0]?.rect,
|
|
483
|
+
rectB: alignRects.bottom[0]?.rect,
|
|
484
|
+
targetRect,
|
|
485
|
+
isHorizontal: false
|
|
486
|
+
});
|
|
487
|
+
return {
|
|
488
|
+
top: topSpacing,
|
|
489
|
+
bottom: bottomSpacing,
|
|
490
|
+
left: leftSpacing,
|
|
491
|
+
right: rightSpacing,
|
|
492
|
+
midHorizontal: midHorizontalSpacing,
|
|
493
|
+
midVertical: midVerticalSpacing
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
getAlignRects(params) {
|
|
497
|
+
const { targetRect, snapNodeRects } = params;
|
|
498
|
+
const topVerticalRects = [];
|
|
499
|
+
const bottomVerticalRects = [];
|
|
500
|
+
const leftHorizontalRects = [];
|
|
501
|
+
const rightHorizontalRects = [];
|
|
502
|
+
snapNodeRects.forEach((snapNodeRect) => {
|
|
503
|
+
const nodeRect = snapNodeRect.rect;
|
|
504
|
+
const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(nodeRect, targetRect);
|
|
505
|
+
if (isIntersection) {
|
|
506
|
+
return;
|
|
507
|
+
} else if (isVerticalIntersection) {
|
|
508
|
+
if (isGreaterThan(nodeRect.center.y, targetRect.center.y)) {
|
|
509
|
+
bottomVerticalRects.push({
|
|
510
|
+
rect: nodeRect,
|
|
511
|
+
sourceNodeId: snapNodeRect.id
|
|
512
|
+
});
|
|
513
|
+
} else {
|
|
514
|
+
topVerticalRects.push({
|
|
515
|
+
rect: nodeRect,
|
|
516
|
+
sourceNodeId: snapNodeRect.id
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
} else if (isHorizontalIntersection) {
|
|
520
|
+
if (isGreaterThan(nodeRect.center.x, targetRect.center.x)) {
|
|
521
|
+
rightHorizontalRects.push({
|
|
522
|
+
rect: nodeRect,
|
|
523
|
+
sourceNodeId: snapNodeRect.id
|
|
524
|
+
});
|
|
525
|
+
} else {
|
|
526
|
+
leftHorizontalRects.push({
|
|
527
|
+
rect: nodeRect,
|
|
528
|
+
sourceNodeId: snapNodeRect.id
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
return {
|
|
534
|
+
top: topVerticalRects,
|
|
535
|
+
bottom: bottomVerticalRects,
|
|
536
|
+
left: leftHorizontalRects,
|
|
537
|
+
right: rightHorizontalRects
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
getMidAlignSpacing(params) {
|
|
541
|
+
const { rectA, rectB, targetRect, isHorizontal } = params;
|
|
542
|
+
if (!rectA || !rectB) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(
|
|
546
|
+
rectA,
|
|
547
|
+
rectB
|
|
548
|
+
);
|
|
549
|
+
if (isIntersection) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {
|
|
553
|
+
const betweenSpacing = Math.min(
|
|
554
|
+
Math.abs(rectA.left - rectB.right),
|
|
555
|
+
Math.abs(rectA.right - rectB.left)
|
|
556
|
+
);
|
|
557
|
+
return (betweenSpacing - targetRect.width) / 2;
|
|
558
|
+
} else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {
|
|
559
|
+
const betweenSpacing = Math.min(
|
|
560
|
+
Math.abs(rectA.top - rectB.bottom),
|
|
561
|
+
Math.abs(rectA.bottom - rectB.top)
|
|
562
|
+
);
|
|
563
|
+
return (betweenSpacing - targetRect.height) / 2;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
getDirectionAlignSpacing(params) {
|
|
567
|
+
const { rects, isHorizontal } = params;
|
|
568
|
+
if (rects.length < 2) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const rectA = rects[0].rect;
|
|
572
|
+
const rectB = rects[1].rect;
|
|
573
|
+
const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(
|
|
574
|
+
rectA,
|
|
575
|
+
rectB
|
|
576
|
+
);
|
|
577
|
+
if (isIntersection) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {
|
|
581
|
+
return Math.min(Math.abs(rectA.left - rectB.right), Math.abs(rectA.right - rectB.left));
|
|
582
|
+
} else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {
|
|
583
|
+
return Math.min(Math.abs(rectA.top - rectB.bottom), Math.abs(rectA.bottom - rectB.top));
|
|
584
|
+
}
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
intersection(rectA, rectB) {
|
|
588
|
+
const isVerticalIntersection = isLessThan(rectA.left, rectB.right) && isGreaterThan(rectA.right, rectB.left);
|
|
589
|
+
const isHorizontalIntersection = isLessThan(rectA.top, rectB.bottom) && isGreaterThan(rectA.bottom, rectB.top);
|
|
590
|
+
const isIntersection = isHorizontalIntersection && isVerticalIntersection;
|
|
591
|
+
return {
|
|
592
|
+
isHorizontalIntersection,
|
|
593
|
+
isVerticalIntersection,
|
|
594
|
+
isIntersection
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
__decorateClass([
|
|
599
|
+
(0, import_inversify.inject)(import_free_layout_core.WorkflowDocument)
|
|
600
|
+
], WorkflowSnapService.prototype, "document", 2);
|
|
601
|
+
__decorateClass([
|
|
602
|
+
(0, import_inversify.inject)(import_core.EntityManager)
|
|
603
|
+
], WorkflowSnapService.prototype, "entityManager", 2);
|
|
604
|
+
__decorateClass([
|
|
605
|
+
(0, import_inversify.inject)(import_free_layout_core2.WorkflowDragService)
|
|
606
|
+
], WorkflowSnapService.prototype, "dragService", 2);
|
|
607
|
+
__decorateClass([
|
|
608
|
+
(0, import_inversify.inject)(import_core.PlaygroundConfigEntity)
|
|
609
|
+
], WorkflowSnapService.prototype, "playgroundConfig", 2);
|
|
610
|
+
WorkflowSnapService = __decorateClass([
|
|
611
|
+
(0, import_inversify.injectable)()
|
|
612
|
+
], WorkflowSnapService);
|
|
613
|
+
|
|
614
|
+
// src/layer.tsx
|
|
615
|
+
var import_react = __toESM(require("react"));
|
|
616
|
+
var import_inversify2 = require("inversify");
|
|
617
|
+
var import_document3 = require("@flowgram.ai/document");
|
|
618
|
+
var import_core2 = require("@flowgram.ai/core");
|
|
619
|
+
var import_free_layout_core3 = require("@flowgram.ai/free-layout-core");
|
|
620
|
+
var import_utils3 = require("@flowgram.ai/utils");
|
|
621
|
+
var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
622
|
+
constructor() {
|
|
623
|
+
super(...arguments);
|
|
624
|
+
this.node = import_utils3.domUtils.createDivWithClass(
|
|
625
|
+
"gedit-playground-layer gedit-flow-snap-layer"
|
|
626
|
+
);
|
|
627
|
+
this.edgeLines = [];
|
|
628
|
+
this.alignLines = [];
|
|
629
|
+
}
|
|
630
|
+
onReady() {
|
|
631
|
+
this.node.style.zIndex = "9999";
|
|
632
|
+
this.toDispose.pushAll([
|
|
633
|
+
this.service.onSnap((event) => {
|
|
634
|
+
this.edgeLines = this.calcEdgeLines(event);
|
|
635
|
+
this.alignLines = this.calcAlignLines(event);
|
|
636
|
+
this.render();
|
|
637
|
+
})
|
|
638
|
+
]);
|
|
639
|
+
}
|
|
640
|
+
render() {
|
|
641
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, this.alignLines.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "workflow-snap-align-lines" }, this.renderAlignLines()), this.edgeLines.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "workflow-snap-edge-lines" }, this.renderEdgeLines()));
|
|
642
|
+
}
|
|
643
|
+
onZoom(scale) {
|
|
644
|
+
this.node.style.transform = `scale(${scale})`;
|
|
645
|
+
}
|
|
646
|
+
renderEdgeLines() {
|
|
647
|
+
return this.edgeLines.map((renderLine) => {
|
|
648
|
+
const { className, sourceNode, top, left, width, height, dashed } = renderLine;
|
|
649
|
+
const id = `${className}-${sourceNode}-${top}-${left}-${width}-${height}`;
|
|
650
|
+
const isHorizontal = width < height;
|
|
651
|
+
const border = `${this.options.edgeLineWidth}px ${dashed ? "dashed" : "solid"} ${this.options.edgeColor}`;
|
|
652
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
653
|
+
"div",
|
|
654
|
+
{
|
|
655
|
+
className: `workflow-snap-edge-line ${className}`,
|
|
656
|
+
"data-testid": "sdk.workflow.canvas.snap.edgeLine",
|
|
657
|
+
"data-snap-line-id": id,
|
|
658
|
+
"data-snap-line-sourceNode": sourceNode,
|
|
659
|
+
key: id,
|
|
660
|
+
style: {
|
|
661
|
+
top,
|
|
662
|
+
left,
|
|
663
|
+
width,
|
|
664
|
+
height,
|
|
665
|
+
position: "absolute",
|
|
666
|
+
borderLeft: isHorizontal ? border : "none",
|
|
667
|
+
borderTop: !isHorizontal ? border : "none"
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
);
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
renderAlignLines() {
|
|
674
|
+
return this.alignLines.map((renderLine) => {
|
|
675
|
+
const id = `${renderLine.className}-${renderLine.sourceNode}-${renderLine.top}-${renderLine.left}-${renderLine.width}-${renderLine.height}`;
|
|
676
|
+
const isHorizontal = isGreaterThan(renderLine.width, renderLine.height);
|
|
677
|
+
const alignLineWidth = this.options.alignLineWidth;
|
|
678
|
+
const alignCrossWidth = this.options.alignCrossWidth;
|
|
679
|
+
const adjustedTop = isHorizontal ? renderLine.top - alignLineWidth / 2 : renderLine.top;
|
|
680
|
+
const adjustedLeft = isHorizontal ? renderLine.left : renderLine.left - alignLineWidth / 2;
|
|
681
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
682
|
+
"div",
|
|
683
|
+
{
|
|
684
|
+
className: `workflow-snap-align-line ${renderLine.className}`,
|
|
685
|
+
"data-testid": "sdk.workflow.canvas.snap.alignLine",
|
|
686
|
+
"data-snap-line-id": id,
|
|
687
|
+
"data-snap-line-sourceNode": renderLine.sourceNode,
|
|
688
|
+
key: id,
|
|
689
|
+
style: {
|
|
690
|
+
position: "absolute"
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
694
|
+
"div",
|
|
695
|
+
{
|
|
696
|
+
style: {
|
|
697
|
+
position: "absolute",
|
|
698
|
+
top: adjustedTop,
|
|
699
|
+
left: adjustedLeft,
|
|
700
|
+
width: isHorizontal ? renderLine.width : alignLineWidth,
|
|
701
|
+
height: isHorizontal ? alignLineWidth : renderLine.height,
|
|
702
|
+
backgroundColor: this.options.alignColor
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
),
|
|
706
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
707
|
+
"div",
|
|
708
|
+
{
|
|
709
|
+
style: {
|
|
710
|
+
position: "absolute",
|
|
711
|
+
top: isHorizontal ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2 : adjustedTop,
|
|
712
|
+
left: isHorizontal ? adjustedLeft : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,
|
|
713
|
+
width: isHorizontal ? alignLineWidth : alignCrossWidth,
|
|
714
|
+
height: isHorizontal ? alignCrossWidth : alignLineWidth,
|
|
715
|
+
backgroundColor: this.options.alignColor
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
),
|
|
719
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
720
|
+
"div",
|
|
721
|
+
{
|
|
722
|
+
style: {
|
|
723
|
+
position: "absolute",
|
|
724
|
+
top: isHorizontal ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2 : adjustedTop + renderLine.height - alignLineWidth,
|
|
725
|
+
left: isHorizontal ? adjustedLeft + renderLine.width - alignLineWidth : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,
|
|
726
|
+
width: isHorizontal ? alignLineWidth : alignCrossWidth,
|
|
727
|
+
height: isHorizontal ? alignCrossWidth : alignLineWidth,
|
|
728
|
+
backgroundColor: this.options.alignColor
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
)
|
|
732
|
+
);
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
calcEdgeLines(event) {
|
|
736
|
+
const { alignRects, snapRect, snapEdgeLines } = event;
|
|
737
|
+
const edgeLines = [];
|
|
738
|
+
const topFullAlign = this.directionFullAlign({
|
|
739
|
+
alignRects: alignRects.top,
|
|
740
|
+
targetRect: snapRect,
|
|
741
|
+
isVertical: true
|
|
742
|
+
});
|
|
743
|
+
const bottomFullAlign = this.directionFullAlign({
|
|
744
|
+
alignRects: alignRects.bottom,
|
|
745
|
+
targetRect: snapRect,
|
|
746
|
+
isVertical: true
|
|
747
|
+
});
|
|
748
|
+
const leftFullAlign = this.directionFullAlign({
|
|
749
|
+
alignRects: alignRects.left,
|
|
750
|
+
targetRect: snapRect,
|
|
751
|
+
isVertical: false
|
|
752
|
+
});
|
|
753
|
+
const rightFullAlign = this.directionFullAlign({
|
|
754
|
+
alignRects: alignRects.right,
|
|
755
|
+
targetRect: snapRect,
|
|
756
|
+
isVertical: false
|
|
757
|
+
});
|
|
758
|
+
if (topFullAlign) {
|
|
759
|
+
const top = topFullAlign.rect.top;
|
|
760
|
+
const height = bottomFullAlign ? snapRect.bottom - snapRect.height / 2 - top : snapRect.bottom - top;
|
|
761
|
+
const width = this.options.edgeLineWidth;
|
|
762
|
+
const lineData = {
|
|
763
|
+
top,
|
|
764
|
+
width,
|
|
765
|
+
height
|
|
766
|
+
};
|
|
767
|
+
edgeLines.push({
|
|
768
|
+
className: "edge-full-top-left",
|
|
769
|
+
sourceNode: topFullAlign.sourceNodeId,
|
|
770
|
+
left: snapRect.left,
|
|
771
|
+
...lineData
|
|
772
|
+
});
|
|
773
|
+
edgeLines.push({
|
|
774
|
+
className: "edge-full-top-right",
|
|
775
|
+
sourceNode: topFullAlign.sourceNodeId,
|
|
776
|
+
left: snapRect.right,
|
|
777
|
+
...lineData
|
|
778
|
+
});
|
|
779
|
+
edgeLines.push({
|
|
780
|
+
className: "edge-full-top-mid",
|
|
781
|
+
sourceNode: topFullAlign.sourceNodeId,
|
|
782
|
+
left: snapRect.left + snapRect.width / 2,
|
|
783
|
+
dashed: true,
|
|
784
|
+
...lineData
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
if (bottomFullAlign) {
|
|
788
|
+
const top = topFullAlign ? snapRect.top + snapRect.height / 2 : snapRect.top;
|
|
789
|
+
const height = bottomFullAlign.rect.bottom - top;
|
|
790
|
+
const width = this.options.edgeLineWidth;
|
|
791
|
+
const lineData = {
|
|
792
|
+
top,
|
|
793
|
+
width,
|
|
794
|
+
height
|
|
795
|
+
};
|
|
796
|
+
edgeLines.push({
|
|
797
|
+
className: "edge-full-bottom-left",
|
|
798
|
+
sourceNode: bottomFullAlign.sourceNodeId,
|
|
799
|
+
left: snapRect.left,
|
|
800
|
+
...lineData
|
|
801
|
+
});
|
|
802
|
+
edgeLines.push({
|
|
803
|
+
className: "edge-full-bottom-right",
|
|
804
|
+
sourceNode: bottomFullAlign.sourceNodeId,
|
|
805
|
+
left: snapRect.right,
|
|
806
|
+
...lineData
|
|
807
|
+
});
|
|
808
|
+
edgeLines.push({
|
|
809
|
+
className: "edge-full-bottom-mid",
|
|
810
|
+
sourceNode: bottomFullAlign.sourceNodeId,
|
|
811
|
+
left: snapRect.left + snapRect.width / 2,
|
|
812
|
+
dashed: true,
|
|
813
|
+
...lineData
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
if (leftFullAlign) {
|
|
817
|
+
const left = leftFullAlign.rect.left;
|
|
818
|
+
const width = rightFullAlign ? snapRect.right - snapRect.width / 2 - left : snapRect.right - left;
|
|
819
|
+
const height = this.options.edgeLineWidth;
|
|
820
|
+
const lineData = {
|
|
821
|
+
left,
|
|
822
|
+
width,
|
|
823
|
+
height
|
|
824
|
+
};
|
|
825
|
+
edgeLines.push({
|
|
826
|
+
className: "edge-full-left-top",
|
|
827
|
+
sourceNode: leftFullAlign.sourceNodeId,
|
|
828
|
+
top: snapRect.top,
|
|
829
|
+
...lineData
|
|
830
|
+
});
|
|
831
|
+
edgeLines.push({
|
|
832
|
+
className: "edge-full-left-bottom",
|
|
833
|
+
sourceNode: leftFullAlign.sourceNodeId,
|
|
834
|
+
top: snapRect.bottom,
|
|
835
|
+
...lineData
|
|
836
|
+
});
|
|
837
|
+
edgeLines.push({
|
|
838
|
+
className: "edge-full-left-mid",
|
|
839
|
+
sourceNode: leftFullAlign.sourceNodeId,
|
|
840
|
+
top: snapRect.top + snapRect.height / 2,
|
|
841
|
+
dashed: true,
|
|
842
|
+
...lineData
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
if (rightFullAlign) {
|
|
846
|
+
const left = leftFullAlign ? snapRect.left + snapRect.width / 2 : snapRect.left;
|
|
847
|
+
const width = rightFullAlign.rect.right - left;
|
|
848
|
+
const height = this.options.edgeLineWidth;
|
|
849
|
+
const lineData = {
|
|
850
|
+
left,
|
|
851
|
+
width,
|
|
852
|
+
height
|
|
853
|
+
};
|
|
854
|
+
edgeLines.push({
|
|
855
|
+
className: "edge-full-right-top",
|
|
856
|
+
sourceNode: rightFullAlign.sourceNodeId,
|
|
857
|
+
top: snapRect.top,
|
|
858
|
+
...lineData
|
|
859
|
+
});
|
|
860
|
+
edgeLines.push({
|
|
861
|
+
className: "edge-full-right-bottom",
|
|
862
|
+
sourceNode: rightFullAlign.sourceNodeId,
|
|
863
|
+
top: snapRect.bottom,
|
|
864
|
+
...lineData
|
|
865
|
+
});
|
|
866
|
+
edgeLines.push({
|
|
867
|
+
className: "edge-full-right-mid",
|
|
868
|
+
sourceNode: rightFullAlign.sourceNodeId,
|
|
869
|
+
top: snapRect.top + snapRect.height / 2,
|
|
870
|
+
dashed: true,
|
|
871
|
+
...lineData
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
const snappedEdgeLines = Object.entries(snapEdgeLines).map(([direction, snapLine]) => {
|
|
875
|
+
if (!snapLine) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const sourceNode = this.document.getNode(snapLine.sourceNodeId);
|
|
879
|
+
if (!sourceNode) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
const nodeRect = sourceNode.getData(import_document3.FlowNodeTransformData).bounds;
|
|
883
|
+
if (isNumber(snapLine.x)) {
|
|
884
|
+
const top = Math.min(nodeRect.top, snapRect.top);
|
|
885
|
+
const bottom = Math.max(nodeRect.bottom, snapRect.bottom);
|
|
886
|
+
const height = bottom - top;
|
|
887
|
+
const left = snapLine.x;
|
|
888
|
+
const width = this.options.edgeLineWidth;
|
|
889
|
+
const isMidX = direction === "midVertical";
|
|
890
|
+
const lineData = {
|
|
891
|
+
className: `edge-snapped-${direction}`,
|
|
892
|
+
sourceNode: snapLine.sourceNodeId,
|
|
893
|
+
top,
|
|
894
|
+
left,
|
|
895
|
+
width,
|
|
896
|
+
height,
|
|
897
|
+
dashed: isMidX
|
|
898
|
+
};
|
|
899
|
+
const onTop = top === nodeRect.top;
|
|
900
|
+
if (onTop && topFullAlign) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
if (!onTop && bottomFullAlign) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
return lineData;
|
|
907
|
+
} else if (isNumber(snapLine.y)) {
|
|
908
|
+
const left = Math.min(nodeRect.left, snapRect.left);
|
|
909
|
+
const right = Math.max(nodeRect.right, snapRect.right);
|
|
910
|
+
const width = right - left;
|
|
911
|
+
const top = snapLine.y;
|
|
912
|
+
const height = this.options.edgeLineWidth;
|
|
913
|
+
const isMidY = direction === "midHorizontal";
|
|
914
|
+
const lineData = {
|
|
915
|
+
className: `edge-snapped-${direction}`,
|
|
916
|
+
sourceNode: snapLine.sourceNodeId,
|
|
917
|
+
top,
|
|
918
|
+
left,
|
|
919
|
+
width,
|
|
920
|
+
height,
|
|
921
|
+
dashed: isMidY
|
|
922
|
+
};
|
|
923
|
+
const onLeft = left === nodeRect.left;
|
|
924
|
+
if (onLeft && leftFullAlign) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
if (!onLeft && rightFullAlign) {
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
return lineData;
|
|
931
|
+
}
|
|
932
|
+
}).filter(Boolean);
|
|
933
|
+
edgeLines.push(...snappedEdgeLines);
|
|
934
|
+
return edgeLines;
|
|
935
|
+
}
|
|
936
|
+
directionFullAlign(params) {
|
|
937
|
+
const { alignRects, targetRect, isVertical } = params;
|
|
938
|
+
let fullAlignIndex = -1;
|
|
939
|
+
for (let i = 0; i < alignRects.length; i++) {
|
|
940
|
+
const alignRect = alignRects[i];
|
|
941
|
+
const prevRect = alignRects[i - 1]?.rect ?? targetRect;
|
|
942
|
+
const isFullAlign = this.rectFullAlign(alignRect.rect, prevRect, isVertical);
|
|
943
|
+
if (!isFullAlign) {
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
fullAlignIndex = i;
|
|
947
|
+
}
|
|
948
|
+
const fullAlignRect = alignRects[fullAlignIndex];
|
|
949
|
+
return fullAlignRect;
|
|
950
|
+
}
|
|
951
|
+
rectFullAlign(rectA, rectB, isVertical) {
|
|
952
|
+
if (isVertical) {
|
|
953
|
+
return isEqual(rectA.left, rectB.left) && isEqual(rectA.right, rectB.right);
|
|
954
|
+
} else {
|
|
955
|
+
return isEqual(rectA.top, rectB.top) && isEqual(rectA.bottom, rectB.bottom);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
calcAlignLines(event) {
|
|
959
|
+
const { alignRects, alignSpacing, snapRect } = event;
|
|
960
|
+
const topAlignLines = this.calcDirectionAlignLines({
|
|
961
|
+
alignRects: alignRects.top,
|
|
962
|
+
targetRect: snapRect,
|
|
963
|
+
isVertical: true,
|
|
964
|
+
spacing: alignSpacing.midVertical ?? alignSpacing.top
|
|
965
|
+
});
|
|
966
|
+
const bottomAlignLines = this.calcDirectionAlignLines({
|
|
967
|
+
alignRects: alignRects.bottom,
|
|
968
|
+
targetRect: snapRect,
|
|
969
|
+
isVertical: true,
|
|
970
|
+
spacing: alignSpacing.midVertical ?? alignSpacing.bottom
|
|
971
|
+
});
|
|
972
|
+
const leftAlignLines = this.calcDirectionAlignLines({
|
|
973
|
+
alignRects: alignRects.left,
|
|
974
|
+
targetRect: snapRect,
|
|
975
|
+
isVertical: false,
|
|
976
|
+
spacing: alignSpacing.midHorizontal ?? alignSpacing.left
|
|
977
|
+
});
|
|
978
|
+
const rightAlignLines = this.calcDirectionAlignLines({
|
|
979
|
+
alignRects: alignRects.right,
|
|
980
|
+
targetRect: snapRect,
|
|
981
|
+
isVertical: false,
|
|
982
|
+
spacing: alignSpacing.midHorizontal ?? alignSpacing.right
|
|
983
|
+
});
|
|
984
|
+
return [...topAlignLines, ...bottomAlignLines, ...leftAlignLines, ...rightAlignLines];
|
|
985
|
+
}
|
|
986
|
+
calcDirectionAlignLines(params) {
|
|
987
|
+
const { alignRects, targetRect, isVertical, spacing } = params;
|
|
988
|
+
const alignLines = [];
|
|
989
|
+
if (!spacing) {
|
|
990
|
+
return alignLines;
|
|
991
|
+
}
|
|
992
|
+
for (let i = 0; i < alignRects.length; i++) {
|
|
993
|
+
const alignRect = alignRects[i];
|
|
994
|
+
const rect = alignRect.rect;
|
|
995
|
+
const prevRect = alignRects[i - 1]?.rect ?? targetRect;
|
|
996
|
+
const betweenSpacing = isVertical ? Math.min(Math.abs(prevRect.top - rect.bottom), Math.abs(prevRect.bottom - rect.top)) : Math.min(Math.abs(prevRect.left - rect.right), Math.abs(prevRect.right - rect.left));
|
|
997
|
+
if (!isEqual(betweenSpacing, spacing)) {
|
|
998
|
+
break;
|
|
999
|
+
}
|
|
1000
|
+
if (isVertical) {
|
|
1001
|
+
const centerX = this.calcHorizontalIntersectionCenter(rect, targetRect);
|
|
1002
|
+
alignLines.push({
|
|
1003
|
+
className: "align-vertical",
|
|
1004
|
+
sourceNode: alignRect.sourceNodeId,
|
|
1005
|
+
top: Math.min(rect.bottom, prevRect.bottom),
|
|
1006
|
+
left: centerX,
|
|
1007
|
+
width: 1,
|
|
1008
|
+
height: spacing
|
|
1009
|
+
});
|
|
1010
|
+
} else {
|
|
1011
|
+
const centerY = this.calcVerticalIntersectionCenter(rect, targetRect);
|
|
1012
|
+
alignLines.push({
|
|
1013
|
+
className: "align-horizontal",
|
|
1014
|
+
sourceNode: alignRect.sourceNodeId,
|
|
1015
|
+
top: centerY,
|
|
1016
|
+
left: Math.min(rect.right, prevRect.right),
|
|
1017
|
+
width: spacing,
|
|
1018
|
+
height: 1
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return alignLines;
|
|
1023
|
+
}
|
|
1024
|
+
calcVerticalIntersectionCenter(rectA, rectB) {
|
|
1025
|
+
const top = Math.max(rectA.top, rectB.top);
|
|
1026
|
+
const bottom = Math.min(rectA.bottom, rectB.bottom);
|
|
1027
|
+
return (top + bottom) / 2;
|
|
1028
|
+
}
|
|
1029
|
+
calcHorizontalIntersectionCenter(rectA, rectB) {
|
|
1030
|
+
const left = Math.max(rectA.left, rectB.left);
|
|
1031
|
+
const right = Math.min(rectA.right, rectB.right);
|
|
1032
|
+
return (left + right) / 2;
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
WorkflowSnapLayer.type = "WorkflowSnapLayer";
|
|
1036
|
+
__decorateClass([
|
|
1037
|
+
(0, import_inversify2.inject)(import_free_layout_core3.WorkflowDocument)
|
|
1038
|
+
], WorkflowSnapLayer.prototype, "document", 2);
|
|
1039
|
+
__decorateClass([
|
|
1040
|
+
(0, import_inversify2.inject)(WorkflowSnapService)
|
|
1041
|
+
], WorkflowSnapLayer.prototype, "service", 2);
|
|
1042
|
+
WorkflowSnapLayer = __decorateClass([
|
|
1043
|
+
(0, import_inversify2.injectable)()
|
|
1044
|
+
], WorkflowSnapLayer);
|
|
1045
|
+
|
|
1046
|
+
// src/create-plugin.ts
|
|
1047
|
+
var createFreeSnapPlugin = (0, import_core3.definePluginCreator)({
|
|
1048
|
+
onBind({ bind }) {
|
|
1049
|
+
bind(WorkflowSnapService).toSelf().inSingletonScope();
|
|
1050
|
+
},
|
|
1051
|
+
onInit(ctx, opts) {
|
|
1052
|
+
const options = {
|
|
1053
|
+
...SnapDefaultOptions,
|
|
1054
|
+
...opts
|
|
1055
|
+
};
|
|
1056
|
+
ctx.playground.registerLayer(WorkflowSnapLayer, options);
|
|
1057
|
+
const snapService = ctx.get(WorkflowSnapService);
|
|
1058
|
+
snapService.init(options);
|
|
1059
|
+
},
|
|
1060
|
+
onDispose(ctx) {
|
|
1061
|
+
const snapService = ctx.get(WorkflowSnapService);
|
|
1062
|
+
snapService.dispose();
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1066
|
+
0 && (module.exports = {
|
|
1067
|
+
WorkflowSnapService,
|
|
1068
|
+
createFreeSnapPlugin
|
|
1069
|
+
});
|
|
1070
|
+
//# sourceMappingURL=index.js.map
|