Skip to content

Commit a879e78

Browse files
author
shuzarevich
committed
feat(connection): add editable waypoints with candidates + demo example
- Introduced Connection Waypoints: interactive waypoints to shape connection routes - Added waypoint candidates to suggest insertion points and enable drag-to-add UX - Implemented waypoint operations: insert from candidate, drag-to-move, right-click delete - Emitted fConnectionWaypointsChanged to keep user state in sync - Added "Connection Waypoints" example showcasing all built-in connection types (straight, segment, bezier, adaptive-curve) with show/enable toggles and styling - Refactored picking/handling logic and related utils for clarity and maintainability
1 parent f595d55 commit a879e78

File tree

63 files changed

+540
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+540
-272
lines changed

projects/f-examples/connections/connection-types/connection-types.html

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<f-flow fDraggable (fLoaded)="loaded()">
22
<f-canvas>
3-
<f-connection [fReassignDisabled]="true" fType="straight" fOutputId="1" fInputId="2">
4-
<f-connection-control-points />
5-
</f-connection>
3+
<f-connection [fReassignDisabled]="true" fType="straight" fOutputId="1" fInputId="2"/>
64
<div
75
fNode
86
fDragHandle
@@ -24,9 +22,7 @@
2422
Node
2523
</div>
2624

27-
<f-connection [fReassignDisabled]="true" fType="segment" fOutputId="3" fInputId="4">
28-
<f-connection-control-points />
29-
</f-connection>
25+
<f-connection [fReassignDisabled]="true" fType="segment" fOutputId="3" fInputId="4"/>
3026
<div
3127
fNode
3228
fDragHandle
@@ -48,9 +44,7 @@
4844
Node
4945
</div>
5046

51-
<f-connection [fReassignDisabled]="true" fType="bezier" fOutputId="5" fInputId="6">
52-
<f-connection-control-points />
53-
</f-connection>
47+
<f-connection [fReassignDisabled]="true" fType="bezier" fOutputId="5" fInputId="6"/>
5448
<div
5549
fNode
5650
fDragHandle
@@ -72,9 +66,7 @@
7266
Node
7367
</div>
7468

