Skip to content

Commit

Permalink
feat: Optimized reassign and create connection
Browse files Browse the repository at this point in the history
  • Loading branch information
siarheihuzarevich committed Jan 17, 2025
1 parent 200071b commit ec697c3
Show file tree
Hide file tree
Showing 19 changed files with 107 additions and 184 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<f-flow fDraggable (fLoaded)="onLoaded()" (fNodeIntersectedWithConnections)="onNodeIntersectedWithConnection($event)">
<f-flow fDraggable
(fLoaded)="onLoaded()"
emitWhenNodeIntersectedWithConnection
(fNodeIntersectedWithConnections)="onNodeIntersectedWithConnection($event)">
<f-canvas>
@for (connection of connections; track connection.inputId) {
<f-connection [fReassignDisabled]="false"
Expand Down
2 changes: 1 addition & 1 deletion projects/f-flow/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@foblex/flow",
"version": "17.0.8",
"version": "17.0.9",
"description": "An Angular library designed to simplify the creation and manipulation of dynamic flow. Provides components for flows, nodes, and connections, automating node manipulation and inter-node connections.",
"main": "index.js",
"types": "index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { EmitSelectionChangeEventRequest } from './emit-selection-change-event-r
import { FComponentsStore } from '../../../f-storage';
import { FDraggableDataContext, FSelectionChangeEvent } from '../../../f-draggable';
import { GetCurrentSelectionRequest, ICurrentSelection } from '../../f-selection';
import { NotifyTransformChangedRequest } from '../../../f-storage/features/notify-transform-changed';
import { NotifyTransformChangedRequest } from '../../../f-storage';

@Injectable()
@FExecutionRegister(EmitSelectionChangeEventRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export const F_CONNECTION_DRAG_HANDLE_CLASS = 'f-connection-drag-handle';
})
export class FConnectionDragHandleComponent implements IHasHostElement {

public point!: IPoint;
public radius: number = 8;

public readonly class: string = F_CONNECTION_DRAG_HANDLE_CLASS;

public get hostElement(): SVGCircleElement {
Expand All @@ -29,9 +32,9 @@ export class FConnectionDragHandleComponent implements IHasHostElement {
}

public redraw(penultimatePoint: IPoint, endPoint: IPoint): void {
const point = this._calculateCircleCenter(penultimatePoint, endPoint, 8);
this.hostElement.setAttribute('cx', point.x.toString());
this.hostElement.setAttribute('cy', point.y.toString());
this.point = this._calculateCircleCenter(penultimatePoint, endPoint, 8);
this.hostElement.setAttribute('cx', this.point.x.toString());
this.hostElement.setAttribute('cy', this.point.y.toString());
}

private _calculateCircleCenter(start: IPoint, end: IPoint, radius: number): IPoint {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,54 @@
import { IHandler } from '@foblex/mediator';
import { IPointerEvent } from '@foblex/drag-toolkit';
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { FComponentsStore } from '../../../../f-storage';
import { FConnectorBase, isNodeOutlet, isNodeOutput } from '../../../../f-connectors';
import { FNodeBase } from '../../../../f-node';
import { CreateConnectionPreparationRequest } from './create-connection-preparation.request';
import { FExecutionRegister, FMediator } from '@foblex/mediator';
import { CreateConnectionFromOutletPreparationRequest } from './create-connection-from-outlet-preparation';
import { CreateConnectionFromOutputPreparationRequest } from './create-connection-from-output-preparation';
import { FDraggableDataContext } from '../../../f-draggable-data-context';

@Injectable()
@FExecutionRegister(CreateConnectionPreparationRequest)
export class CreateConnectionPreparationExecution
implements IHandler<CreateConnectionPreparationRequest, void> {

constructor(
private fComponentsStore: FComponentsStore,
private fMediator: FMediator,
) {
}
private _fMediator = inject(FMediator);
private _fComponentsStore = inject(FComponentsStore);
private _fDraggableDataContext = inject(FDraggableDataContext);

public handle(request: CreateConnectionPreparationRequest): void {
if (!this._isValid(request)) {
return;
}

if (isNodeOutlet(request.event.targetElement)) {
this.fMediator.send<void>(new CreateConnectionFromOutletPreparationRequest(request.event));
} else if (this.isNodeOutput(request.event.targetElement, this.getNode(request.event))) {
this.fMediator.send<void>(new CreateConnectionFromOutputPreparationRequest(request.event));
this._fMediator.send<void>(new CreateConnectionFromOutletPreparationRequest(request.event));
} else if (this.isNodeOutput(request.event.targetElement, this._getNode(request.event)!)) {
this._fMediator.send<void>(new CreateConnectionFromOutputPreparationRequest(request.event));
}
}

private getNode(event: IPointerEvent): FNodeBase {
return this.fComponentsStore
.fNodes.find(n => n.isContains(event.targetElement))!;
private _isValid(request: CreateConnectionPreparationRequest): boolean {
return !!this._getNode(request.event) && this._isValidConditions();
}

private _getNode(event: IPointerEvent): FNodeBase | undefined {
return this._fComponentsStore
.fNodes.find(n => n.isContains(event.targetElement));
}

private _isValidConditions(): boolean {
return !this._fDraggableDataContext.draggableItems.length && !!this._fComponentsStore.fTempConnection;
}

private isNodeOutput(targetElement: HTMLElement, node: FNodeBase): boolean {
return isNodeOutput(targetElement) && !this.getOutlets(node).length;
}

private getOutlets(node: FNodeBase): FConnectorBase[] {
return this.fComponentsStore.fOutlets.filter((x) => node.isContains(x.hostElement));
return this._fComponentsStore.fOutlets.filter((x) => node.isContains(x.hostElement));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ export * from './create-connection-from-output-preparation';
export * from './create-connection-preparation.execution';

export * from './create-connection-preparation.request';

export * from './create-connection-preparation.validator';
7 changes: 1 addition & 6 deletions projects/f-flow/src/f-draggable/connections/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import {
CreateConnectionFromOutputPreparationExecution,
CreateConnectionFromOutputPreparationValidator,
CreateConnectionPreparationExecution,
CreateConnectionPreparationValidator,
GetCanBeConnectedOutputByOutletExecution,
GetCanBeConnectedOutputByOutletValidator
} from './create-connection';
import {
ReassignConnectionFinalizeExecution,
ReassignConnectionPreparationExecution, ReassignConnectionPreparationValidator,
ReassignConnectionPreparationExecution,
} from './reassign-connection';
import { GetInputUnderPointerExecution, GetInputUnderPointerValidator } from './get-input-under-pointer';

Expand All @@ -37,11 +36,7 @@ export const CONNECTIONS_PROVIDERS = [

CreateConnectionPreparationExecution,

CreateConnectionPreparationValidator,

ReassignConnectionFinalizeExecution,

ReassignConnectionPreparationExecution,

ReassignConnectionPreparationValidator,
];
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export * from './reassign-connection-preparation.execution';

export * from './reassign-connection-preparation.request';

export * from './reassign-connection-preparation.validator';
Original file line number Diff line number Diff line change
@@ -1,73 +1,82 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ReassignConnectionPreparationRequest } from './reassign-connection-preparation.request';
import { IPoint, ITransformModel, Point } from '@foblex/2d';
import { FComponentsStore } from '../../../../f-storage';
import { FDraggableDataContext } from '../../../f-draggable-data-context';
import { UpdateItemAndChildrenLayersRequest } from '../../../../domain';
import { FExecutionRegister, FMediator, IExecution } from '@foblex/mediator';
import { F_CONNECTION_DRAG_HANDLE_CLASS, FConnectionBase } from '../../../../f-connection';
import { FConnectionBase } from '../../../../f-connection';
import { ReassignConnectionDragHandler } from '../reassign-connection.drag-handler';
import { BrowserService } from '@foblex/platform';

@Injectable()
@FExecutionRegister(ReassignConnectionPreparationRequest)
export class ReassignConnectionPreparationExecution implements IExecution<ReassignConnectionPreparationRequest, void> {

private get transform(): ITransformModel {
return this.fComponentsStore.fCanvas!.transform;
}
private _fMediator = inject(FMediator);
private _fComponentsStore = inject(FComponentsStore);
private _fDraggableDataContext = inject(FDraggableDataContext);

private _fConnection: FConnectionBase | undefined;

private get flowHost(): HTMLElement {
return this.fComponentsStore.fFlow!.hostElement;
private get _transform(): ITransformModel {
return this._fComponentsStore.fCanvas!.transform;
}

private get fConnections(): FConnectionBase[] {
return this.fComponentsStore.fConnections;
private get _fHost(): HTMLElement {
return this._fComponentsStore.fFlow!.hostElement;
}

constructor(
private fComponentsStore: FComponentsStore,
private fDraggableDataContext: FDraggableDataContext,
private fMediator: FMediator,
private fBrowser: BrowserService
) {
private get _fConnections(): FConnectionBase[] {
return this._fComponentsStore.fConnections;
}

public handle(request: ReassignConnectionPreparationRequest): void {
const connectionToReassign = this.getConnectionHandler(
this.getDragHandleElement(request.event.getPosition())
)!;
if (connectionToReassign.fDraggingDisabled) {
if (!this._isValid(request)) {
return;
}

this.fMediator.send<void>(
new UpdateItemAndChildrenLayersRequest(
connectionToReassign, this.fComponentsStore.fCanvas!.fConnectionsContainer.nativeElement
)
);
this._fDraggableDataContext.onPointerDownScale = this._transform.scale;
this._fDraggableDataContext.onPointerDownPosition = Point.fromPoint(request.event.getPosition())
.elementTransform(this._fHost).div(this._transform.scale);
this._fDraggableDataContext.draggableItems = [
new ReassignConnectionDragHandler(this._fMediator, this._fComponentsStore, this._fConnection!)
];

setTimeout(() => this._updateConnectionLayer());
}

this.fDraggableDataContext.onPointerDownScale = this.transform.scale;
private _isValid(request: ReassignConnectionPreparationRequest): boolean {
this._fConnection = this._getConnectionToReassign(this._getPointInFlow(request));
return !!this._fConnection && !this._fDraggableDataContext.draggableItems.length;
}

this.fDraggableDataContext.onPointerDownPosition = Point.fromPoint(request.event.getPosition())
.elementTransform(this.flowHost).div(this.transform.scale);
private _getPointInFlow(request: ReassignConnectionPreparationRequest): IPoint {
return Point.fromPoint(request.event.getPosition())
.elementTransform(this._fHost)
.sub(this._transform.scaledPosition).sub(this._transform.position)
.div(this._transform.scale);
}

this.fDraggableDataContext.draggableItems = [
new ReassignConnectionDragHandler(this.fMediator, this.fComponentsStore, connectionToReassign)
];
private _getConnectionToReassign(position: IPoint): FConnectionBase | undefined {
const connections = this._getConnectionsFromPoint(position);
return connections.length ? connections[0] : undefined;
}

private getDragHandleElement(position: IPoint): HTMLElement {
return this.getElementsFromPoint(position).filter((x) => {
return !!this.getConnectionHandler(x as HTMLElement) && x.classList.contains(F_CONNECTION_DRAG_HANDLE_CLASS);
}).find((x) => !!x)!;
private _getConnectionsFromPoint(position: IPoint): FConnectionBase[] {
return this._fConnections.filter((x) => {
return this._isPointInsideCircle(position, x.fDragHandle.point) && !x.fDraggingDisabled;
});
}

private getElementsFromPoint(position: IPoint): HTMLElement[] {
return this.fBrowser.document.elementsFromPoint(position.x, position.y) as HTMLElement[];
private _isPointInsideCircle(point: IPoint, circleCenter: IPoint): boolean {
return (point.x - circleCenter.x) ** 2 + (point.y - circleCenter.y) ** 2 <= 8 ** 2;
}

public getConnectionHandler(element: HTMLElement | SVGElement): FConnectionBase | undefined {
return this.fConnections.find(c => c.isContains(element));
private _updateConnectionLayer(): void {
this._fMediator.execute<void>(
new UpdateItemAndChildrenLayersRequest(
this._fConnection!, this._fComponentsStore.fCanvas!.fConnectionsContainer.nativeElement
)
);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,7 @@ export class IsConnectionUnderNodeExecution implements IExecution<IsConnectionUn

private _calculateConnectionsUnderNode(fNode: FNodeBase): FConnectionBase[] {
const fNodeRect = this._fMediator.send<IRoundedRect>(new GetNormalizedElementRectRequest(fNode.hostElement, true));
return this._fComponentsStore.fConnections.filter((x) => {
return this._isConnectionHasIntersectionsWithNode(x, fNodeRect);
});
return this._fComponentsStore.fConnections.filter((x) => this._isConnectionHasIntersectionsWithNode(x, fNodeRect));
}

private _isConnectionHasIntersectionsWithNode(fConnection: FConnectionBase, fNodeRect: IRoundedRect): boolean {
Expand Down
2 changes: 2 additions & 0 deletions projects/f-flow/src/f-draggable/f-draggable-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export abstract class FDraggableBase extends DragAndDropBase {

public abstract fNodeIntersectedWithConnections: EventEmitter<FNodeIntersectedWithConnections>;

public abstract emitWhenNodeIntersectedWithConnection: boolean;

public abstract fCreateNode: EventEmitter<FCreateNodeEvent>;

public abstract fReassignConnection: EventEmitter<FReassignConnectionEvent>;
Expand Down
Loading

0 comments on commit ec697c3

Please sign in to comment.