@kubex/zinc 1.1.28 → 1.1.31
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/custom-elements-manifest.config.js +2 -2
- package/dist/custom-elements.json +1910 -362
- package/dist/vscode.html-custom-data.json +55 -14
- package/dist/web-types.json +185 -32
- package/dist/zn.d.ts +684 -16
- package/dist/zn.min.js +1325 -929
- package/docs/pages/components/flow-builder-troubleshooter-demo.njk +106 -78
- package/docs/pages/components/flow-builder.md +422 -66
- package/docs/pages/components/page-builder.md +167 -0
- package/docs/pages/components/settings-container.md +37 -2
- package/package.json +1 -1
- package/src/components/datepicker/datepicker.scss +0 -10
- package/src/components/flow-builder/flow-builder.component.ts +377 -16
- package/src/components/flow-builder/flow-builder.scss +398 -250
- package/src/components/flow-builder/flow-builder.test.ts +241 -4
- package/src/components/flow-builder/flow-layout.ts +42 -17
- package/src/components/flow-builder/flow.types.ts +168 -43
- package/src/components/flow-builder/modules/flow-branch-conditions/flow-branch-conditions.component.ts +300 -0
- package/src/components/flow-builder/modules/flow-branch-conditions/flow-branch-conditions.scss +222 -0
- package/src/components/flow-builder/modules/flow-branch-conditions/flow-branch-conditions.test.ts +125 -0
- package/src/components/flow-builder/modules/flow-branch-conditions/index.ts +12 -0
- package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +184 -26
- package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +170 -120
- package/src/components/flow-builder/modules/flow-node/flow-node.scss +42 -42
- package/src/components/flow-builder/modules/flow-step/flow-step.component.ts +2 -1
- package/src/components/flow-builder/modules/flow-step/flow-step.scss +25 -17
- package/src/components/header/header.scss +3 -1
- package/src/components/icon-picker/icon-picker.component.ts +20 -37
- package/src/components/icon-picker/icon-picker.scss +5 -45
- package/src/components/page-builder/index.ts +14 -0
- package/src/components/page-builder/modules/page-palette-item/index.ts +12 -0
- package/src/components/page-builder/modules/page-palette-item/page-palette-item.component.ts +71 -0
- package/src/components/page-builder/modules/page-palette-item/page-palette-item.scss +70 -0
- package/src/components/page-builder/modules/page-palette-item/page-palette-item.test.ts +20 -0
- package/src/components/page-builder/modules/page-section-card/index.ts +12 -0
- package/src/components/page-builder/modules/page-section-card/page-section-card.component.ts +93 -0
- package/src/components/page-builder/modules/page-section-card/page-section-card.scss +92 -0
- package/src/components/page-builder/modules/page-section-card/page-section-card.test.ts +50 -0
- package/src/components/page-builder/page-builder.component.ts +1053 -0
- package/src/components/page-builder/page-builder.scss +494 -0
- package/src/components/page-builder/page-builder.test.ts +464 -0
- package/src/components/page-builder/page-registry.ts +48 -0
- package/src/components/page-builder/page.types.ts +75 -0
- package/src/components/panel/panel.scss +1 -0
- package/src/components/settings-container/settings-container.component.ts +74 -9
- package/src/components/settings-container/settings-container.scss +75 -0
- package/src/components/settings-container/settings-container.test.ts +35 -0
- package/src/events/events.ts +2 -0
- package/src/events/zn-page-change.ts +9 -0
- package/src/events/zn-page-selection-change.ts +7 -0
- package/src/zinc.ts +4 -0
- package/web-test-runner.config.js +6 -1
|
@@ -9,6 +9,7 @@ import ZnIcon from '../../../icon';
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
branchDropXs,
|
|
12
|
+
branchPillTop,
|
|
12
13
|
BUS_OFFSET,
|
|
13
14
|
firstInputId,
|
|
14
15
|
FLOW_TYPE_MIME,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
NOTE_MIN_HEIGHT,
|
|
28
29
|
NOTE_MIN_WIDTH,
|
|
29
30
|
NOTE_WIDTH,
|
|
30
|
-
|
|
31
|
+
pillsCollide,
|
|
31
32
|
pillSize,
|
|
32
33
|
portAnchor,
|
|
33
34
|
snapToGrid,
|
|
@@ -46,6 +47,10 @@ const ADD_POINT_OFFSET = 40;
|
|
|
46
47
|
// whole grid units so wire runs sit on grid lines.
|
|
47
48
|
const WIRE_STUB = 20;
|
|
48
49
|
const WIRE_APPROACH = 20;
|
|
50
|
+
// Half the wire "+"'s footprint.
|
|
51
|
+
const WIRE_PLUS_RADIUS = 10;
|
|
52
|
+
// How far a node's input port circle pokes up into the wire's last segment.
|
|
53
|
+
const WIRE_PORT_POKE = 8;
|
|
49
54
|
const WIRE_DETOUR = 40;
|
|
50
55
|
// Vertical spacing between the horizontal runs of wires heading to different
|
|
51
56
|
// targets, so unrelated wires never share a line (which reads as a join).
|
|
@@ -54,7 +59,7 @@ const TYPE_MIME = FLOW_TYPE_MIME;
|
|
|
54
59
|
|
|
55
60
|
type DragState =
|
|
56
61
|
| { kind: 'pan'; ox: number; oy: number; px: number; py: number }
|
|
57
|
-
| { kind: 'node'; id: string; offX: number; offY: number }
|
|
62
|
+
| { kind: 'node'; id: string; offX: number; offY: number; startX: number; startY: number }
|
|
58
63
|
| { kind: 'note'; id: string; offX: number; offY: number }
|
|
59
64
|
| { kind: 'note-resize'; id: string };
|
|
60
65
|
|
|
@@ -86,6 +91,7 @@ type DragState =
|
|
|
86
91
|
*
|
|
87
92
|
* @csspart base - The canvas viewport.
|
|
88
93
|
* @csspart toolbar - The floating toolbar.
|
|
94
|
+
* @csspart bin - The delete drop-zone shown bottom-right while dragging a node or step.
|
|
89
95
|
*/
|
|
90
96
|
export default class ZnFlowCanvas extends ZincElement {
|
|
91
97
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
@@ -113,6 +119,8 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
113
119
|
@state() private panX = 0;
|
|
114
120
|
@state() private panY = 0;
|
|
115
121
|
@state() private drag: DragState | null = null;
|
|
122
|
+
/** Whether the current drag (node or step) is over the delete bin. */
|
|
123
|
+
@state() private _overBin = false;
|
|
116
124
|
/** Canvas-space top-left where a step, if dropped now, would be placed. */
|
|
117
125
|
@state() private _dropGhost: { x: number; y: number } | null = null;
|
|
118
126
|
/** The stray branch being drawn from an output port until it attaches or cancels. */
|
|
@@ -273,7 +281,7 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
273
281
|
const node = this.nodes.find(n => n.id === e.detail.nodeId);
|
|
274
282
|
if (!node) return;
|
|
275
283
|
const p = this.screenToCanvas(e.detail.clientX, e.detail.clientY);
|
|
276
|
-
this.drag = {kind: 'node', id: node.id, offX: p.x - node.x, offY: p.y - node.y};
|
|
284
|
+
this.drag = {kind: 'node', id: node.id, offX: p.x - node.x, offY: p.y - node.y, startX: node.x, startY: node.y};
|
|
277
285
|
this._dragMoved = false;
|
|
278
286
|
this._setupWindow('grabbing');
|
|
279
287
|
};
|
|
@@ -307,6 +315,23 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
307
315
|
this._startLink(nodeId, open?.id ?? NEW_OUTPUT_PORT);
|
|
308
316
|
};
|
|
309
317
|
|
|
318
|
+
/**
|
|
319
|
+
* An orphaned branch pill's port was clicked. If a branch from another node
|
|
320
|
+
* is already in flight, attach it to this pill's node; otherwise start a
|
|
321
|
+
* stray branch that continues from this pill.
|
|
322
|
+
*/
|
|
323
|
+
private _onBranchPortClick(nodeId: string, port: string) {
|
|
324
|
+
if (this._linking) {
|
|
325
|
+
const link = this._linking;
|
|
326
|
+
this._cancelLink();
|
|
327
|
+
if (link.nodeId !== nodeId) {
|
|
328
|
+
this._emit('flow-link-assign', {nodeId: link.nodeId, port: link.port, targetId: nodeId});
|
|
329
|
+
}
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this._startLink(nodeId, port);
|
|
333
|
+
}
|
|
334
|
+
|
|
310
335
|
private _startLink(nodeId: string, port: string) {
|
|
311
336
|
this._linking = {nodeId, port};
|
|
312
337
|
this._linkPos = null;
|
|
@@ -398,17 +423,31 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
398
423
|
}
|
|
399
424
|
const p = this.screenToCanvas(e.clientX, e.clientY);
|
|
400
425
|
if (this.drag.kind === 'node') {
|
|
426
|
+
this._overBin = this._binHit(e.clientX, e.clientY);
|
|
401
427
|
const node = this.nodes.find(n => n.id === (this.drag as { id: string }).id);
|
|
402
428
|
if (node) {
|
|
403
429
|
// Snap to the grid, and refuse positions where any part of the node's
|
|
404
430
|
// footprint (card or branch pills) would overlap another's — falling
|
|
405
|
-
// back to one axis at a time so the node slides along edges.
|
|
431
|
+
// back to one axis at a time so the node slides along edges. The
|
|
432
|
+
// candidate position substitutes into the node list, because moving a
|
|
433
|
+
// node also moves its parent's pill (it centres between the two): the
|
|
434
|
+
// pills must be checked where they WOULD be, not where they were.
|
|
406
435
|
const desired = {x: snapToGrid(p.x - this.drag.offX), y: snapToGrid(p.y - this.drag.offY)};
|
|
407
|
-
const
|
|
436
|
+
const parents = new Set(
|
|
437
|
+
this.connections.filter(c => c.target.node === node.id && c.source.node !== node.id).map(c => c.source.node));
|
|
408
438
|
const fits = (pos: { x: number; y: number }) => {
|
|
409
439
|
const moved = {...node, ...pos};
|
|
410
|
-
|
|
411
|
-
|
|
440
|
+
const placed = this.nodes.map(n => (n.id === node.id ? moved : n));
|
|
441
|
+
const typeOf = (t: string) => this.registry?.get(t);
|
|
442
|
+
if (placed.some(o => o !== moved && nodesCollide(moved, o, typeOf, placed, this.connections))) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
// The parents' pills re-centre with this move — they must land clear
|
|
446
|
+
// of everything too (a pair that doesn't involve the moved node).
|
|
447
|
+
return ![...parents].some(pid => {
|
|
448
|
+
const parent = placed.find(n => n.id === pid);
|
|
449
|
+
return !!parent && pillsCollide(parent, typeOf, placed, this.connections);
|
|
450
|
+
});
|
|
412
451
|
};
|
|
413
452
|
const next = fits(desired) ? desired
|
|
414
453
|
: fits({x: desired.x, y: node.y}) ? {x: desired.x, y: node.y}
|
|
@@ -442,15 +481,35 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
442
481
|
|
|
443
482
|
private _onPointerUp = () => {
|
|
444
483
|
const drag = this.drag;
|
|
484
|
+
const overBin = this._overBin;
|
|
445
485
|
this.drag = null;
|
|
486
|
+
this._overBin = false;
|
|
446
487
|
this._teardownWindow();
|
|
447
488
|
if (!drag) return;
|
|
448
489
|
|
|
490
|
+
// Dropped on the bin: put the node back where the drag started (so the
|
|
491
|
+
// delete's undo restores the pre-drag layout) and delete it.
|
|
492
|
+
if (drag.kind === 'node' && this._dragMoved && overBin) {
|
|
493
|
+
const node = this.nodes.find(n => n.id === drag.id);
|
|
494
|
+
if (node) {
|
|
495
|
+
node.x = drag.startX;
|
|
496
|
+
node.y = drag.startY;
|
|
497
|
+
}
|
|
498
|
+
this._emit('flow-node-action', {nodeId: drag.id, action: 'delete'});
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
449
502
|
if ((drag.kind === 'node' || drag.kind === 'note' || drag.kind === 'note-resize') && this._dragMoved) {
|
|
450
503
|
this._emit('flow-change-commit');
|
|
451
504
|
}
|
|
452
505
|
};
|
|
453
506
|
|
|
507
|
+
/** Whether a client-space point is over the delete bin. */
|
|
508
|
+
private _binHit(clientX: number, clientY: number): boolean {
|
|
509
|
+
const rect = this.shadowRoot?.querySelector('.bin')?.getBoundingClientRect();
|
|
510
|
+
return !!rect && clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
|
|
511
|
+
}
|
|
512
|
+
|
|
454
513
|
private _beginMove() {
|
|
455
514
|
this._dragMoved = true;
|
|
456
515
|
this._emit('flow-interaction-start');
|
|
@@ -469,17 +528,20 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
469
528
|
const cx = node.x + NODE_WIDTH / 2;
|
|
470
529
|
const by = node.y + NODE_HEIGHT;
|
|
471
530
|
const busY = by + BUS_OFFSET;
|
|
472
|
-
//
|
|
473
|
-
|
|
531
|
+
// Drops always sit at the natural fan position under the source, so the
|
|
532
|
+
// arrow into a pill is a straight vertical; the wire below the pill takes
|
|
533
|
+
// up any lateral offset to the child.
|
|
534
|
+
const dropXs = branchDropXs(node, t => this.registry?.get(t));
|
|
474
535
|
const branches = outputs.map((port, i) => {
|
|
475
536
|
const conn = this.connections.find(c => c.source.node === node.id && c.source.port === port.id);
|
|
476
537
|
const child = conn ? this.nodes.find(n => n.id === conn.target.node) : undefined;
|
|
477
538
|
const x = dropXs[i];
|
|
478
|
-
// Labelled outputs show their branch pill on the drop
|
|
479
|
-
//
|
|
480
|
-
// the
|
|
481
|
-
|
|
539
|
+
// Labelled outputs show their branch pill on the drop, centred along the
|
|
540
|
+
// wire when a child sits below; the wire continues from below it. Long
|
|
541
|
+
// names wrap, so the pill height (and the exit where the +/child wire
|
|
542
|
+
// begins) grows with the text.
|
|
482
543
|
const pillH = port.label ? pillSize(port.label).h : 0;
|
|
544
|
+
const pillTop = port.label ? branchPillTop(node, pillH, child, outputs.length === 1) : null;
|
|
483
545
|
const exitY = pillTop === null ? busY : pillTop + pillH;
|
|
484
546
|
return {port, conn, child, x, pillTop, pillH, exitY};
|
|
485
547
|
});
|
|
@@ -527,9 +589,15 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
527
589
|
return offsets;
|
|
528
590
|
}
|
|
529
591
|
|
|
530
|
-
/**
|
|
592
|
+
/**
|
|
593
|
+
* Whether an orthogonal segment passes through any node card (with margin) —
|
|
594
|
+
* or the approach zone above one, where incoming arrows land. A foreign wire
|
|
595
|
+
* running just over a card's input port reads as connecting to it, so routes
|
|
596
|
+
* keep well clear of that strip too.
|
|
597
|
+
*/
|
|
531
598
|
private _segmentBlocked(a: { x: number; y: number }, b: { x: number; y: number }, ignore: Set<string>): boolean {
|
|
532
599
|
const M = 8;
|
|
600
|
+
const APPROACH_ZONE = 48;
|
|
533
601
|
const minX = Math.min(a.x, b.x);
|
|
534
602
|
const maxX = Math.max(a.x, b.x);
|
|
535
603
|
const minY = Math.min(a.y, b.y);
|
|
@@ -537,7 +605,7 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
537
605
|
return this.nodes.some(n =>
|
|
538
606
|
!ignore.has(n.id)
|
|
539
607
|
&& minX < n.x + NODE_WIDTH + M && maxX > n.x - M
|
|
540
|
-
&& minY < n.y + NODE_HEIGHT + M && maxY > n.y -
|
|
608
|
+
&& minY < n.y + NODE_HEIGHT + M && maxY > n.y - APPROACH_ZONE
|
|
541
609
|
);
|
|
542
610
|
}
|
|
543
611
|
|
|
@@ -688,7 +756,7 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
688
756
|
if (this.movingNodeId) return [];
|
|
689
757
|
const midOffsets = this._elbowMidOffsets();
|
|
690
758
|
const loops = loopConnections(this.nodes, this.connections);
|
|
691
|
-
const
|
|
759
|
+
const candidates: { connectionId: string; at: (d: number) => { px: number; py: number }; d: number; len: number }[] = [];
|
|
692
760
|
for (const node of this.nodes) {
|
|
693
761
|
const {branches} = this._outputLayout(node);
|
|
694
762
|
branches.forEach(b => {
|
|
@@ -707,13 +775,47 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
707
775
|
seg = i;
|
|
708
776
|
}
|
|
709
777
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
778
|
+
// The "+" prefers the segment's free run centre — the child's input
|
|
779
|
+
// port circle pokes into the last segment's end, so the "+" splits the
|
|
780
|
+
// pill-to-port gap evenly however tight it is.
|
|
781
|
+
const a = pts[seg];
|
|
782
|
+
const c = pts[seg + 1];
|
|
783
|
+
const len = Math.abs(c.x - a.x) + Math.abs(c.y - a.y);
|
|
784
|
+
const dx = Math.sign(c.x - a.x);
|
|
785
|
+
const dy = Math.sign(c.y - a.y);
|
|
786
|
+
const at = (d: number) => ({px: a.x + dx * d, py: a.y + dy * d});
|
|
787
|
+
const d = Math.max(0, (seg === pts.length - 2 ? len - WIRE_PORT_POKE : len) / 2);
|
|
788
|
+
candidates.push({connectionId: b.conn.id, at, d, len});
|
|
715
789
|
});
|
|
716
790
|
}
|
|
791
|
+
|
|
792
|
+
// Place each "+" — always rendered, sliding along its own wire away from
|
|
793
|
+
// node cards and from "+"s already placed, so wires sharing a corridor
|
|
794
|
+
// never stack their buttons on top of each other.
|
|
795
|
+
const M = WIRE_PLUS_RADIUS;
|
|
796
|
+
const onCard = (px: number, py: number) => this.nodes.some(n =>
|
|
797
|
+
px > n.x - M && px < n.x + NODE_WIDTH + M && py > n.y - M && py < n.y + NODE_HEIGHT + M);
|
|
798
|
+
const points: { connectionId: string; px: number; py: number }[] = [];
|
|
799
|
+
for (const cand of candidates) {
|
|
800
|
+
const clash = (p: { px: number; py: number }) =>
|
|
801
|
+
onCard(p.px, p.py) || points.some(f => Math.hypot(f.px - p.px, f.py - p.py) < WIRE_PLUS_RADIUS * 2 + 6);
|
|
802
|
+
let spot = cand.at(cand.d);
|
|
803
|
+
if (clash(spot)) {
|
|
804
|
+
// eslint-disable-next-line no-labels
|
|
805
|
+
outer: for (let step = GRID_SIZE / 2; step <= cand.len; step += GRID_SIZE / 2) {
|
|
806
|
+
for (const dd of [cand.d - step, cand.d + step]) {
|
|
807
|
+
if (dd < 0 || dd > cand.len) continue;
|
|
808
|
+
const p = cand.at(dd);
|
|
809
|
+
if (!clash(p)) {
|
|
810
|
+
spot = p;
|
|
811
|
+
// eslint-disable-next-line no-labels
|
|
812
|
+
break outer;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
points.push({connectionId: cand.connectionId, ...spot});
|
|
818
|
+
}
|
|
717
819
|
return points;
|
|
718
820
|
}
|
|
719
821
|
|
|
@@ -788,6 +890,31 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
788
890
|
this._emit('flow-wire-assign', {connectionId, type});
|
|
789
891
|
}
|
|
790
892
|
|
|
893
|
+
// --- Delete bin ------------------------------------------------------------
|
|
894
|
+
// Shown while a node is dragged (drop it there to delete) or while a step is
|
|
895
|
+
// dragged in from the panel (drop it there to discard instead of placing).
|
|
896
|
+
|
|
897
|
+
private _onBinDragOver = (e: DragEvent) => {
|
|
898
|
+
if (!e.dataTransfer?.types.includes(TYPE_MIME)) return;
|
|
899
|
+
e.preventDefault();
|
|
900
|
+
e.stopPropagation();
|
|
901
|
+
e.dataTransfer.dropEffect = 'move';
|
|
902
|
+
this._overBin = true;
|
|
903
|
+
this._dropGhost = null; // nothing will be placed from here
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
private _onBinDragLeave = () => {
|
|
907
|
+
this._overBin = false;
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
private _onBinDrop = (e: DragEvent) => {
|
|
911
|
+
// Swallow the drop — the step is discarded, never reaching the canvas.
|
|
912
|
+
e.preventDefault();
|
|
913
|
+
e.stopPropagation();
|
|
914
|
+
this._overBin = false;
|
|
915
|
+
this._dropGhost = null;
|
|
916
|
+
};
|
|
917
|
+
|
|
791
918
|
// --- Toolbar ----------------------------------------------------------------
|
|
792
919
|
|
|
793
920
|
private _zoomBy(delta: number) {
|
|
@@ -882,7 +1009,9 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
882
1009
|
// (arrowhead), then continue from the pill to the child / open "+".
|
|
883
1010
|
active.forEach(b => {
|
|
884
1011
|
if (b.pillTop !== null) {
|
|
885
|
-
|
|
1012
|
+
// Start no lower than 10 above the pill, so the arrowhead keeps a
|
|
1013
|
+
// tail even when a tight gap pins the pill right up against the bus.
|
|
1014
|
+
paths.push(svg`<path class="wire" d="M ${b.x} ${Math.min(busY, b.pillTop - 10)} L ${b.x} ${b.pillTop}" marker-end="url(#flow-arrow)"></path>`);
|
|
886
1015
|
}
|
|
887
1016
|
if (b.conn && b.child) {
|
|
888
1017
|
const offset = midOffsets.get(b.conn.id) ?? 0;
|
|
@@ -900,12 +1029,17 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
900
1029
|
});
|
|
901
1030
|
}
|
|
902
1031
|
|
|
903
|
-
// The in-progress branch follows the cursor from the node's bottom port
|
|
904
|
-
//
|
|
1032
|
+
// The in-progress branch follows the cursor from the node's bottom port —
|
|
1033
|
+
// or from the branch's pill when it has one (an orphaned branch continues
|
|
1034
|
+
// from its pill) — snapping onto the hovered target's input until it
|
|
1035
|
+
// attaches or cancels.
|
|
905
1036
|
if (this._linking && this._linkPos) {
|
|
906
1037
|
const src = this.nodes.find(n => n.id === this._linking!.nodeId);
|
|
907
1038
|
if (src) {
|
|
908
|
-
const
|
|
1039
|
+
const branch = this._outputLayout(src).branches.find(b => b.port.id === this._linking!.port);
|
|
1040
|
+
const from = branch && branch.pillTop !== null
|
|
1041
|
+
? {x: branch.x, y: branch.exitY}
|
|
1042
|
+
: {x: src.x + NODE_WIDTH / 2, y: src.y + NODE_HEIGHT};
|
|
909
1043
|
const target = this._linkTarget ? this.nodes.find(n => n.id === this._linkTarget) : undefined;
|
|
910
1044
|
const end = target
|
|
911
1045
|
? this._inputAnchor(target, firstInputId(target, this._typeFor(target)) ?? '')
|
|
@@ -940,7 +1074,8 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
940
1074
|
private _renderBranchPills() {
|
|
941
1075
|
const loops = loopConnections(this.nodes, this.connections);
|
|
942
1076
|
const items: {
|
|
943
|
-
key: string; nodeId: string; portId: string; label: string; x: number; top: number; height: number;
|
|
1077
|
+
key: string; nodeId: string; portId: string; label: string; x: number; top: number; height: number;
|
|
1078
|
+
loop: boolean; open: boolean;
|
|
944
1079
|
}[] = [];
|
|
945
1080
|
for (const node of this.nodes) {
|
|
946
1081
|
const {branches} = this._outputLayout(node);
|
|
@@ -955,6 +1090,7 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
955
1090
|
top: b.pillTop,
|
|
956
1091
|
height: b.pillH,
|
|
957
1092
|
loop: !!b.conn && loops.has(b.conn),
|
|
1093
|
+
open: !b.conn,
|
|
958
1094
|
});
|
|
959
1095
|
}
|
|
960
1096
|
}
|
|
@@ -986,6 +1122,16 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
986
1122
|
>
|
|
987
1123
|
<zn-icon src="x@lu" size="14"></zn-icon>
|
|
988
1124
|
</button>
|
|
1125
|
+
${i.open ? html`
|
|
1126
|
+
<span
|
|
1127
|
+
class="branch-pill-port"
|
|
1128
|
+
title="Start a branch"
|
|
1129
|
+
@pointerdown="${(e: PointerEvent) => e.button === 0 && e.stopPropagation()}"
|
|
1130
|
+
@click="${(e: Event) => {
|
|
1131
|
+
e.stopPropagation();
|
|
1132
|
+
this._onBranchPortClick(i.nodeId, i.portId);
|
|
1133
|
+
}}"
|
|
1134
|
+
></span>` : ''}
|
|
989
1135
|
</div>
|
|
990
1136
|
`
|
|
991
1137
|
);
|
|
@@ -1150,6 +1296,18 @@ export default class ZnFlowCanvas extends ZincElement {
|
|
|
1150
1296
|
<zn-button icon="minus@lu" icon-size="18" icon-button="small" plain
|
|
1151
1297
|
@click="${() => this._zoomBy(-ZOOM_STEP)}"></zn-button>
|
|
1152
1298
|
</div>
|
|
1299
|
+
|
|
1300
|
+
${(this.drag?.kind === 'node' && this._dragMoved) || this.dragType ? html`
|
|
1301
|
+
<div
|
|
1302
|
+
part="bin"
|
|
1303
|
+
class="bin ${this._overBin ? 'bin--over' : ''}"
|
|
1304
|
+
title="Drop here to delete"
|
|
1305
|
+
@dragover="${this._onBinDragOver}"
|
|
1306
|
+
@dragleave="${this._onBinDragLeave}"
|
|
1307
|
+
@drop="${this._onBinDrop}"
|
|
1308
|
+
>
|
|
1309
|
+
<zn-icon src="trash-2@lu" size="22"></zn-icon>
|
|
1310
|
+
</div>` : ''}
|
|
1153
1311
|
</div>
|
|
1154
1312
|
`;
|
|
1155
1313
|
}
|