75-
<f-connection [fReassignDisabled]="true" fType="adaptive-curve" fOutputId="7" fInputId="8">
76-
<f-connection-control-points />
77-
</f-connection>
69+
<f-connection [fReassignDisabled]="true" fType="adaptive-curve" fOutputId="7" fInputId="8"/>
7870
<div
7971
fNode
8072
fDragHandle
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<f-flow fDraggable (fLoaded)="loaded()" (fConnectionWaypointsChanged)="changed($event)">
2+
<f-canvas>
3+
<f-connection [fReassignDisabled]="true" fType="straight" fOutputId="1" fInputId="2">
4+
@if (waypointsOn()) {
5+
<f-connection-waypoints [(waypoints)]="waypointsStraight" radius="8" [visibility]="waypointsVisibility()"/>
6+
}
7+
</f-connection>
8+
<div
9+
fNode
10+
fDragHandle
11+
[fNodePosition]="{ x: -150, y: 0 }"
12+
fNodeOutput
13+
fOutputId="1"
14+
fOutputConnectableSide="right"
15+
>
16+
Node
17+
</div>
18+
<div
19+
fNode
20+
fDragHandle
21+
[fNodePosition]="{ x: 200, y: 0 }"
22+
fNodeInput
23+
fInputId="2"
24+
fInputConnectableSide="left"
25+
>
26+
Node
27+
</div>
28+
29+
<f-connection [fReassignDisabled]="true" fType="segment" fOutputId="3" fInputId="4">
30+
@if (waypointsOn()) {
31+
<f-connection-waypoints [(waypoints)]="waypointsSegment" radius="4" [visibility]="waypointsVisibility()"/>
32+
}
33+
</f-connection>
34+
<div
35+
fNode
36+
fDragHandle
37+
[fNodePosition]="{ x: -150, y: 150 }"
38+
fNodeOutput
39+
fOutputId="3"
40+
fOutputConnectableSide="right"
41+
>
42+
Node
43+
</div>
44+
<div
45+
fNode
46+
fDragHandle
47+
[fNodePosition]="{ x: 250, y: 200 }"
48+
fNodeInput
49+
fInputId="4"
50+
fInputConnectableSide="left"
51+
>
52+
Node
53+
</div>
54+
55+
<f-connection [fReassignDisabled]="true" fType="bezier" fOutputId="5" fInputId="6">
56+
@if (waypointsOn()) {
57+
<f-connection-waypoints [(waypoints)]="waypointsBezier" radius="6" [visibility]="waypointsVisibility()"/>
58+
}
59+
</f-connection>
60+
<div
61+
fNode
62+
fDragHandle
63+
[fNodePosition]="{ x: -150, y: 300 }"
64+
fNodeOutput
65+
fOutputId="5"
66+
fOutputConnectableSide="right"
67+
>
68+
Node
69+
</div>
70+
<div
71+
fNode
72+
fDragHandle
73+
[fNodePosition]="{ x: 200, y: 350 }"
74+
fNodeInput
75+
fInputId="6"
76+
fInputConnectableSide="left"
77+
>
78+
Node
79+
</div>
80+
81+
<f-connection [fReassignDisabled]="true" fType="adaptive-curve" fOutputId="7" fInputId="8">
82+
@if (waypointsOn()) {
83+
<f-connection-waypoints [(waypoints)]="waypointsAdaptiveCurve" [visibility]="waypointsVisibility()"/>
84+
}
85+
</f-connection>
86+
<div
87+
fNode
88+
fDragHandle
89+
[fNodePosition]="{ x: -150, y: 450 }"
90+
fNodeOutput
91+
fOutputId="7"
92+
fOutputConnectableSide="right"
93+
>
94+
Node
95+
</div>
96+
<div
97+
fNode
98+
fDragHandle
99+
[fNodePosition]="{ x: 250, y: 500 }"
100+
fNodeInput
101+
fInputId="8"
102+
fInputConnectableSide="left"
103+
>
104+
Node
105+
</div>
106+
</f-canvas>
107+
</f-flow>
108+
109+
<div class="examples-toolbar">
110+
<f-checkbox (change)="toggleWaypointsVisibility()" [checked]="waypointsVisibility()">
111+
Show waypoints
112+
</f-checkbox>
113+
<f-checkbox (change)="toggleWaypointsOn()" [checked]="waypointsOn()">
114+
Enable waypoints
115+
</f-checkbox>
116+
</div>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
@use '../../flow-common';
2+
3+
::ng-deep f-flow {
4+
@include flow-common.connection;
5+
6+
.f-connection {
7+
.f-connection-waypoints {
8+
circle {
9+
vector-effect: non-scaling-stroke;
10+
transform-box: fill-box;
11+
transform-origin: center;
12+
transition: transform 120ms ease, stroke 120ms ease, fill 120ms ease;
13+
}
14+
15+
.f-candidate {
16+
fill: var(--node-background-color);
17+
stroke: #30a46c;
18+
stroke-width: 2;
19+
cursor: pointer;
20+
21+
&:hover {
22+
stroke: #298459;
23+
transform: scale(1.12);
24+
}
25+
}
26+
27+
.f-waypoint {
28+
fill: var(--node-background-color);
29+
stroke: var(--minimap-node-selected-color);
30+
stroke-width: 2;
31+
cursor: grab;
32+
33+
&:hover {
34+
transform: scale(1.10);
35+
}
36+
}
37+
}
38+
}
39+
}
40+
41+
.f-node {
42+
@include flow-common.node;
43+
}
44+
45+
@include flow-common.examples-toolbar;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { ChangeDetectionStrategy, Component, signal, viewChild } from '@angular/core';
2+
import { FCanvasComponent, FConnectionWaypointsChangedEvent, FFlowModule } from '@foblex/flow';
3+
import { FCheckboxComponent } from '@foblex/m-render';
4+
5+
@Component({
6+
selector: 'connection-waypoints',
7+
styleUrls: ['./connection-waypoints.scss'],
8+
templateUrl: './connection-waypoints.html',
9+
changeDetection: ChangeDetectionStrategy.OnPush,
10+
standalone: true,
11+
imports: [FFlowModule, FCheckboxComponent],
12+
})
13+
export class ConnectionWaypoints {
14+
private readonly _canvas = viewChild.required(FCanvasComponent);
15+
16+
protected waypointsStraight = [
17+
{ x: 50, y: 60 },
18+
{ x: 100, y: 0 },
19+
{ x: 150, y: 30 },
20+
];
21+
protected waypointsSegment = [
22+
{ x: 120, y: 100 },
23+
{ x: 300, y: 150 },
24+
];
25+
protected waypointsBezier = [
26+
{ x: 50, y: 350 },
27+
{ x: 100, y: 400 },
28+
{ x: 150, y: 350 },
29+
];
30+
protected waypointsAdaptiveCurve = [
31+
{ x: 50, y: 450 },
32+
{ x: 100, y: 500 },
33+
{ x: 150, y: 550 },
34+
{ x: 200, y: 500 },
35+
];
36+
37+
protected readonly waypointsVisibility = signal(true);
38+
protected readonly waypointsOn = signal(true);
39+
40+
protected loaded(): void {
41+
this._canvas()?.resetScaleAndCenter(false);
42+
}
43+
44+
protected changed({ connectionId, waypoints }: FConnectionWaypointsChangedEvent): void {
45+
console.log('Connection waypoints changed', connectionId, waypoints);
46+
}
47+
48+
protected toggleWaypointsVisibility(): void {
49+
this.waypointsVisibility.update((x) => !x);
50+
}
51+
52+
protected toggleWaypointsOn(): void {
53+
this.waypointsOn.update((x) => !x);
54+
}
55+
}
56+

