Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
966cb3d
refactor(connections): rename components and remove deprecated proper…
siarheihuzarevich Dec 31, 2025
7b07f2a
refactor(assign-node-to-connection-on-drop example): rename component…
siarheihuzarevich Dec 31, 2025
fa6d42b
refactor(drag-handle example): rename component files and update prop…
siarheihuzarevich Dec 31, 2025
31762e5
refactor(connections): rename execution classes and update request pr…
siarheihuzarevich Dec 31, 2025
2f62e4f
refactor(internal reactivity): enhance debounce and listen functionality
siarheihuzarevich Dec 31, 2025
f387226
refactor(zoom): rename execution classes and update request properties
siarheihuzarevich Dec 31, 2025
5dfeeb5
feat(pinch-to-zoom): implement pinch-to-zoom functionality
siarheihuzarevich Dec 31, 2025
ba6334c
refactor(single-select): rename and restructure single select components
siarheihuzarevich Dec 31, 2025
069f0bc
refactor(components): rename and restructure connection-related files
siarheihuzarevich Jan 18, 2026
3276baf
feat(connection): add buildConnectionAnchors utility and refactor rel…
siarheihuzarevich Jan 22, 2026
6e559f4
refactor(app): clean up unused imports and commented code
Jan 22, 2026
b18109a
feat(connection): add control points support and improve candidate/pi…
Jan 23, 2026
77c1adb
refactor(connection): rename and restructure connection-related files
Jan 24, 2026
19772df
feat(connection): add editable waypoints with candidates + demo example
Jan 25, 2026
229d6e3
feat(connection): implement waypoints management with drag-and-drop s…
Jan 25, 2026
7093298
refactor(connection): rename connection-markers component files and m…
Jan 25, 2026
c8f5e2e
refactor(connection): rename and update node move preparation classes
Jan 25, 2026
186b2bc
refactor(connection): replace pivots with waypoints in connection cal…
Jan 25, 2026
fbb0825
chore(connection): implement waypoint removal and update connection l…
Jan 26, 2026
83ac55d
chore(connection): implement removal of connection waypoints
Jan 26, 2026
bbbd15f
chore(release): bump version to 18.0.0 and update CHANGELOG
siarheihuzarevich Jan 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 0 additions & 4 deletions .env.example

This file was deleted.

17 changes: 17 additions & 0 deletions .github/git-commit-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Write your commit message in the Conventional Commits format.

Requirements:

- Title: <type>(<scope>): <brief description of what was done>
- Use the following types: feat, fix, refactor, perf, docs, test, chore.
- Scope: module/feature name (e.g., auth, payments, flow-editor).
- Title up to 72 characters, without a period at the end.

Body (required if there is more than one change):

- 3–7 bullet points, each with a past tense verb.
- Indicate important behavior changes, migrations, and removed fields/methods.
- If there is a BREAKING CHANGE, add it as a separate section "BREAKING CHANGE:".

