@flowgram.ai/free-lines-plugin 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +1305 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +192 -0
- package/dist/index.d.ts +192 -0
- package/dist/index.js +1329 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,1305 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/components/workflow-port-render/index.tsx
|
|
13
|
+
import ReactDOM from "react-dom";
|
|
14
|
+
import React2, { useEffect, useState } from "react";
|
|
15
|
+
import classNames from "clsx";
|
|
16
|
+
import {
|
|
17
|
+
WorkflowHoverService,
|
|
18
|
+
usePlaygroundReadonlyState,
|
|
19
|
+
WorkflowLinesManager
|
|
20
|
+
} from "@flowgram.ai/free-layout-core";
|
|
21
|
+
import { useService } from "@flowgram.ai/core";
|
|
22
|
+
|
|
23
|
+
// src/constants/points.ts
|
|
24
|
+
var STROKE_WIDTH_SLECTED = 3;
|
|
25
|
+
var STROKE_WIDTH = 2;
|
|
26
|
+
var PORT_BG_CLASS_NAME = "workflow-port-bg";
|
|
27
|
+
|
|
28
|
+
// src/components/workflow-port-render/style.ts
|
|
29
|
+
import styled from "styled-components";
|
|
30
|
+
var WorkflowPointStyle = styled.div`
|
|
31
|
+
width: 20px;
|
|
32
|
+
height: 20px;
|
|
33
|
+
border-radius: 50%;
|
|
34
|
+
margin-top: -10px;
|
|
35
|
+
margin-left: -10px;
|
|
36
|
+
left: 50%;
|
|
37
|
+
top: 50%;
|
|
38
|
+
position: absolute;
|
|
39
|
+
// 非 hover 状态下的样式
|
|
40
|
+
border: none;
|
|
41
|
+
|
|
42
|
+
& > .symbol {
|
|
43
|
+
opacity: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.bg-circle {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
position: absolute;
|
|
51
|
+
border-radius: 50%;
|
|
52
|
+
width: 20px;
|
|
53
|
+
height: 20px;
|
|
54
|
+
background-color: #fff;
|
|
55
|
+
transform: scale(0.5);
|
|
56
|
+
transition: all 0.2s linear 0s;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.bg {
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
position: relative;
|
|
64
|
+
width: 100%;
|
|
65
|
+
height: 100%;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
background: #9197F1;
|
|
68
|
+
transform: scale(0.4, 0.4);
|
|
69
|
+
transition: all 0.2s linear 0s;
|
|
70
|
+
|
|
71
|
+
&.hasError {
|
|
72
|
+
background: red;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.symbol {
|
|
76
|
+
position: absolute;
|
|
77
|
+
width: 14px;
|
|
78
|
+
height: 14px;
|
|
79
|
+
opacity: 0;
|
|
80
|
+
pointer-events: none;
|
|
81
|
+
color: #fff;
|
|
82
|
+
transition: opacity 0.2s linear 0s;
|
|
83
|
+
|
|
84
|
+
& > svg {
|
|
85
|
+
width: 14px;
|
|
86
|
+
height: 14px;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.focus-circle {
|
|
91
|
+
position: absolute;
|
|
92
|
+
display: flex;
|
|
93
|
+
justify-content: center;
|
|
94
|
+
align-items: center;
|
|
95
|
+
width: 8px;
|
|
96
|
+
height: 8px;
|
|
97
|
+
opacity: 0;
|
|
98
|
+
background: #9197f1;
|
|
99
|
+
border-radius: 50%;
|
|
100
|
+
transition: opacity 0.2s linear 0s;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
&.linked .bg:not(.hasError) {
|
|
105
|
+
background: #4d53e8;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
&.hovered .bg:not(.hasError) {
|
|
109
|
+
border: none;
|
|
110
|
+
cursor: crosshair;
|
|
111
|
+
transform: scale(1, 1);
|
|
112
|
+
background: #4d53e8;
|
|
113
|
+
|
|
114
|
+
& > .symbol {
|
|
115
|
+
opacity: 1;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.cross-hair {
|
|
120
|
+
position: relative;
|
|
121
|
+
left: 2px;
|
|
122
|
+
top: 2px;
|
|
123
|
+
|
|
124
|
+
&::after,
|
|
125
|
+
&::before {
|
|
126
|
+
content: '';
|
|
127
|
+
background: #fff;
|
|
128
|
+
border-radius: 2px;
|
|
129
|
+
position: absolute;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&::after {
|
|
133
|
+
left: 4px;
|
|
134
|
+
width: 2px;
|
|
135
|
+
height: 6px;
|
|
136
|
+
box-shadow: 0 4px #fff;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
&::before {
|
|
140
|
+
top: 4px;
|
|
141
|
+
width: 6px;
|
|
142
|
+
height: 2px;
|
|
143
|
+
box-shadow: 4px 0 #fff;
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
|
|
147
|
+
// src/components/workflow-port-render/cross-hair.tsx
|
|
148
|
+
import React from "react";
|
|
149
|
+
function CrossHair() {
|
|
150
|
+
return /* @__PURE__ */ React.createElement("div", { className: "symbol" }, /* @__PURE__ */ React.createElement("div", { className: "cross-hair" }));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/components/workflow-port-render/index.tsx
|
|
154
|
+
var WorkflowPortRender = (
|
|
155
|
+
// eslint-disable-next-line react/display-name
|
|
156
|
+
React2.memo((props) => {
|
|
157
|
+
const hoverService = useService(WorkflowHoverService);
|
|
158
|
+
const linesManager = useService(WorkflowLinesManager);
|
|
159
|
+
const { entity, onClick } = props;
|
|
160
|
+
const { portType, relativePosition, disabled } = entity;
|
|
161
|
+
const [targetElement, setTargetElement] = useState(entity.targetElement);
|
|
162
|
+
const [posX, updatePosX] = useState(relativePosition.x);
|
|
163
|
+
const [posY, updatePosY] = useState(relativePosition.y);
|
|
164
|
+
const [hovered, setHovered] = useState(false);
|
|
165
|
+
const [linked, setLinked] = useState(Boolean(entity?.lines?.length));
|
|
166
|
+
const [hasError, setHasError] = useState(props.entity.hasError);
|
|
167
|
+
const readonly = usePlaygroundReadonlyState();
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
entity.validate();
|
|
170
|
+
setHasError(entity.hasError);
|
|
171
|
+
const dispose = entity.onEntityChange(() => {
|
|
172
|
+
if (entity.targetElement) {
|
|
173
|
+
if (entity.targetElement !== targetElement) {
|
|
174
|
+
setTargetElement(entity.targetElement);
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const newPos = entity.relativePosition;
|
|
179
|
+
updatePosX(Math.round(newPos.x));
|
|
180
|
+
updatePosY(Math.round(newPos.y));
|
|
181
|
+
});
|
|
182
|
+
const dispose2 = hoverService.onHoveredChange((id) => {
|
|
183
|
+
setHovered(hoverService.isHovered(entity.id));
|
|
184
|
+
});
|
|
185
|
+
const dispose3 = entity.onErrorChanged(() => {
|
|
186
|
+
setHasError(entity.hasError);
|
|
187
|
+
});
|
|
188
|
+
const dispose4 = linesManager.onAvailableLinesChange(() => {
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
if (linesManager.disposed || entity.disposed) return;
|
|
191
|
+
setLinked(Boolean(entity.lines.length));
|
|
192
|
+
}, 0);
|
|
193
|
+
});
|
|
194
|
+
return () => {
|
|
195
|
+
dispose.dispose();
|
|
196
|
+
dispose2.dispose();
|
|
197
|
+
dispose3.dispose();
|
|
198
|
+
dispose4.dispose();
|
|
199
|
+
};
|
|
200
|
+
}, [hoverService, entity, targetElement]);
|
|
201
|
+
const className = classNames(props.className || "", {
|
|
202
|
+
hovered: !readonly && hovered && !disabled && portType !== "input",
|
|
203
|
+
// 有线条链接的时候深蓝色小圆点
|
|
204
|
+
linked
|
|
205
|
+
});
|
|
206
|
+
const content = /* @__PURE__ */ React2.createElement(
|
|
207
|
+
WorkflowPointStyle,
|
|
208
|
+
{
|
|
209
|
+
className,
|
|
210
|
+
style: targetElement ? props.style : { ...props.style, left: posX, top: posY },
|
|
211
|
+
onClick,
|
|
212
|
+
"data-port-entity-id": entity.id,
|
|
213
|
+
"data-port-entity-type": entity.portType,
|
|
214
|
+
"data-testid": "sdk.workflow.canvas.node.port"
|
|
215
|
+
},
|
|
216
|
+
/* @__PURE__ */ React2.createElement("div", { className: classNames("bg-circle", "workflow-bg-circle") }),
|
|
217
|
+
/* @__PURE__ */ React2.createElement(
|
|
218
|
+
"div",
|
|
219
|
+
{
|
|
220
|
+
className: classNames({
|
|
221
|
+
bg: true,
|
|
222
|
+
[PORT_BG_CLASS_NAME]: true,
|
|
223
|
+
"workflow-point-bg": true,
|
|
224
|
+
hasError
|
|
225
|
+
})
|
|
226
|
+
},
|
|
227
|
+
/* @__PURE__ */ React2.createElement(CrossHair, null)
|
|
228
|
+
),
|
|
229
|
+
/* @__PURE__ */ React2.createElement("div", { className: "focus-circle" })
|
|
230
|
+
);
|
|
231
|
+
if (targetElement) {
|
|
232
|
+
return ReactDOM.createPortal(content, targetElement);
|
|
233
|
+
}
|
|
234
|
+
return content;
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// src/constants/lines.ts
|
|
239
|
+
var LINE_OFFSET = 6;
|
|
240
|
+
var LINE_PADDING = 12;
|
|
241
|
+
|
|
242
|
+
// src/create-free-lines-plugin.ts
|
|
243
|
+
import { WorkflowLinesManager as WorkflowLinesManager2 } from "@flowgram.ai/free-layout-core";
|
|
244
|
+
import { definePluginCreator } from "@flowgram.ai/core";
|
|
245
|
+
|
|
246
|
+
// src/layer/workflow-lines-layer.tsx
|
|
247
|
+
import ReactDOM2 from "react-dom";
|
|
248
|
+
import React5, { useLayoutEffect, useState as useState2 } from "react";
|
|
249
|
+
import { inject, injectable } from "inversify";
|
|
250
|
+
import { domUtils } from "@flowgram.ai/utils";
|
|
251
|
+
import { StackingContextManager } from "@flowgram.ai/free-stack-plugin";
|
|
252
|
+
import {
|
|
253
|
+
nanoid,
|
|
254
|
+
WorkflowDocument,
|
|
255
|
+
WorkflowHoverService as WorkflowHoverService2,
|
|
256
|
+
WorkflowLineEntity,
|
|
257
|
+
WorkflowLineRenderData as WorkflowLineRenderData2,
|
|
258
|
+
WorkflowNodeEntity,
|
|
259
|
+
WorkflowPortEntity,
|
|
260
|
+
WorkflowSelectService
|
|
261
|
+
} from "@flowgram.ai/free-layout-core";
|
|
262
|
+
import { Layer, observeEntities, observeEntityDatas, TransformData } from "@flowgram.ai/core";
|
|
263
|
+
|
|
264
|
+
// src/components/workflow-line-render/index.tsx
|
|
265
|
+
import { memo } from "react";
|
|
266
|
+
|
|
267
|
+
// src/components/workflow-line-render/line-svg.tsx
|
|
268
|
+
import React4 from "react";
|
|
269
|
+
import clsx from "clsx";
|
|
270
|
+
import { POINT_RADIUS } from "@flowgram.ai/free-layout-core";
|
|
271
|
+
import { WorkflowLineRenderData } from "@flowgram.ai/free-layout-core";
|
|
272
|
+
|
|
273
|
+
// src/components/workflow-line-render/index.style.ts
|
|
274
|
+
import styled2 from "styled-components";
|
|
275
|
+
var LineStyle = styled2.div.attrs({
|
|
276
|
+
className: "gedit-flow-activity-edge"
|
|
277
|
+
})`
|
|
278
|
+
position: absolute;
|
|
279
|
+
|
|
280
|
+
@keyframes flowingDash {
|
|
281
|
+
to {
|
|
282
|
+
stroke-dashoffset: -13;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.dashed-line {
|
|
287
|
+
stroke-dasharray: 8, 5;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.flowing-line {
|
|
291
|
+
animation: flowingDash 0.5s linear infinite;
|
|
292
|
+
}
|
|
293
|
+
`;
|
|
294
|
+
|
|
295
|
+
// src/components/workflow-line-render/arrow.tsx
|
|
296
|
+
import React3 from "react";
|
|
297
|
+
function ArrowRenderer({
|
|
298
|
+
id,
|
|
299
|
+
pos,
|
|
300
|
+
reverseArrow,
|
|
301
|
+
strokeWidth,
|
|
302
|
+
vertical,
|
|
303
|
+
hide
|
|
304
|
+
}) {
|
|
305
|
+
if (hide) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
const arrowPath = vertical ? reverseArrow ? `M ${pos.x - LINE_OFFSET},${pos.y} L ${pos.x},${pos.y - LINE_OFFSET} L ${pos.x + LINE_OFFSET},${pos.y}` : `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${pos.x + LINE_OFFSET},${pos.y - LINE_OFFSET}` : reverseArrow ? `M ${pos.x},${pos.y + LINE_OFFSET} L ${pos.x - LINE_OFFSET},${pos.y} L ${pos.x},${pos.y - LINE_OFFSET}` : `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${pos.x - LINE_OFFSET},${pos.y + LINE_OFFSET}`;
|
|
309
|
+
return /* @__PURE__ */ React3.createElement(
|
|
310
|
+
"path",
|
|
311
|
+
{
|
|
312
|
+
d: arrowPath,
|
|
313
|
+
strokeLinecap: "round",
|
|
314
|
+
stroke: `url(#${id})`,
|
|
315
|
+
fill: "none",
|
|
316
|
+
strokeWidth
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/components/workflow-line-render/line-svg.tsx
|
|
322
|
+
var PADDING = 12;
|
|
323
|
+
var LineSVG = (props) => {
|
|
324
|
+
const { line, color, selected, children, strokePrefix } = props;
|
|
325
|
+
const { position, reverse, vertical, hideArrow } = line;
|
|
326
|
+
const renderData = line.getData(WorkflowLineRenderData);
|
|
327
|
+
const { bounds, path: bezierPath } = renderData;
|
|
328
|
+
const toRelative = (p) => ({
|
|
329
|
+
x: p.x - bounds.x + PADDING,
|
|
330
|
+
y: p.y - bounds.y + PADDING
|
|
331
|
+
});
|
|
332
|
+
const fromPos = toRelative(position.from);
|
|
333
|
+
const toPos = toRelative(position.to);
|
|
334
|
+
const arrowToPos = vertical ? { x: toPos.x, y: toPos.y - POINT_RADIUS } : { x: toPos.x - POINT_RADIUS, y: toPos.y };
|
|
335
|
+
const arrowFromPos = vertical ? { x: fromPos.x, y: fromPos.y + POINT_RADIUS + LINE_OFFSET } : { x: fromPos.x + POINT_RADIUS + LINE_OFFSET, y: fromPos.y };
|
|
336
|
+
const strokeWidth = selected ? STROKE_WIDTH_SLECTED : STROKE_WIDTH;
|
|
337
|
+
const strokeID = strokePrefix ? `${strokePrefix}-${line.id}` : line.id;
|
|
338
|
+
const path = /* @__PURE__ */ React4.createElement(
|
|
339
|
+
"path",
|
|
340
|
+
{
|
|
341
|
+
d: bezierPath,
|
|
342
|
+
fill: "none",
|
|
343
|
+
stroke: `url(#${strokeID})`,
|
|
344
|
+
strokeWidth,
|
|
345
|
+
className: clsx(
|
|
346
|
+
line.className,
|
|
347
|
+
// 显示流动线条的条件:没有自定义线条class,并且线条处于流动或处理中
|
|
348
|
+
!line.className && (line.processing || line.flowing ? "dashed-line flowing-line" : "")
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
return /* @__PURE__ */ React4.createElement(
|
|
353
|
+
LineStyle,
|
|
354
|
+
{
|
|
355
|
+
style: {
|
|
356
|
+
left: bounds.x - PADDING,
|
|
357
|
+
top: bounds.y - PADDING,
|
|
358
|
+
position: "absolute"
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
children,
|
|
362
|
+
/* @__PURE__ */ React4.createElement("svg", { width: bounds.width + PADDING * 2, height: bounds.height + PADDING * 2 }, /* @__PURE__ */ React4.createElement("defs", null, /* @__PURE__ */ React4.createElement(
|
|
363
|
+
"linearGradient",
|
|
364
|
+
{
|
|
365
|
+
x1: vertical ? "100%" : "0%",
|
|
366
|
+
y1: vertical ? "0%" : "100%",
|
|
367
|
+
x2: "100%",
|
|
368
|
+
y2: "100%",
|
|
369
|
+
id: strokeID,
|
|
370
|
+
gradientUnits: "userSpaceOnUse"
|
|
371
|
+
},
|
|
372
|
+
/* @__PURE__ */ React4.createElement("stop", { stopColor: color, offset: "0%" }),
|
|
373
|
+
/* @__PURE__ */ React4.createElement("stop", { stopColor: color, offset: "100%" })
|
|
374
|
+
)), /* @__PURE__ */ React4.createElement("g", null, path, /* @__PURE__ */ React4.createElement(
|
|
375
|
+
ArrowRenderer,
|
|
376
|
+
{
|
|
377
|
+
id: strokeID,
|
|
378
|
+
reverseArrow: reverse,
|
|
379
|
+
pos: reverse ? arrowFromPos : arrowToPos,
|
|
380
|
+
strokeWidth,
|
|
381
|
+
vertical,
|
|
382
|
+
hide: hideArrow
|
|
383
|
+
}
|
|
384
|
+
)))
|
|
385
|
+
);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// src/components/workflow-line-render/index.tsx
|
|
389
|
+
var WorkflowLineRender = memo(
|
|
390
|
+
LineSVG,
|
|
391
|
+
(prevProps, nextProps) => prevProps.version === nextProps.version
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// src/layer/workflow-lines-layer.tsx
|
|
395
|
+
var WorkflowLinesLayer = class extends Layer {
|
|
396
|
+
constructor() {
|
|
397
|
+
super(...arguments);
|
|
398
|
+
this.layerID = nanoid();
|
|
399
|
+
this.mountedLines = /* @__PURE__ */ new Map();
|
|
400
|
+
this._version = 0;
|
|
401
|
+
/**
|
|
402
|
+
* 节点线条
|
|
403
|
+
*/
|
|
404
|
+
this.node = domUtils.createDivWithClass("gedit-playground-layer gedit-flow-lines-layer");
|
|
405
|
+
}
|
|
406
|
+
onZoom(scale) {
|
|
407
|
+
this.node.style.transform = `scale(${scale})`;
|
|
408
|
+
}
|
|
409
|
+
onReady() {
|
|
410
|
+
this.pipelineNode.appendChild(this.node);
|
|
411
|
+
this.toDispose.pushAll([
|
|
412
|
+
this.selectService.onSelectionChanged(() => this.render()),
|
|
413
|
+
this.hoverService.onHoveredChange(() => this.render()),
|
|
414
|
+
this.workflowDocument.linesManager.onForceUpdate(() => {
|
|
415
|
+
this.mountedLines.clear();
|
|
416
|
+
this.bumpVersion();
|
|
417
|
+
this.render();
|
|
418
|
+
})
|
|
419
|
+
]);
|
|
420
|
+
}
|
|
421
|
+
dispose() {
|
|
422
|
+
this.mountedLines.clear();
|
|
423
|
+
}
|
|
424
|
+
render() {
|
|
425
|
+
const [, forceUpdate] = useState2({});
|
|
426
|
+
useLayoutEffect(() => {
|
|
427
|
+
const updateLines = () => {
|
|
428
|
+
let needsUpdate = false;
|
|
429
|
+
this.lines.forEach((line) => {
|
|
430
|
+
const renderData = line.getData(WorkflowLineRenderData2);
|
|
431
|
+
const oldVersion = renderData.renderVersion;
|
|
432
|
+
renderData.update();
|
|
433
|
+
if (renderData.renderVersion !== oldVersion) {
|
|
434
|
+
needsUpdate = true;
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
if (needsUpdate) {
|
|
438
|
+
forceUpdate({});
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
const rafId = requestAnimationFrame(updateLines);
|
|
442
|
+
return () => cancelAnimationFrame(rafId);
|
|
443
|
+
}, [this.lines]);
|
|
444
|
+
const lines = this.lines.map((line) => this.renderLine(line));
|
|
445
|
+
return /* @__PURE__ */ React5.createElement(React5.Fragment, null, lines);
|
|
446
|
+
}
|
|
447
|
+
// 用来绕过 memo
|
|
448
|
+
bumpVersion() {
|
|
449
|
+
this._version = this._version + 1;
|
|
450
|
+
if (this._version === Number.MAX_SAFE_INTEGER) {
|
|
451
|
+
this._version = 0;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
lineProps(line) {
|
|
455
|
+
const renderData = line.getData(WorkflowLineRenderData2);
|
|
456
|
+
const { renderVersion } = renderData;
|
|
457
|
+
const { lineType } = this.workflowDocument.linesManager;
|
|
458
|
+
const selected = this.selectService.isSelected(line.id);
|
|
459
|
+
const { version: lineVersion, color } = line;
|
|
460
|
+
const version = `${this._version}:${lineVersion}:${renderVersion}:${color}:${selected}`;
|
|
461
|
+
return {
|
|
462
|
+
key: line.id,
|
|
463
|
+
color: line.color,
|
|
464
|
+
selected,
|
|
465
|
+
line,
|
|
466
|
+
lineType,
|
|
467
|
+
version,
|
|
468
|
+
strokePrefix: this.layerID
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
lineComponent(props) {
|
|
472
|
+
const RenderInsideLine = this.options.renderInsideLine ?? (() => /* @__PURE__ */ React5.createElement(React5.Fragment, null));
|
|
473
|
+
return /* @__PURE__ */ React5.createElement(WorkflowLineRender, { ...props }, /* @__PURE__ */ React5.createElement(RenderInsideLine, { ...props }));
|
|
474
|
+
}
|
|
475
|
+
renderLine(line) {
|
|
476
|
+
const lineProps = this.lineProps(line);
|
|
477
|
+
const cache = this.mountedLines.get(line.id);
|
|
478
|
+
const isCached = cache !== void 0;
|
|
479
|
+
const { portal: cachedPortal, version: cachedVersion } = cache ?? {};
|
|
480
|
+
if (isCached && cachedVersion === lineProps.version) {
|
|
481
|
+
return cachedPortal;
|
|
482
|
+
}
|
|
483
|
+
if (!isCached) {
|
|
484
|
+
this.renderElement.appendChild(line.node);
|
|
485
|
+
line.onDispose(() => {
|
|
486
|
+
this.mountedLines.delete(line.id);
|
|
487
|
+
line.node.remove();
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
const portal = ReactDOM2.createPortal(this.lineComponent(lineProps), line.node);
|
|
491
|
+
this.mountedLines.set(line.id, { line, portal, version: lineProps.version });
|
|
492
|
+
return portal;
|
|
493
|
+
}
|
|
494
|
+
get renderElement() {
|
|
495
|
+
return this.stackContext.node;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
WorkflowLinesLayer.type = "WorkflowLinesLayer";
|
|
499
|
+
__decorateClass([
|
|
500
|
+
inject(WorkflowHoverService2)
|
|
501
|
+
], WorkflowLinesLayer.prototype, "hoverService", 2);
|
|
502
|
+
__decorateClass([
|
|
503
|
+
inject(WorkflowSelectService)
|
|
504
|
+
], WorkflowLinesLayer.prototype, "selectService", 2);
|
|
505
|
+
__decorateClass([
|
|
506
|
+
inject(StackingContextManager)
|
|
507
|
+
], WorkflowLinesLayer.prototype, "stackContext", 2);
|
|
508
|
+
__decorateClass([
|
|
509
|
+
observeEntities(WorkflowLineEntity)
|
|
510
|
+
], WorkflowLinesLayer.prototype, "lines", 2);
|
|
511
|
+
__decorateClass([
|
|
512
|
+
observeEntities(WorkflowPortEntity)
|
|
513
|
+
], WorkflowLinesLayer.prototype, "ports", 2);
|
|
514
|
+
__decorateClass([
|
|
515
|
+
observeEntityDatas(WorkflowNodeEntity, TransformData)
|
|
516
|
+
], WorkflowLinesLayer.prototype, "trans", 2);
|
|
517
|
+
__decorateClass([
|
|
518
|
+
inject(WorkflowDocument)
|
|
519
|
+
], WorkflowLinesLayer.prototype, "workflowDocument", 2);
|
|
520
|
+
WorkflowLinesLayer = __decorateClass([
|
|
521
|
+
injectable()
|
|
522
|
+
], WorkflowLinesLayer);
|
|
523
|
+
|
|
524
|
+
// src/contributions/bezier/bezier-controls.ts
|
|
525
|
+
import { Rectangle } from "@flowgram.ai/utils";
|
|
526
|
+
var BezierControlType = /* @__PURE__ */ ((BezierControlType2) => {
|
|
527
|
+
BezierControlType2[BezierControlType2["RIGHT_TOP"] = 0] = "RIGHT_TOP";
|
|
528
|
+
BezierControlType2[BezierControlType2["RIGHT_BOTTOM"] = 1] = "RIGHT_BOTTOM";
|
|
529
|
+
BezierControlType2[BezierControlType2["LEFT_TOP"] = 2] = "LEFT_TOP";
|
|
530
|
+
BezierControlType2[BezierControlType2["LEFT_BOTTOM"] = 3] = "LEFT_BOTTOM";
|
|
531
|
+
return BezierControlType2;
|
|
532
|
+
})(BezierControlType || {});
|
|
533
|
+
var CONTROL_MAX = 300;
|
|
534
|
+
function getBezierHorizontalControlPoints(fromPos, toPos) {
|
|
535
|
+
const rect = Rectangle.createRectangleWithTwoPoints(fromPos, toPos);
|
|
536
|
+
let type;
|
|
537
|
+
if (fromPos.x <= toPos.x) {
|
|
538
|
+
type = fromPos.y <= toPos.y ? 1 /* RIGHT_BOTTOM */ : 0 /* RIGHT_TOP */;
|
|
539
|
+
} else {
|
|
540
|
+
type = fromPos.y <= toPos.y ? 3 /* LEFT_BOTTOM */ : 2 /* LEFT_TOP */;
|
|
541
|
+
}
|
|
542
|
+
let controls;
|
|
543
|
+
switch (type) {
|
|
544
|
+
case 0 /* RIGHT_TOP */:
|
|
545
|
+
controls = [
|
|
546
|
+
{
|
|
547
|
+
x: rect.rightBottom.x - rect.width / 2,
|
|
548
|
+
y: rect.rightBottom.y
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
x: rect.leftTop.x + rect.width / 2,
|
|
552
|
+
y: rect.leftTop.y
|
|
553
|
+
}
|
|
554
|
+
];
|
|
555
|
+
break;
|
|
556
|
+
case 1 /* RIGHT_BOTTOM */:
|
|
557
|
+
controls = [
|
|
558
|
+
{
|
|
559
|
+
x: rect.rightTop.x - rect.width / 2,
|
|
560
|
+
y: rect.rightTop.y
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
x: rect.leftBottom.x + rect.width / 2,
|
|
564
|
+
y: rect.leftBottom.y
|
|
565
|
+
}
|
|
566
|
+
];
|
|
567
|
+
break;
|
|
568
|
+
case 2 /* LEFT_TOP */:
|
|
569
|
+
controls = [
|
|
570
|
+
{
|
|
571
|
+
x: rect.rightBottom.x + Math.min(rect.width, CONTROL_MAX),
|
|
572
|
+
y: rect.rightBottom.y
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
x: rect.leftTop.x - Math.min(rect.width, CONTROL_MAX),
|
|
576
|
+
y: rect.leftTop.y
|
|
577
|
+
}
|
|
578
|
+
];
|
|
579
|
+
break;
|
|
580
|
+
case 3 /* LEFT_BOTTOM */:
|
|
581
|
+
controls = [
|
|
582
|
+
{
|
|
583
|
+
x: rect.rightTop.x + Math.min(rect.width, CONTROL_MAX),
|
|
584
|
+
y: rect.rightTop.y
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
x: rect.leftBottom.x - Math.min(rect.width, CONTROL_MAX),
|
|
588
|
+
y: rect.leftBottom.y
|
|
589
|
+
}
|
|
590
|
+
];
|
|
591
|
+
}
|
|
592
|
+
return controls;
|
|
593
|
+
}
|
|
594
|
+
function getBezierVerticalControlPoints(fromPos, toPos) {
|
|
595
|
+
const rect = Rectangle.createRectangleWithTwoPoints(fromPos, toPos);
|
|
596
|
+
let type;
|
|
597
|
+
if (fromPos.y <= toPos.y) {
|
|
598
|
+
type = fromPos.x <= toPos.x ? 1 /* RIGHT_BOTTOM */ : 3 /* LEFT_BOTTOM */;
|
|
599
|
+
} else {
|
|
600
|
+
type = fromPos.x <= toPos.x ? 0 /* RIGHT_TOP */ : 2 /* LEFT_TOP */;
|
|
601
|
+
}
|
|
602
|
+
let controls;
|
|
603
|
+
switch (type) {
|
|
604
|
+
case 1 /* RIGHT_BOTTOM */:
|
|
605
|
+
controls = [
|
|
606
|
+
{
|
|
607
|
+
x: rect.leftTop.x,
|
|
608
|
+
y: rect.leftTop.y + rect.height / 2
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
x: rect.rightBottom.x,
|
|
612
|
+
y: rect.rightBottom.y - rect.height / 2
|
|
613
|
+
}
|
|
614
|
+
];
|
|
615
|
+
break;
|
|
616
|
+
case 3 /* LEFT_BOTTOM */:
|
|
617
|
+
controls = [
|
|
618
|
+
{
|
|
619
|
+
x: rect.rightTop.x,
|
|
620
|
+
y: rect.rightTop.y + rect.height / 2
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
x: rect.leftBottom.x,
|
|
624
|
+
y: rect.leftBottom.y - rect.height / 2
|
|
625
|
+
}
|
|
626
|
+
];
|
|
627
|
+
break;
|
|
628
|
+
case 0 /* RIGHT_TOP */:
|
|
629
|
+
controls = [
|
|
630
|
+
{
|
|
631
|
+
x: rect.leftBottom.x,
|
|
632
|
+
y: rect.leftBottom.y + Math.min(rect.height, CONTROL_MAX)
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
x: rect.rightTop.x,
|
|
636
|
+
y: rect.rightTop.y - Math.min(rect.height, CONTROL_MAX)
|
|
637
|
+
}
|
|
638
|
+
];
|
|
639
|
+
break;
|
|
640
|
+
case 2 /* LEFT_TOP */:
|
|
641
|
+
controls = [
|
|
642
|
+
{
|
|
643
|
+
x: rect.rightBottom.x,
|
|
644
|
+
y: rect.rightBottom.y + Math.min(rect.height, CONTROL_MAX)
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
x: rect.leftTop.x,
|
|
648
|
+
y: rect.leftTop.y - Math.min(rect.height, CONTROL_MAX)
|
|
649
|
+
}
|
|
650
|
+
];
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
return controls;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// src/contributions/bezier/index.ts
|
|
657
|
+
import { Bezier } from "bezier-js";
|
|
658
|
+
import { Point, Rectangle as Rectangle2 } from "@flowgram.ai/utils";
|
|
659
|
+
import {
|
|
660
|
+
POINT_RADIUS as POINT_RADIUS2
|
|
661
|
+
} from "@flowgram.ai/free-layout-core";
|
|
662
|
+
import { LineType } from "@flowgram.ai/free-layout-core";
|
|
663
|
+
var WorkflowBezierLineContribution = class {
|
|
664
|
+
constructor(entity) {
|
|
665
|
+
this.entity = entity;
|
|
666
|
+
}
|
|
667
|
+
get path() {
|
|
668
|
+
return this.data?.path ?? "";
|
|
669
|
+
}
|
|
670
|
+
calcDistance(pos) {
|
|
671
|
+
if (!this.data) {
|
|
672
|
+
return Number.MAX_SAFE_INTEGER;
|
|
673
|
+
}
|
|
674
|
+
return Point.getDistance(pos, this.data.bezier.project(pos));
|
|
675
|
+
}
|
|
676
|
+
get bounds() {
|
|
677
|
+
if (!this.data) {
|
|
678
|
+
return new Rectangle2();
|
|
679
|
+
}
|
|
680
|
+
return this.data.bbox;
|
|
681
|
+
}
|
|
682
|
+
update(params) {
|
|
683
|
+
this.data = this.calcBezier(params.fromPos, params.toPos);
|
|
684
|
+
}
|
|
685
|
+
calcBezier(fromPos, toPos) {
|
|
686
|
+
const controls = this.entity.vertical ? getBezierVerticalControlPoints(fromPos, toPos) : getBezierHorizontalControlPoints(fromPos, toPos);
|
|
687
|
+
const bezier = new Bezier([fromPos, ...controls, toPos]);
|
|
688
|
+
const bbox = bezier.bbox();
|
|
689
|
+
const bboxBounds = new Rectangle2(
|
|
690
|
+
bbox.x.min,
|
|
691
|
+
bbox.y.min,
|
|
692
|
+
bbox.x.max - bbox.x.min,
|
|
693
|
+
bbox.y.max - bbox.y.min
|
|
694
|
+
);
|
|
695
|
+
const path = this.getPath({ bbox: bboxBounds, fromPos, toPos, controls });
|
|
696
|
+
this.data = {
|
|
697
|
+
fromPos,
|
|
698
|
+
toPos,
|
|
699
|
+
bezier,
|
|
700
|
+
bbox: bboxBounds,
|
|
701
|
+
controls,
|
|
702
|
+
path
|
|
703
|
+
};
|
|
704
|
+
return this.data;
|
|
705
|
+
}
|
|
706
|
+
getPath(params) {
|
|
707
|
+
const { bbox } = params;
|
|
708
|
+
const toRelative = (p) => ({
|
|
709
|
+
x: p.x - bbox.x + LINE_PADDING,
|
|
710
|
+
y: p.y - bbox.y + LINE_PADDING
|
|
711
|
+
});
|
|
712
|
+
const fromPos = toRelative(params.fromPos);
|
|
713
|
+
const toPos = toRelative(params.toPos);
|
|
714
|
+
const controls = params.controls.map((c) => toRelative(c));
|
|
715
|
+
const renderToPos = this.entity.vertical ? { x: toPos.x, y: toPos.y - POINT_RADIUS2 } : { x: toPos.x - POINT_RADIUS2, y: toPos.y };
|
|
716
|
+
const getPathData = () => {
|
|
717
|
+
const controlPoints = controls.map((s) => `${s.x} ${s.y}`).join(",");
|
|
718
|
+
const curveType = controls.length === 1 ? "S" : "C";
|
|
719
|
+
if (this.entity.vertical) {
|
|
720
|
+
return `M${fromPos.x} ${fromPos.y + POINT_RADIUS2} ${curveType} ${controlPoints}, ${renderToPos.x} ${renderToPos.y}`;
|
|
721
|
+
}
|
|
722
|
+
return `M${fromPos.x + POINT_RADIUS2} ${fromPos.y} ${curveType} ${controlPoints}, ${renderToPos.x} ${renderToPos.y}`;
|
|
723
|
+
};
|
|
724
|
+
const path = getPathData();
|
|
725
|
+
return path;
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
WorkflowBezierLineContribution.type = LineType.BEZIER;
|
|
729
|
+
|
|
730
|
+
// src/contributions/fold/index.ts
|
|
731
|
+
import { Rectangle as Rectangle4 } from "@flowgram.ai/utils";
|
|
732
|
+
import {
|
|
733
|
+
POINT_RADIUS as POINT_RADIUS3
|
|
734
|
+
} from "@flowgram.ai/free-layout-core";
|
|
735
|
+
import { LineType as LineType2 } from "@flowgram.ai/free-layout-core";
|
|
736
|
+
|
|
737
|
+
// src/contributions/fold/fold-line.ts
|
|
738
|
+
import { Point as Point2, Rectangle as Rectangle3 } from "@flowgram.ai/utils";
|
|
739
|
+
function pointLineDistance(p1, p2, p3) {
|
|
740
|
+
let len;
|
|
741
|
+
if (p1.x - p2.x === 0) {
|
|
742
|
+
len = Math.abs(p3.x - p1.x);
|
|
743
|
+
} else {
|
|
744
|
+
const A = (p1.y - p2.y) / (p1.x - p2.x);
|
|
745
|
+
const B = p1.y - A * p1.x;
|
|
746
|
+
len = Math.abs((A * p3.x + B - p3.y) / Math.sqrt(A * A + 1));
|
|
747
|
+
}
|
|
748
|
+
return len;
|
|
749
|
+
}
|
|
750
|
+
var FoldLine;
|
|
751
|
+
((FoldLine2) => {
|
|
752
|
+
const EDGE_RADIUS = 5;
|
|
753
|
+
const OFFSET = 20;
|
|
754
|
+
function getEdgeCenter({ source, target }) {
|
|
755
|
+
const xOffset = Math.abs(target.x - source.x) / 2;
|
|
756
|
+
const centerX = target.x < source.x ? target.x + xOffset : target.x - xOffset;
|
|
757
|
+
const yOffset = Math.abs(target.y - source.y) / 2;
|
|
758
|
+
const centerY = target.y < source.y ? target.y + yOffset : target.y - yOffset;
|
|
759
|
+
return [centerX, centerY];
|
|
760
|
+
}
|
|
761
|
+
const getDirection = ({ source, target }) => source.x < target.x ? { x: 1, y: 0 } : { x: -1, y: 0 };
|
|
762
|
+
function getPoints({
|
|
763
|
+
source,
|
|
764
|
+
target,
|
|
765
|
+
vertical = false
|
|
766
|
+
}) {
|
|
767
|
+
const sourceDir = vertical ? { x: 0, y: 1 } : { x: 1, y: 0 };
|
|
768
|
+
const targetDir = vertical ? { x: 0, y: -1 } : { x: -1, y: 0 };
|
|
769
|
+
const sourceGapped = {
|
|
770
|
+
x: source.x + sourceDir.x * OFFSET,
|
|
771
|
+
y: source.y + sourceDir.y * OFFSET
|
|
772
|
+
};
|
|
773
|
+
const targetGapped = {
|
|
774
|
+
x: target.x + targetDir.x * OFFSET,
|
|
775
|
+
y: target.y + targetDir.y * OFFSET
|
|
776
|
+
};
|
|
777
|
+
const dir = vertical ? { x: 0, y: sourceGapped.y < targetGapped.y ? 1 : -1 } : getDirection({ source: sourceGapped, target: targetGapped });
|
|
778
|
+
const dirAccessor = dir.x !== 0 ? "x" : "y";
|
|
779
|
+
const currDir = dir[dirAccessor];
|
|
780
|
+
let points = [];
|
|
781
|
+
let centerX, centerY;
|
|
782
|
+
const [defaultCenterX, defaultCenterY] = getEdgeCenter({
|
|
783
|
+
source,
|
|
784
|
+
target
|
|
785
|
+
});
|
|
786
|
+
if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
|
|
787
|
+
centerX = defaultCenterX;
|
|
788
|
+
centerY = defaultCenterY;
|
|
789
|
+
const verticalSplit = [
|
|
790
|
+
{ x: centerX, y: sourceGapped.y },
|
|
791
|
+
{ x: centerX, y: targetGapped.y }
|
|
792
|
+
];
|
|
793
|
+
const horizontalSplit = [
|
|
794
|
+
{ x: sourceGapped.x, y: centerY },
|
|
795
|
+
{ x: targetGapped.x, y: centerY }
|
|
796
|
+
];
|
|
797
|
+
if (sourceDir[dirAccessor] === currDir) {
|
|
798
|
+
points = dirAccessor === "x" ? verticalSplit : horizontalSplit;
|
|
799
|
+
} else {
|
|
800
|
+
points = dirAccessor === "x" ? horizontalSplit : verticalSplit;
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
const sourceTarget = [{ x: sourceGapped.x, y: targetGapped.y }];
|
|
804
|
+
const targetSource = [{ x: targetGapped.x, y: sourceGapped.y }];
|
|
805
|
+
if (dirAccessor === "x") {
|
|
806
|
+
points = sourceDir.x === currDir ? targetSource : sourceTarget;
|
|
807
|
+
} else {
|
|
808
|
+
points = sourceDir.y === currDir ? sourceTarget : targetSource;
|
|
809
|
+
}
|
|
810
|
+
const dirAccessorOpposite = dirAccessor === "x" ? "y" : "x";
|
|
811
|
+
const isSameDir = sourceDir[dirAccessor] === targetDir[dirAccessorOpposite];
|
|
812
|
+
const sourceGtTargetOppo = sourceGapped[dirAccessorOpposite] > targetGapped[dirAccessorOpposite];
|
|
813
|
+
const sourceLtTargetOppo = sourceGapped[dirAccessorOpposite] < targetGapped[dirAccessorOpposite];
|
|
814
|
+
const flipSourceTarget = sourceDir[dirAccessor] === 1 && (!isSameDir && sourceGtTargetOppo || isSameDir && sourceLtTargetOppo) || sourceDir[dirAccessor] !== 1 && (!isSameDir && sourceLtTargetOppo || isSameDir && sourceGtTargetOppo);
|
|
815
|
+
if (flipSourceTarget) {
|
|
816
|
+
points = dirAccessor === "x" ? sourceTarget : targetSource;
|
|
817
|
+
}
|
|
818
|
+
const sourceGapPoint = { x: sourceGapped.x, y: sourceGapped.y };
|
|
819
|
+
const targetGapPoint = { x: targetGapped.x, y: targetGapped.y };
|
|
820
|
+
const maxXDistance = Math.max(
|
|
821
|
+
Math.abs(sourceGapPoint.x - points[0].x),
|
|
822
|
+
Math.abs(targetGapPoint.x - points[0].x)
|
|
823
|
+
);
|
|
824
|
+
const maxYDistance = Math.max(
|
|
825
|
+
Math.abs(sourceGapPoint.y - points[0].y),
|
|
826
|
+
Math.abs(targetGapPoint.y - points[0].y)
|
|
827
|
+
);
|
|
828
|
+
if (maxXDistance >= maxYDistance) {
|
|
829
|
+
centerX = (sourceGapPoint.x + targetGapPoint.x) / 2;
|
|
830
|
+
centerY = points[0].y;
|
|
831
|
+
} else {
|
|
832
|
+
centerX = points[0].x;
|
|
833
|
+
centerY = (sourceGapPoint.y + targetGapPoint.y) / 2;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
const pathPoints = [
|
|
837
|
+
source,
|
|
838
|
+
{ x: sourceGapped.x, y: sourceGapped.y },
|
|
839
|
+
...points,
|
|
840
|
+
{ x: targetGapped.x, y: targetGapped.y },
|
|
841
|
+
target
|
|
842
|
+
];
|
|
843
|
+
return pathPoints;
|
|
844
|
+
}
|
|
845
|
+
FoldLine2.getPoints = getPoints;
|
|
846
|
+
function getBend(a, b, c) {
|
|
847
|
+
const bendSize = Math.min(
|
|
848
|
+
Point2.getDistance(a, b) / 2,
|
|
849
|
+
Point2.getDistance(b, c) / 2,
|
|
850
|
+
EDGE_RADIUS
|
|
851
|
+
);
|
|
852
|
+
const { x, y } = b;
|
|
853
|
+
if (a.x === x && x === c.x || a.y === y && y === c.y) {
|
|
854
|
+
return `L${x} ${y}`;
|
|
855
|
+
}
|
|
856
|
+
if (a.y === y) {
|
|
857
|
+
const xDir2 = a.x < c.x ? -1 : 1;
|
|
858
|
+
const yDir2 = a.y < c.y ? 1 : -1;
|
|
859
|
+
return `L ${x + bendSize * xDir2},${y}Q ${x},${y} ${x},${y + bendSize * yDir2}`;
|
|
860
|
+
}
|
|
861
|
+
const xDir = a.x < c.x ? 1 : -1;
|
|
862
|
+
const yDir = a.y < c.y ? -1 : 1;
|
|
863
|
+
return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`;
|
|
864
|
+
}
|
|
865
|
+
function getSmoothStepPath(points) {
|
|
866
|
+
const path = points.reduce((res, p, i) => {
|
|
867
|
+
let segment = "";
|
|
868
|
+
if (i > 0 && i < points.length - 1) {
|
|
869
|
+
segment = getBend(points[i - 1], p, points[i + 1]);
|
|
870
|
+
} else {
|
|
871
|
+
segment = `${i === 0 ? "M" : "L"}${p.x} ${p.y}`;
|
|
872
|
+
}
|
|
873
|
+
res += segment;
|
|
874
|
+
return res;
|
|
875
|
+
}, "");
|
|
876
|
+
return path;
|
|
877
|
+
}
|
|
878
|
+
FoldLine2.getSmoothStepPath = getSmoothStepPath;
|
|
879
|
+
function getBounds(points) {
|
|
880
|
+
const xList = points.map((p) => p.x);
|
|
881
|
+
const yList = points.map((p) => p.y);
|
|
882
|
+
const left = Math.min(...xList);
|
|
883
|
+
const right = Math.max(...xList);
|
|
884
|
+
const top = Math.min(...yList);
|
|
885
|
+
const bottom = Math.max(...yList);
|
|
886
|
+
return Rectangle3.createRectangleWithTwoPoints(
|
|
887
|
+
{
|
|
888
|
+
x: left,
|
|
889
|
+
y: top
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
x: right,
|
|
893
|
+
y: bottom
|
|
894
|
+
}
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
FoldLine2.getBounds = getBounds;
|
|
898
|
+
function getFoldLineToPointDistance(points, pos) {
|
|
899
|
+
const bounds = getBounds(points);
|
|
900
|
+
if (bounds.contains(pos.x, pos.y)) {
|
|
901
|
+
const lines = points.reduce((res, point, index) => {
|
|
902
|
+
if (index === 0) {
|
|
903
|
+
return res;
|
|
904
|
+
}
|
|
905
|
+
res.push([points[index - 1], point]);
|
|
906
|
+
return res;
|
|
907
|
+
}, []);
|
|
908
|
+
return Math.min(...lines.map((l) => pointLineDistance(...l, pos)));
|
|
909
|
+
}
|
|
910
|
+
return Math.min(...points.map((p) => Point2.getDistance(p, pos)));
|
|
911
|
+
}
|
|
912
|
+
FoldLine2.getFoldLineToPointDistance = getFoldLineToPointDistance;
|
|
913
|
+
})(FoldLine || (FoldLine = {}));
|
|
914
|
+
|
|
915
|
+
// src/contributions/fold/index.ts
|
|
916
|
+
var WorkflowFoldLineContribution = class {
|
|
917
|
+
constructor(entity) {
|
|
918
|
+
this.entity = entity;
|
|
919
|
+
}
|
|
920
|
+
get path() {
|
|
921
|
+
return this.data?.path ?? "";
|
|
922
|
+
}
|
|
923
|
+
calcDistance(pos) {
|
|
924
|
+
if (!this.data) {
|
|
925
|
+
return Number.MAX_SAFE_INTEGER;
|
|
926
|
+
}
|
|
927
|
+
return FoldLine.getFoldLineToPointDistance(this.data.points, pos);
|
|
928
|
+
}
|
|
929
|
+
get bounds() {
|
|
930
|
+
if (!this.data) {
|
|
931
|
+
return new Rectangle4();
|
|
932
|
+
}
|
|
933
|
+
return this.data.bbox;
|
|
934
|
+
}
|
|
935
|
+
update(params) {
|
|
936
|
+
const { fromPos, toPos } = params;
|
|
937
|
+
const { vertical } = this.entity;
|
|
938
|
+
const sourceOffset = {
|
|
939
|
+
x: vertical ? 0 : POINT_RADIUS3,
|
|
940
|
+
y: vertical ? POINT_RADIUS3 : 0
|
|
941
|
+
};
|
|
942
|
+
const targetOffset = {
|
|
943
|
+
x: vertical ? 0 : -POINT_RADIUS3,
|
|
944
|
+
y: vertical ? -POINT_RADIUS3 : 0
|
|
945
|
+
};
|
|
946
|
+
const points = FoldLine.getPoints({
|
|
947
|
+
source: {
|
|
948
|
+
x: fromPos.x + sourceOffset.x,
|
|
949
|
+
y: fromPos.y + sourceOffset.y
|
|
950
|
+
},
|
|
951
|
+
target: {
|
|
952
|
+
x: toPos.x + targetOffset.x,
|
|
953
|
+
y: toPos.y + targetOffset.y
|
|
954
|
+
},
|
|
955
|
+
vertical
|
|
956
|
+
});
|
|
957
|
+
const bbox = FoldLine.getBounds(points);
|
|
958
|
+
const adjustedPoints = points.map((p) => ({
|
|
959
|
+
x: p.x - bbox.x + LINE_PADDING,
|
|
960
|
+
y: p.y - bbox.y + LINE_PADDING
|
|
961
|
+
}));
|
|
962
|
+
const path = FoldLine.getSmoothStepPath(adjustedPoints);
|
|
963
|
+
this.data = {
|
|
964
|
+
points,
|
|
965
|
+
path,
|
|
966
|
+
bbox
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
WorkflowFoldLineContribution.type = LineType2.LINE_CHART;
|
|
971
|
+
|
|
972
|
+
// src/contributions/straight/index.ts
|
|
973
|
+
import { Point as Point3, Rectangle as Rectangle5 } from "@flowgram.ai/utils";
|
|
974
|
+
import {
|
|
975
|
+
POINT_RADIUS as POINT_RADIUS4
|
|
976
|
+
} from "@flowgram.ai/free-layout-core";
|
|
977
|
+
|
|
978
|
+
// src/contributions/straight/point-on-line.ts
|
|
979
|
+
function projectPointOnLine(point, lineStart, lineEnd) {
|
|
980
|
+
const dx = lineEnd.x - lineStart.x;
|
|
981
|
+
const dy = lineEnd.y - lineStart.y;
|
|
982
|
+
if (dx === 0) {
|
|
983
|
+
return { x: lineStart.x, y: point.y };
|
|
984
|
+
}
|
|
985
|
+
if (dy === 0) {
|
|
986
|
+
return { x: point.x, y: lineStart.y };
|
|
987
|
+
}
|
|
988
|
+
const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (dx * dx + dy * dy);
|
|
989
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
990
|
+
return {
|
|
991
|
+
x: lineStart.x + clampedT * dx,
|
|
992
|
+
y: lineStart.y + clampedT * dy
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// src/contributions/straight/index.ts
|
|
997
|
+
var WorkflowStraightLineContribution = class {
|
|
998
|
+
constructor(entity) {
|
|
999
|
+
this.entity = entity;
|
|
1000
|
+
}
|
|
1001
|
+
get path() {
|
|
1002
|
+
return this.data?.path ?? "";
|
|
1003
|
+
}
|
|
1004
|
+
calcDistance(pos) {
|
|
1005
|
+
if (!this.data) {
|
|
1006
|
+
return Number.MAX_SAFE_INTEGER;
|
|
1007
|
+
}
|
|
1008
|
+
const [start, end] = this.data.points;
|
|
1009
|
+
return Point3.getDistance(pos, projectPointOnLine(pos, start, end));
|
|
1010
|
+
}
|
|
1011
|
+
get bounds() {
|
|
1012
|
+
if (!this.data) {
|
|
1013
|
+
return new Rectangle5();
|
|
1014
|
+
}
|
|
1015
|
+
return this.data.bbox;
|
|
1016
|
+
}
|
|
1017
|
+
update(params) {
|
|
1018
|
+
const { fromPos, toPos } = params;
|
|
1019
|
+
const { vertical } = this.entity;
|
|
1020
|
+
const sourceOffset = {
|
|
1021
|
+
x: vertical ? 0 : POINT_RADIUS4,
|
|
1022
|
+
y: vertical ? POINT_RADIUS4 : 0
|
|
1023
|
+
};
|
|
1024
|
+
const targetOffset = {
|
|
1025
|
+
x: vertical ? 0 : -POINT_RADIUS4,
|
|
1026
|
+
y: vertical ? -POINT_RADIUS4 : 0
|
|
1027
|
+
};
|
|
1028
|
+
const points = [
|
|
1029
|
+
{
|
|
1030
|
+
x: fromPos.x + sourceOffset.x,
|
|
1031
|
+
y: fromPos.y + sourceOffset.y
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
x: toPos.x + targetOffset.x,
|
|
1035
|
+
y: toPos.y + targetOffset.y
|
|
1036
|
+
}
|
|
1037
|
+
];
|
|
1038
|
+
const bbox = Rectangle5.createRectangleWithTwoPoints(points[0], points[1]);
|
|
1039
|
+
const adjustedPoints = points.map((p) => ({
|
|
1040
|
+
x: p.x - bbox.x + LINE_PADDING,
|
|
1041
|
+
y: p.y - bbox.y + LINE_PADDING
|
|
1042
|
+
}));
|
|
1043
|
+
const path = `M ${adjustedPoints[0].x} ${adjustedPoints[0].y} L ${adjustedPoints[1].x} ${adjustedPoints[1].y}`;
|
|
1044
|
+
this.data = {
|
|
1045
|
+
points,
|
|
1046
|
+
path,
|
|
1047
|
+
bbox
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
WorkflowStraightLineContribution.type = "WorkflowStraightLineContribution";
|
|
1052
|
+
|
|
1053
|
+
// src/contributions/arc/index.ts
|
|
1054
|
+
import { Point as Point4, Rectangle as Rectangle6 } from "@flowgram.ai/utils";
|
|
1055
|
+
import {
|
|
1056
|
+
POINT_RADIUS as POINT_RADIUS5
|
|
1057
|
+
} from "@flowgram.ai/free-layout-core";
|
|
1058
|
+
var WorkflowArkLineContribution = class {
|
|
1059
|
+
constructor(entity) {
|
|
1060
|
+
this.entity = entity;
|
|
1061
|
+
}
|
|
1062
|
+
get path() {
|
|
1063
|
+
return this.data?.path ?? "";
|
|
1064
|
+
}
|
|
1065
|
+
calcDistance(pos) {
|
|
1066
|
+
if (!this.data) {
|
|
1067
|
+
return Number.MAX_SAFE_INTEGER;
|
|
1068
|
+
}
|
|
1069
|
+
const { fromPos, toPos, bbox } = this.data;
|
|
1070
|
+
if (!bbox.contains(pos.x, pos.y)) {
|
|
1071
|
+
const dx = Math.max(bbox.x - pos.x, 0, pos.x - (bbox.x + bbox.width));
|
|
1072
|
+
const dy = Math.max(bbox.y - pos.y, 0, pos.y - (bbox.y + bbox.height));
|
|
1073
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1074
|
+
}
|
|
1075
|
+
const center = {
|
|
1076
|
+
x: (fromPos.x + toPos.x) / 2,
|
|
1077
|
+
y: (fromPos.y + toPos.y) / 2
|
|
1078
|
+
};
|
|
1079
|
+
const radius = Point4.getDistance(fromPos, center);
|
|
1080
|
+
const distanceToCenter = Point4.getDistance(pos, center);
|
|
1081
|
+
return Math.abs(distanceToCenter - radius);
|
|
1082
|
+
}
|
|
1083
|
+
get bounds() {
|
|
1084
|
+
if (!this.data) {
|
|
1085
|
+
return new Rectangle6();
|
|
1086
|
+
}
|
|
1087
|
+
return this.data.bbox;
|
|
1088
|
+
}
|
|
1089
|
+
update(params) {
|
|
1090
|
+
const { fromPos, toPos } = params;
|
|
1091
|
+
const { vertical } = this.entity;
|
|
1092
|
+
const sourceOffset = {
|
|
1093
|
+
x: vertical ? 0 : POINT_RADIUS5,
|
|
1094
|
+
y: vertical ? POINT_RADIUS5 : 0
|
|
1095
|
+
};
|
|
1096
|
+
const targetOffset = {
|
|
1097
|
+
x: vertical ? 0 : -POINT_RADIUS5,
|
|
1098
|
+
y: vertical ? -POINT_RADIUS5 : 0
|
|
1099
|
+
};
|
|
1100
|
+
const start = {
|
|
1101
|
+
x: fromPos.x + sourceOffset.x,
|
|
1102
|
+
y: fromPos.y + sourceOffset.y
|
|
1103
|
+
};
|
|
1104
|
+
const end = {
|
|
1105
|
+
x: toPos.x + targetOffset.x,
|
|
1106
|
+
y: toPos.y + targetOffset.y
|
|
1107
|
+
};
|
|
1108
|
+
const bbox = this.calculateArcBBox(start, end);
|
|
1109
|
+
const path = this.getArcPath(start, end, bbox);
|
|
1110
|
+
this.data = {
|
|
1111
|
+
fromPos: start,
|
|
1112
|
+
toPos: end,
|
|
1113
|
+
path,
|
|
1114
|
+
bbox
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
calculateArcBBox(start, end) {
|
|
1118
|
+
const dx = end.x - start.x;
|
|
1119
|
+
const dy = end.y - start.y;
|
|
1120
|
+
const radius = Math.sqrt(dx * dx + dy * dy) / 2;
|
|
1121
|
+
const centerX = (start.x + end.x) / 2;
|
|
1122
|
+
const centerY = (start.y + end.y) / 2;
|
|
1123
|
+
return new Rectangle6(centerX - radius, centerY - radius, radius * 2, radius * 2);
|
|
1124
|
+
}
|
|
1125
|
+
getArcPath(start, end, bbox) {
|
|
1126
|
+
const dx = end.x - start.x;
|
|
1127
|
+
const dy = end.y - start.y;
|
|
1128
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
1129
|
+
const startRel = {
|
|
1130
|
+
x: start.x - bbox.x + LINE_PADDING,
|
|
1131
|
+
y: start.y - bbox.y + LINE_PADDING
|
|
1132
|
+
};
|
|
1133
|
+
const endRel = {
|
|
1134
|
+
x: end.x - bbox.x + LINE_PADDING,
|
|
1135
|
+
y: end.y - bbox.y + LINE_PADDING
|
|
1136
|
+
};
|
|
1137
|
+
return `M ${startRel.x} ${startRel.y} A ${distance / 2} ${distance / 2} 0 0 1 ${endRel.x} ${endRel.y}`;
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
WorkflowArkLineContribution.type = "WorkflowArkLineContribution";
|
|
1141
|
+
|
|
1142
|
+
// src/contributions/manhattan/index.ts
|
|
1143
|
+
import { Point as Point5, Rectangle as Rectangle7 } from "@flowgram.ai/utils";
|
|
1144
|
+
import {
|
|
1145
|
+
POINT_RADIUS as POINT_RADIUS6
|
|
1146
|
+
} from "@flowgram.ai/free-layout-core";
|
|
1147
|
+
var WorkflowManhattanLineContribution = class {
|
|
1148
|
+
constructor(entity) {
|
|
1149
|
+
this.entity = entity;
|
|
1150
|
+
}
|
|
1151
|
+
get path() {
|
|
1152
|
+
return this.data?.path ?? "";
|
|
1153
|
+
}
|
|
1154
|
+
calcDistance(pos) {
|
|
1155
|
+
if (!this.data) {
|
|
1156
|
+
return Number.MAX_SAFE_INTEGER;
|
|
1157
|
+
}
|
|
1158
|
+
return Math.min(
|
|
1159
|
+
...this.data.points.slice(1).map((point, index) => {
|
|
1160
|
+
const prevPoint = this.data.points[index];
|
|
1161
|
+
return this.getDistanceToLineSegment(pos, prevPoint, point);
|
|
1162
|
+
})
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
getDistanceToLineSegment(point, start, end) {
|
|
1166
|
+
const dx = end.x - start.x;
|
|
1167
|
+
const dy = end.y - start.y;
|
|
1168
|
+
if (dx === 0 && dy === 0) {
|
|
1169
|
+
return Point5.getDistance(point, start);
|
|
1170
|
+
}
|
|
1171
|
+
const t = ((point.x - start.x) * dx + (point.y - start.y) * dy) / (dx * dx + dy * dy);
|
|
1172
|
+
if (t < 0) return Point5.getDistance(point, start);
|
|
1173
|
+
if (t > 1) return Point5.getDistance(point, end);
|
|
1174
|
+
const projectionPoint = {
|
|
1175
|
+
x: start.x + t * dx,
|
|
1176
|
+
y: start.y + t * dy
|
|
1177
|
+
};
|
|
1178
|
+
return Point5.getDistance(point, projectionPoint);
|
|
1179
|
+
}
|
|
1180
|
+
get bounds() {
|
|
1181
|
+
if (!this.data) {
|
|
1182
|
+
return new Rectangle7();
|
|
1183
|
+
}
|
|
1184
|
+
return this.data.bbox;
|
|
1185
|
+
}
|
|
1186
|
+
update(params) {
|
|
1187
|
+
const { fromPos, toPos } = params;
|
|
1188
|
+
const { vertical } = this.entity;
|
|
1189
|
+
const sourceOffset = {
|
|
1190
|
+
x: vertical ? 0 : POINT_RADIUS6,
|
|
1191
|
+
y: vertical ? POINT_RADIUS6 : 0
|
|
1192
|
+
};
|
|
1193
|
+
const targetOffset = {
|
|
1194
|
+
x: vertical ? 0 : -POINT_RADIUS6,
|
|
1195
|
+
y: vertical ? -POINT_RADIUS6 : 0
|
|
1196
|
+
};
|
|
1197
|
+
const points = this.getManhattanPoints({
|
|
1198
|
+
source: {
|
|
1199
|
+
x: fromPos.x + sourceOffset.x,
|
|
1200
|
+
y: fromPos.y + sourceOffset.y
|
|
1201
|
+
},
|
|
1202
|
+
target: {
|
|
1203
|
+
x: toPos.x + targetOffset.x,
|
|
1204
|
+
y: toPos.y + targetOffset.y
|
|
1205
|
+
},
|
|
1206
|
+
vertical
|
|
1207
|
+
});
|
|
1208
|
+
const bbox = Rectangle7.createRectangleWithTwoPoints(
|
|
1209
|
+
points.reduce(
|
|
1210
|
+
(min, p) => ({
|
|
1211
|
+
x: Math.min(min.x, p.x),
|
|
1212
|
+
y: Math.min(min.y, p.y)
|
|
1213
|
+
}),
|
|
1214
|
+
points[0]
|
|
1215
|
+
),
|
|
1216
|
+
points.reduce(
|
|
1217
|
+
(max, p) => ({
|
|
1218
|
+
x: Math.max(max.x, p.x),
|
|
1219
|
+
y: Math.max(max.y, p.y)
|
|
1220
|
+
}),
|
|
1221
|
+
points[0]
|
|
1222
|
+
)
|
|
1223
|
+
);
|
|
1224
|
+
const adjustedPoints = points.map((p) => ({
|
|
1225
|
+
x: p.x - bbox.x + LINE_PADDING,
|
|
1226
|
+
y: p.y - bbox.y + LINE_PADDING
|
|
1227
|
+
}));
|
|
1228
|
+
const path = this.getPathFromPoints(adjustedPoints);
|
|
1229
|
+
this.data = {
|
|
1230
|
+
points,
|
|
1231
|
+
path,
|
|
1232
|
+
bbox
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
getManhattanPoints(params) {
|
|
1236
|
+
const { source, target, vertical } = params;
|
|
1237
|
+
const points = [source];
|
|
1238
|
+
if (vertical) {
|
|
1239
|
+
if (source.y !== target.y) {
|
|
1240
|
+
points.push({ x: source.x, y: target.y });
|
|
1241
|
+
}
|
|
1242
|
+
if (source.x !== target.x) {
|
|
1243
|
+
points.push({ x: target.x, y: target.y });
|
|
1244
|
+
}
|
|
1245
|
+
} else {
|
|
1246
|
+
if (source.x !== target.x) {
|
|
1247
|
+
points.push({ x: target.x, y: source.y });
|
|
1248
|
+
}
|
|
1249
|
+
if (source.y !== target.y) {
|
|
1250
|
+
points.push({ x: target.x, y: target.y });
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (points[points.length - 1] !== target) {
|
|
1254
|
+
points.push(target);
|
|
1255
|
+
}
|
|
1256
|
+
return points;
|
|
1257
|
+
}
|
|
1258
|
+
getPathFromPoints(points) {
|
|
1259
|
+
return points.reduce((path, point, index) => {
|
|
1260
|
+
if (index === 0) {
|
|
1261
|
+
return `M ${point.x} ${point.y}`;
|
|
1262
|
+
}
|
|
1263
|
+
return `${path} L ${point.x} ${point.y}`;
|
|
1264
|
+
}, "");
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
WorkflowManhattanLineContribution.type = "WorkflowManhattanLineContribution";
|
|
1268
|
+
|
|
1269
|
+
// src/create-free-lines-plugin.ts
|
|
1270
|
+
var createFreeLinesPlugin = definePluginCreator({
|
|
1271
|
+
onInit: (ctx, opts) => {
|
|
1272
|
+
ctx.playground.registerLayer(WorkflowLinesLayer, {
|
|
1273
|
+
...opts
|
|
1274
|
+
});
|
|
1275
|
+
},
|
|
1276
|
+
onReady: (ctx, opts) => {
|
|
1277
|
+
const linesManager = ctx.container.get(WorkflowLinesManager2);
|
|
1278
|
+
linesManager.registerContribution(WorkflowBezierLineContribution).registerContribution(WorkflowFoldLineContribution);
|
|
1279
|
+
if (opts.contributions) {
|
|
1280
|
+
opts.contributions.forEach((contribution) => {
|
|
1281
|
+
linesManager.registerContribution(contribution);
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
if (opts.defaultLineType) {
|
|
1285
|
+
linesManager.switchLineType(opts.defaultLineType);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
export {
|
|
1290
|
+
BezierControlType,
|
|
1291
|
+
LINE_OFFSET,
|
|
1292
|
+
LINE_PADDING,
|
|
1293
|
+
WorkflowLinesLayer as LinesLayer,
|
|
1294
|
+
WorkflowArkLineContribution,
|
|
1295
|
+
WorkflowBezierLineContribution,
|
|
1296
|
+
WorkflowFoldLineContribution,
|
|
1297
|
+
WorkflowLinesLayer,
|
|
1298
|
+
WorkflowManhattanLineContribution,
|
|
1299
|
+
WorkflowPortRender,
|
|
1300
|
+
WorkflowStraightLineContribution,
|
|
1301
|
+
createFreeLinesPlugin,
|
|
1302
|
+
getBezierHorizontalControlPoints,
|
|
1303
|
+
getBezierVerticalControlPoints
|
|
1304
|
+
};
|
|
1305
|
+
//# sourceMappingURL=index.js.map
|