projects/f-flow/src/domain/f-flow/calculate-flow-state/calculate-connections-state/calculate-connections-state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class CalculateConnectionsState
2727
fType: x.fType,
2828
fBehavior: x.fBehavior,
2929
isSelected: x.isSelected(),
30-
pivots: x.fControlPoints()?.pivots() || [],
30+
waypoints: x.fWaypoints()?.waypoints() || [],
3131
fInputSide: x.fInputSide(),
3232
fOutputSide: x.fOutputSide(),
3333
};

projects/f-flow/src/domain/f-flow/calculate-flow-state/i-f-flow-state-connection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface IFFlowStateConnection {
1818

1919
isSelected: boolean;
2020

21-
pivots: IPoint[];
21+
waypoints: IPoint[];
2222

2323
fInputSide: EFConnectionConnectableSide;
2424

projects/f-flow/src/f-connection-v2/components/connection-control-points/utils/find-connection-with-pivot-and-candidate.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

projects/f-flow/src/f-connection-v2/components/connection-control-points/f-connection-control-points.scss renamed to projects/f-flow/src/f-connection-v2/components/connection-waypoints/f-connection-waypoints.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
:host {
2-
pointer-events: all;
2+
pointer-events: none;
33
position: absolute;
44
}
55

@@ -9,3 +9,7 @@ svg {
99
position: absolute;
1010
overflow: visible;
1111
}
12+
13+
circle {
14+
pointer-events: visible;
15+
}

projects/f-flow/src/f-connection-v2/components/connection-control-points/index.ts renamed to projects/f-flow/src/f-connection-v2/components/connection-waypoints/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export * from './models';
22
export * from './utils';
3-
export * from './f-connection-control-points';
3+
export * from './f-connection-waypoints';

projects/f-flow/src/f-connection-v2/components/connection-control-points/models/i-pivot-candidate.ts renamed to projects/f-flow/src/f-connection-v2/components/connection-waypoints/models/i-waypoint-candidate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IPoint } from '@foblex/2d';
22

3-
export interface IPivotCandidate {
3+
export interface IWaypointCandidate {
44
point: IPoint;
55
chainIndex: number;
66
}

0 commit comments

Comments
 (0)