@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.
@@ -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