If the change log shows the issue number (JIRA/GH issue), add the following footer at the end: "Refs: XXX-123".
Don't invent things that aren't in the diff.
68 changes: 68 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,74 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [18.0.0](https://github.com/foblex/flow/compare/v17.9.5...v18.0.0) (2026-01-26)

### Highlights

- **Connection Waypoints (new feature)** — interactive waypoint editing for connections with candidate points and drag-and-drop.
- **Pinch-to-zoom (new feature)** — smooth multi-touch zoom for trackpads and touch devices.
- **Control Flow + Content Projection compatibility** — improved support for `@if/@for` rendering by extending projection slots for nodes/connections.
- **Custom Backgrounds** — richer SVG pattern support plus a new example for advanced backgrounds.

### Features

- **zoom:** add pinch-to-zoom support for touch/trackpad gestures ([5dfeeb5](https://github.com/foblex/flow/commit/5dfeeb5f16dcf9dfbcf4ee38dfea22ded1c37f83))
- **connection:** introduce **waypoints** with candidates + drag-to-add / drag-to-move interactions and demo example ([19772df](https://github.com/foblex/flow/commit/19772df7649e66b4115bd6a057613d3fa25faeec), [229d6e3](https://github.com/foblex/flow/commit/229d6e3b773a64a1e55654c373e3b556a8d38c8e))
- **connection:** add anchors utility and refactor connection builders + candidate generation ([3276baf](https://github.com/foblex/flow/commit/3276baf8405eec062022b285524e3c641d901424), [b18109a](https://github.com/foblex/flow/commit/b18109a2c26fdac7aa8e5980fb5bbd791d3a4b42))
- **canvas:** improve content projection for Angular Control Flow usage (`@if/@for`) by supporting grouped slots (`[fNodes]`, `[fConnections]`) ([b18109a](https://github.com/foblex/flow/commit/b18109a2c26fdac7aa8e5980fb5bbd791d3a4b42))
- **background:** support custom/complex SVG patterns + add a dedicated example ([29e28c8](https://github.com/foblex/flow/commit/29e28c81faf9da5189d9bf108323f4f4deb9387d))
- **showcase:** add AI Low Code Platform entry and links ([6013ee7](https://github.com/foblex/flow/commit/6013ee7c23f5d88d8861027b56618b4bffc9fce2), [dd84ae8](https://github.com/foblex/flow/commit/dd84ae8730d8296a56d47ce5fb4966cc4de55e4b))

### Improvements

- Refactored multiple connection/interaction modules to improve readability, maintainability and internal consistency.
- Candidate/waypoint handling is now more predictable across all connection types.

### Documentation

- fix url to custom-connection-type component in guide ([a6421ac](https://github.com/foblex/flow/commit/a6421aca5fd3ba2576a7f155e5ad8b06c5af9ed7))

### ⚠️ Breaking Changes

#### 1) Custom connection builders: `IFConnectionBuilderResponse` updated

If you implemented a custom connection type / builder, the response interface changed:

- `connectionCenter` was **removed**
- `points` is now **required** (was optional)
- `candidates` was **added**

✅ **Note:** you don’t have to calculate or return `points` or `candidates` yourself.
If you don’t support them, return empty arrays.

```ts
export interface IFConnectionBuilderResponse {
path: string;
penultimatePoint: IPoint;
secondPoint: IPoint;
points: IPoint[]; // can be []
candidates: IPoint[]; // can be []
}
```

#### 2) FConnection API cleanup: removed deprecated inputs

Deprecated connection inputs were removed from FConnectionComponent:

- `fText` - removed (use FConnectionContent instead)
- `fTextStartOffset` - removed (use FConnectionContent instead)

Removed legacy directive:

- [fConnectionCenter] - removed (use FConnectionContent instead)

#### 3) FCanvas API cleanup

Removed deprecated zoom aliases from FCanvasComponent:

- setZoom(...) - removed → use setScale(...)
- resetZoom() - removed → use resetScale()

### [17.9.5](https://github.com/foblex/flow/compare/v17.8.0...v17.9.5) (2025-10-27)

### Features
Expand Down
32 changes: 0 additions & 32 deletions cypress/e2e/assign-node-to-connection-on-drop.component.cy.ts

This file was deleted.

53 changes: 0 additions & 53 deletions cypress/e2e/drag-handle.component.cy.ts

This file was deleted.

56 changes: 56 additions & 0 deletions cypress/e2e/drag-handle.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
describe('DragHandle', () => {
beforeEach(() => {
cy.visit('http://localhost:4200/examples/drag-handle');
cy.get('f-flow').scrollIntoView();
});

it('should drag fNode element and update its transform translate', function () {
cy.wait(500)
.get('.f-node.f-drag-handle')
.then(($dragHandle: JQuery<HTMLElement>) => {
const dragHandleRect = $dragHandle.get(0).getBoundingClientRect();

cy.get('.f-node.f-drag-handle')
.first()
.trigger('mousedown', { button: 0, force: true })
.trigger('mousemove', { clientX: -250, clientY: 0 })
.trigger('pointerup', { clientX: 0, clientY: 0 });

cy.wait(1500)
.get('.f-node.f-drag-handle')
.first()
.then(($dragHandle2: JQuery<HTMLElement>) => {
const dragHandleRect2 = $dragHandle2.get(0).getBoundingClientRect();
expect(Math.round(dragHandleRect.x + 250)).to.equal(Math.round(dragHandleRect2.x));
});
});
});

it('should click fNode element and update its selection state', function () {
cy.wait(500).get('.f-node.f-drag-handle').should('not.have.class', 'f-selected');

cy.wait(500).get('.f-node.f-drag-handle').click();

cy.wait(500).get('.f-node.f-drag-handle').should('have.class', 'f-selected');
});

it('should drag fCanvas element and update its transform translate', function () {
cy.get('f-flow')
.first()
.then(($flow) => {
cy.get('f-canvas')
.first()
.then(($canvas) => {
const transform = $canvas.css('transform');

cy.wrap($flow)
.trigger('mousedown', { clientX: 10, clientY: 10, button: 0, force: true })
.trigger('mousemove', { clientX: 0, clientY: 0 })
.trigger('mousemove', { clientX: 20, clientY: 20 })
.trigger('pointerup', { clientX: 30, clientY: 30, force: true });

cy.wrap($canvas).invoke('css', 'transform').should('not.equal', transform);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
describe('DragToConnectComponent', () => {
describe('DragToConnect', () => {
beforeEach(() => {
cy.visit('http://localhost:4200/examples/drag-to-connect');
cy.get('f-flow').scrollIntoView();
})
});

it('should start creating a connection and show connection-for-create element', function () {
cy.get('.f-node-output').then(($output) => {
const outputRect = $output[0].getBoundingClientRect();
const startX = outputRect.left + outputRect.width / 2;
const startY = outputRect.top + outputRect.height / 2;

cy.get('.f-connection-for-create').should('exist')
.invoke('css', 'display').should('equal', 'none');
cy.get('.f-connection-for-create')
.should('exist')
.invoke('css', 'display')
.should('equal', 'none');

cy.wrap($output)
.trigger('mousedown', {button: 0, clientX: startX, clientY: startY, force: true})
.trigger('mousemove', {clientX: startX + 100, clientY: startY + 100, force: true})
.trigger('mousedown', { button: 0, clientX: startX, clientY: startY, force: true })
.trigger('mousemove', { clientX: startX + 100, clientY: startY + 100, force: true })
.wait(500)
.get('.f-connection-for-create').should('exist')
.invoke('css', 'display').should('equal', 'block')
.get('.f-connection-for-create')
.should('exist')
.invoke('css', 'display')
.should('equal', 'block')
.wrap($output)
.trigger('pointerup');

cy.wait(500).get('.f-connection-for-create').should('exist')
.invoke('css', 'display').should('equal', 'none');
cy.wait(500)
.get('.f-connection-for-create')
.should('exist')
.invoke('css', 'display')
.should('equal', 'none');
});
});

Expand All @@ -41,15 +48,12 @@ describe('DragToConnectComponent', () => {
const endX = inputRect.left + inputRect.width / 2;

cy.wrap($output)
.trigger('mousedown', {button: 0, clientX: startX, clientY: startY, force: true})
.trigger('mousemove', {clientX: endX, clientY: endY, force: true})
.trigger('pointerup', {clientX: endX, clientY: endY, force: true});
.trigger('mousedown', { button: 0, clientX: startX, clientY: startY, force: true })
.trigger('mousemove', { clientX: endX, clientY: endY, force: true })
.trigger('pointerup', { clientX: endX, clientY: endY, force: true });

cy.get('#f-connection-0', {timeout: 2000}).should('exist');
cy.get('#f-connection-0', { timeout: 2000 }).should('exist');
});
});
});
})



});
36 changes: 0 additions & 36 deletions cypress/e2e/drag-to-reassign.component.cy.ts

This file was deleted.

37 changes: 37 additions & 0 deletions cypress/e2e/drag-to-reassign.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
describe('DragToReassign', () => {
beforeEach(() => {
cy.visit('http://localhost:4200/examples/drag-to-reassign');
cy.get('f-flow').scrollIntoView();
});

it('should drag from input to another input and reassign the connection', function () {
cy.get('#connection_113').should('exist');

cy.get('div[data-f-input-id="3"]').should('exist');
cy.get('div[data-f-input-id="4"]').should('exist');

cy.get('.f-connection-drag-handle')
.first()
.then(($handle) => {
const handleRect = $handle[0].getBoundingClientRect();
const startY = handleRect.top + handleRect.height / 2;
const startX = handleRect.left + handleRect.width / 2;

cy.get('div[data-f-input-id="4"]')
.first()
.then(($input2) => {
const input2Rect = $input2[0].getBoundingClientRect();
const endY = input2Rect.top + input2Rect.height / 2;
const endX = input2Rect.left + input2Rect.width / 2;

cy.get('.f-connection-drag-handle')
.trigger('mousedown', { button: 0, clientY: startY, clientX: startX, force: true })
.trigger('mousemove', { clientY: endY, clientX: endX, force: true })
.trigger('pointerup', { clientY: endY, clientX: endX, force: true });

cy.get('#connection_113').should('not.exist');
cy.get('#connection_114').should('exist');
});
});
});
});
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading