From f5eece6df6e556a0ec0bbfa26ec623ce368f60ef Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sat, 10 Aug 2024 16:15:12 +0200 Subject: [PATCH 01/10] feat: Added minimap functionality https://github.com/Foblex/f-flow/issues/26 --- package.json | 2 +- projects/f-flow/package.json | 2 +- .../emit-transform-changes.execution.ts | 19 ++++ .../emit-transform-changes.request.ts | 3 + .../domain/emit-transform-changes/index.ts | 3 + projects/f-flow/src/domain/index.ts | 4 + projects/f-flow/src/domain/providers.ts | 6 ++ .../subscribe-on-transform-changes/index.ts | 3 + ...ubscribe-on-transform-changes.execution.ts | 20 +++++ .../subscribe-on-transform-changes.request.ts | 3 + .../src/f-canvas/f-canvas.component.scss | 4 + .../f-flow/src/f-canvas/f-canvas.component.ts | 5 +- projects/f-flow/src/f-canvas/index.ts | 2 - projects/f-flow/src/f-canvas/providers.ts | 3 - .../src/f-draggable/e-f-draggable-type.ts | 4 +- .../src/f-draggable/f-draggable-base.ts | 1 - .../src/f-draggable/f-draggable.directive.ts | 31 ++++++- .../f-draggable/i-f-drag-and-drop-plugin.ts | 15 ++++ projects/f-flow/src/f-draggable/index.ts | 2 + projects/f-flow/src/f-draggable/providers.ts | 5 +- .../selection-area.drag-handle.ts | 3 +- .../single-select/single-select.validator.ts | 7 +- projects/f-flow/src/f-flow.module.ts | 6 ++ .../f-flow/src/f-flow/f-flow.component.html | 2 +- .../f-flow/src/f-flow/f-flow.component.ts | 3 +- ...flow-point-from-minimap-point.execution.ts | 39 ++++++++ ...e-flow-point-from-minimap-point.request.ts | 13 +++ .../index.ts | 3 + .../src/f-minimap/domain/f-minimap-data.ts | 11 +++ .../domain/f-minimap.drag-handler.ts | 52 +++++++++++ projects/f-flow/src/f-minimap/domain/index.ts | 11 +++ .../domain/minimap-drag-finalize/index.ts | 7 ++ .../minimap-drag-finalize.execution.ts | 20 +++++ .../minimap-drag-finalize.request.ts | 9 ++ .../minimap-drag-finalize.validator.ts | 21 +++++ .../domain/minimap-drag-finalize/providers.ts | 9 ++ .../domain/minimap-drag-preparation/index.ts | 7 ++ .../minimap-drag-preparation.execution.ts | 55 ++++++++++++ .../minimap-drag-preparation.request.ts | 11 +++ .../minimap-drag-preparation.validator.ts | 19 ++++ .../minimap-drag-preparation/providers.ts | 9 ++ .../f-flow/src/f-minimap/domain/providers.ts | 12 +++ .../f-minimap/f-minimap-canvas.directive.ts | 69 +++++++++++++++ .../src/f-minimap/f-minimap-flow.directive.ts | 88 +++++++++++++++++++ .../src/f-minimap/f-minimap-view.directive.ts | 69 +++++++++++++++ .../src/f-minimap/f-minimap.component.html | 6 ++ .../src/f-minimap/f-minimap.component.scss | 8 ++ .../src/f-minimap/f-minimap.component.ts | 82 +++++++++++++++++ projects/f-flow/src/f-minimap/index.ts | 13 +++ projects/f-flow/src/f-minimap/providers.ts | 15 ++++ .../f-flow/src/f-node/f-node.directive.ts | 10 ++- .../src/f-storage/f-components-store.ts | 2 +- .../f-flow/src/f-storage/f-transform-store.ts | 8 ++ projects/f-flow/src/f-storage/index.ts | 2 + .../src/{f-canvas => }/f-zoom/f-zoom-base.ts | 12 +-- .../{f-canvas => }/f-zoom/f-zoom.directive.ts | 2 +- .../f-flow/src/{f-canvas => }/f-zoom/index.ts | 2 + projects/f-flow/src/f-zoom/providers.ts | 6 ++ projects/f-flow/src/file.ts | 7 -- projects/f-flow/src/public-api.ts | 4 + .../components/flow/flow.component.html | 1 + .../components/flow/flow.component.scss | 22 ++++- public/docs/en/environment.ts | 30 ++++--- public/docs/en/f-external-item-directive.md | 71 +++++++++++++++ public/docs/en/f-minimap-component.md | 62 +++++++++++++ public/docs/en/f-visual-programming-flow.md | 1 + public/docs/en/f-zoom-directive.md | 2 +- src/index.html | 6 +- 68 files changed, 1008 insertions(+), 58 deletions(-) create mode 100644 projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.execution.ts create mode 100644 projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.request.ts create mode 100644 projects/f-flow/src/domain/emit-transform-changes/index.ts create mode 100644 projects/f-flow/src/domain/subscribe-on-transform-changes/index.ts create mode 100644 projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts create mode 100644 projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.request.ts create mode 100644 projects/f-flow/src/f-draggable/i-f-drag-and-drop-plugin.ts create mode 100644 projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts create mode 100644 projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.request.ts create mode 100644 projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/index.ts create mode 100644 projects/f-flow/src/f-minimap/domain/f-minimap-data.ts create mode 100644 projects/f-flow/src/f-minimap/domain/f-minimap.drag-handler.ts create mode 100644 projects/f-flow/src/f-minimap/domain/index.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/index.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.execution.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.request.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.validator.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/providers.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/index.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.request.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts create mode 100644 projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/providers.ts create mode 100644 projects/f-flow/src/f-minimap/domain/providers.ts create mode 100644 projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts create mode 100644 projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts create mode 100644 projects/f-flow/src/f-minimap/f-minimap-view.directive.ts create mode 100644 projects/f-flow/src/f-minimap/f-minimap.component.html create mode 100644 projects/f-flow/src/f-minimap/f-minimap.component.scss create mode 100644 projects/f-flow/src/f-minimap/f-minimap.component.ts create mode 100644 projects/f-flow/src/f-minimap/index.ts create mode 100644 projects/f-flow/src/f-minimap/providers.ts create mode 100644 projects/f-flow/src/f-storage/f-transform-store.ts rename projects/f-flow/src/{f-canvas => }/f-zoom/f-zoom-base.ts (89%) rename projects/f-flow/src/{f-canvas => }/f-zoom/f-zoom.directive.ts (96%) rename projects/f-flow/src/{f-canvas => }/f-zoom/index.ts (69%) create mode 100644 projects/f-flow/src/f-zoom/providers.ts delete mode 100644 projects/f-flow/src/file.ts create mode 100644 public/docs/en/f-external-item-directive.md create mode 100644 public/docs/en/f-minimap-component.md diff --git a/package.json b/package.json index baa4b04..4616145 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@angular/platform-browser": "^18.1.0", "@angular/platform-browser-dynamic": "^18.1.0", "@angular/router": "^18.1.0", - "@foblex/core": "^1.1.2", + "@foblex/core": "^1.1.4", "@foblex/f-docs": "^1.2.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", diff --git a/projects/f-flow/package.json b/projects/f-flow/package.json index bda06f2..e5ca258 100644 --- a/projects/f-flow/package.json +++ b/projects/f-flow/package.json @@ -31,7 +31,7 @@ "peerDependencies": { "@angular/common": ">=12.0.0", "@angular/core": ">=12.0.0", - "@foblex/core": ">=1.1.2", + "@foblex/core": ">=1.1.4", "rxjs": ">=6.6.0" }, "dependencies": { diff --git a/projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.execution.ts b/projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.execution.ts new file mode 100644 index 0000000..9559e4d --- /dev/null +++ b/projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.execution.ts @@ -0,0 +1,19 @@ +import { EmitTransformChangesRequest } from './emit-transform-changes.request'; +import { Injectable } from '@angular/core'; +import { FExecutionRegister, IExecution } from '../../infrastructure'; +import { FTransformStore } from '../../f-storage'; + +@Injectable() +@FExecutionRegister(EmitTransformChangesRequest) +export class EmitTransformChangesExecution + implements IExecution { + + constructor( + private fTransformStore: FTransformStore, + ) { + } + + public handle(request: EmitTransformChangesRequest): void { + this.fTransformStore.changes.next(); + } +} diff --git a/projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.request.ts b/projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.request.ts new file mode 100644 index 0000000..7234ffd --- /dev/null +++ b/projects/f-flow/src/domain/emit-transform-changes/emit-transform-changes.request.ts @@ -0,0 +1,3 @@ +export class EmitTransformChangesRequest { + +} diff --git a/projects/f-flow/src/domain/emit-transform-changes/index.ts b/projects/f-flow/src/domain/emit-transform-changes/index.ts new file mode 100644 index 0000000..fcf458a --- /dev/null +++ b/projects/f-flow/src/domain/emit-transform-changes/index.ts @@ -0,0 +1,3 @@ +export * from './emit-transform-changes.execution'; + +export * from './emit-transform-changes.request'; diff --git a/projects/f-flow/src/domain/index.ts b/projects/f-flow/src/domain/index.ts index 5869093..a13bc8b 100644 --- a/projects/f-flow/src/domain/index.ts +++ b/projects/f-flow/src/domain/index.ts @@ -2,6 +2,8 @@ export * from './clear-selection'; export * from './create-connection-markers'; +export * from './emit-transform-changes'; + export * from './get-can-be-selected-items'; export * from './get-connection-line'; @@ -30,6 +32,8 @@ export * from './select-all'; export * from './select-and-update-node-layer'; +export * from './subscribe-on-transform-changes'; + export * from './intersections'; export * from './update-item-layer'; diff --git a/projects/f-flow/src/domain/providers.ts b/projects/f-flow/src/domain/providers.ts index add5b27..311bb49 100644 --- a/projects/f-flow/src/domain/providers.ts +++ b/projects/f-flow/src/domain/providers.ts @@ -18,6 +18,8 @@ import { GetCanBeSelectedItemsExecution } from './get-can-be-selected-items'; import { IsConnectionUnderNodeExecution } from './is-connection-under-node'; import { SelectAndUpdateNodeLayerExecution } from './select-and-update-node-layer'; import { GetExternalNodesRectExecution } from './get-external-nodes-rect'; +import { EmitTransformChangesExecution } from './emit-transform-changes'; +import { SubscribeOnTransformChangesExecution } from './subscribe-on-transform-changes'; export const COMMON_PROVIDERS = [ @@ -25,6 +27,8 @@ export const COMMON_PROVIDERS = [ CreateConnectionMarkersExecution, + EmitTransformChangesExecution, + GetCanBeSelectedItemsExecution, GetConnectionLineExecution, @@ -51,6 +55,8 @@ export const COMMON_PROVIDERS = [ SelectAndUpdateNodeLayerExecution, + SubscribeOnTransformChangesExecution, + UpdateItemLayerExecution, diff --git a/projects/f-flow/src/domain/subscribe-on-transform-changes/index.ts b/projects/f-flow/src/domain/subscribe-on-transform-changes/index.ts new file mode 100644 index 0000000..e3263c1 --- /dev/null +++ b/projects/f-flow/src/domain/subscribe-on-transform-changes/index.ts @@ -0,0 +1,3 @@ +export * from './subscribe-on-transform-changes.execution'; + +export * from './subscribe-on-transform-changes.request'; diff --git a/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts new file mode 100644 index 0000000..03e8edb --- /dev/null +++ b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts @@ -0,0 +1,20 @@ +import { SubscribeOnTransformChangesRequest } from './subscribe-on-transform-changes.request'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { FTransformStore } from '../../f-storage'; +import { FExecutionRegister, IExecution } from '../../infrastructure'; + +@Injectable() +@FExecutionRegister(SubscribeOnTransformChangesRequest) +export class SubscribeOnTransformChangesExecution + implements IExecution> { + + constructor( + private fTransformStore: FTransformStore, + ) { + } + + public handle(request: SubscribeOnTransformChangesRequest): Observable { + return this.fTransformStore.changes; + } +} diff --git a/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.request.ts b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.request.ts new file mode 100644 index 0000000..3e2bb4c --- /dev/null +++ b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.request.ts @@ -0,0 +1,3 @@ +export class SubscribeOnTransformChangesRequest { + +} diff --git a/projects/f-flow/src/f-canvas/f-canvas.component.scss b/projects/f-flow/src/f-canvas/f-canvas.component.scss index 3484249..feb6575 100644 --- a/projects/f-flow/src/f-canvas/f-canvas.component.scss +++ b/projects/f-flow/src/f-canvas/f-canvas.component.scss @@ -7,4 +7,8 @@ top: 0; transform-origin: 0 0; pointer-events: none; + + &.f-scaled-animate { // to prevent jerking when moving the canvas through the minimap + transition: transform 0.09s; + } } diff --git a/projects/f-flow/src/f-canvas/f-canvas.component.ts b/projects/f-flow/src/f-canvas/f-canvas.component.ts index c67f205..fd8a4a1 100644 --- a/projects/f-flow/src/f-canvas/f-canvas.component.ts +++ b/projects/f-flow/src/f-canvas/f-canvas.component.ts @@ -10,7 +10,7 @@ import { FCanvasChangeEvent } from './domain'; import { FComponentsStore } from '../f-storage'; import { FNodeBase } from '../f-node'; import { FFlowMediator } from '../infrastructure'; -import { GetNodesRectRequest } from '../domain'; +import { EmitTransformChangesRequest, GetNodesRectRequest } from '../domain'; @Component({ selector: 'f-canvas', @@ -67,7 +67,7 @@ export class FCanvasComponent extends FCanvasBase implements OnInit { constructor( private elementReference: ElementRef, private fMediator: FFlowMediator, - private fComponentsStore: FComponentsStore + private fComponentsStore: FComponentsStore, ) { super(); } @@ -79,6 +79,7 @@ export class FCanvasComponent extends FCanvasBase implements OnInit { public override redraw(): void { this.fComponentsStore.fBackground?.setTransform(this.transform); this.hostElement.setAttribute("style", `transform: ${ TransformModelExtensions.toString(this.transform) }`); + this.fMediator.send(new EmitTransformChangesRequest()); } public override redrawWithAnimation(): void { diff --git a/projects/f-flow/src/f-canvas/index.ts b/projects/f-flow/src/f-canvas/index.ts index b627333..f700b7a 100644 --- a/projects/f-flow/src/f-canvas/index.ts +++ b/projects/f-flow/src/f-canvas/index.ts @@ -1,7 +1,5 @@ export * from './domain'; -export * from './f-zoom'; - export * from './f-canvas.component'; export * from './f-canvas-base'; diff --git a/projects/f-flow/src/f-canvas/providers.ts b/projects/f-flow/src/f-canvas/providers.ts index fbb301a..2a0d2b2 100644 --- a/projects/f-flow/src/f-canvas/providers.ts +++ b/projects/f-flow/src/f-canvas/providers.ts @@ -1,9 +1,6 @@ import { FCanvasComponent } from './f-canvas.component'; -import { FZoomDirective } from './f-zoom'; export const F_CANVAS_PROVIDERS = [ FCanvasComponent, - - FZoomDirective ]; diff --git a/projects/f-flow/src/f-draggable/e-f-draggable-type.ts b/projects/f-flow/src/f-draggable/e-f-draggable-type.ts index 49c028c..d9f2905 100644 --- a/projects/f-flow/src/f-draggable/e-f-draggable-type.ts +++ b/projects/f-flow/src/f-draggable/e-f-draggable-type.ts @@ -4,14 +4,14 @@ export enum EFDraggableType { NODE_RESIZE = 'node-resize', - NODE_ROTATE = 'node-rotate', - REASSIGN_CONNECTION = 'reassign-connection', CREATE_CONNECTION = 'create-connection', CONNECTION = 'connection', + MINI_MAP = 'minimap', + CANVAS = 'canvas', PALETTE_ITEM = 'palette-item', diff --git a/projects/f-flow/src/f-draggable/f-draggable-base.ts b/projects/f-flow/src/f-draggable/f-draggable-base.ts index 91a77b8..9667e1e 100644 --- a/projects/f-flow/src/f-draggable/f-draggable-base.ts +++ b/projects/f-flow/src/f-draggable/f-draggable-base.ts @@ -60,7 +60,6 @@ export abstract class FDraggableBase implements IHasHostElement { protected constructor( protected ngZone: ICanRunOutsideAngular | undefined ) { - console.log('FDraggableBase', this.ngZone); } private onMouseDown = (event: MouseEvent) => { diff --git a/projects/f-flow/src/f-draggable/f-draggable.directive.ts b/projects/f-flow/src/f-draggable/f-draggable.directive.ts index 9b9713f..fc32c48 100644 --- a/projects/f-flow/src/f-draggable/f-draggable.directive.ts +++ b/projects/f-flow/src/f-draggable/f-draggable.directive.ts @@ -1,12 +1,12 @@ import { - AfterViewInit, + AfterViewInit, ContentChild, ContentChildren, Directive, ElementRef, EventEmitter, Inject, Input, NgZone, OnDestroy, - OnInit, Optional, Output + OnInit, Optional, Output, QueryList } from "@angular/core"; import { F_DRAGGABLE, FDraggableBase } from './f-draggable-base'; import { FComponentsStore } from '../f-storage'; @@ -27,12 +27,13 @@ import { } from './connections'; import { FSelectionChangeEvent } from './f-selection-change-event'; import { FFlowMediator } from '../infrastructure'; -import { GetSelectionRequest } from '../domain'; +import { EmitTransformChangesRequest, GetSelectionRequest } from '../domain'; import { isExternalItem } from '../f-external-item'; import { SingleSelectRequest } from './single-select'; import { NodeResizeFinalizeRequest, NodeResizePreparationRequest } from './node-resize'; import { SelectionAreaFinalizeRequest, SelectionAreaPreparationRequest } from './selection-area'; import { ICanRunOutsideAngular } from './i-can-run-outside-angular'; +import { F_DRAG_AND_DROP_PLUGIN, IFDragAndDropPlugin } from './i-f-drag-and-drop-plugin'; @Directive({ selector: "f-flow[fDraggable]", @@ -67,6 +68,9 @@ export class FDraggableDirective extends FDraggableBase implements OnInit, After @Output() public override fCreateConnection: EventEmitter = new EventEmitter(); + @ContentChildren(F_DRAG_AND_DROP_PLUGIN, { descendants: true }) + private plugins!: QueryList; + constructor( private elementReference: ElementRef, private fDraggableDataContext: FDraggableDataContext, @@ -90,6 +94,10 @@ export class FDraggableDirective extends FDraggableBase implements OnInit, After this.fDraggableDataContext.reset(); let result: boolean = event.isMouseLeftButton(); + this.plugins.forEach((p) => { + p.onPointerDown?.(event); + }); + this.fMediator.send(new SingleSelectRequest(event)); this.fMediator.send(new ReassignConnectionPreparationRequest(event)); @@ -99,11 +107,15 @@ export class FDraggableDirective extends FDraggableBase implements OnInit, After if (!result) { this.finalizeDragSequence(); } - return result; } protected override prepareDragSequence(event: IPointerEvent) { + + this.plugins.forEach((p) => { + p.prepareDragSequence?.(event); + }); + this.fMediator.send(new SelectionAreaPreparationRequest(event)); this.fMediator.send(new NodeResizePreparationRequest(event)); @@ -125,6 +137,11 @@ export class FDraggableDirective extends FDraggableBase implements OnInit, After } protected override onSelect(event: Event): void { + + this.plugins.forEach((p) => { + p.onSelect?.(event); + }); + if (this.isTargetItemExternal(event)) { event.preventDefault(); } @@ -154,6 +171,11 @@ export class FDraggableDirective extends FDraggableBase implements OnInit, After } public override onPointerUp(event: IPointerEvent): void { + + this.plugins.forEach((p) => { + p.onPointerUp?.(event); + }); + this.fMediator.send(new ReassignConnectionFinalizeRequest(event)); this.fMediator.send(new CreateConnectionFinalizeRequest(event)); @@ -185,6 +207,7 @@ export class FDraggableDirective extends FDraggableBase implements OnInit, After } this.fSelectionChange.emit(this.fMediator.send(new GetSelectionRequest())); this.fDraggableDataContext.isSelectedChanged = false; + this.fMediator.send(new EmitTransformChangesRequest()); } public ngOnDestroy(): void { diff --git a/projects/f-flow/src/f-draggable/i-f-drag-and-drop-plugin.ts b/projects/f-flow/src/f-draggable/i-f-drag-and-drop-plugin.ts new file mode 100644 index 0000000..4d5baa9 --- /dev/null +++ b/projects/f-flow/src/f-draggable/i-f-drag-and-drop-plugin.ts @@ -0,0 +1,15 @@ +import { IPointerEvent } from '@foblex/core'; +import { InjectionToken } from '@angular/core'; + +export const F_DRAG_AND_DROP_PLUGIN: InjectionToken = new InjectionToken('F_DRAG_AND_DROP_PLUGIN'); + +export interface IFDragAndDropPlugin { + + onSelect?(event: Event): void; + + onPointerDown?(event: IPointerEvent): void; + + prepareDragSequence?(event: IPointerEvent): void; + + onPointerUp?(event: IPointerEvent): void; +} diff --git a/projects/f-flow/src/f-draggable/index.ts b/projects/f-flow/src/f-draggable/index.ts index 7ecb3fc..2237981 100644 --- a/projects/f-flow/src/f-draggable/index.ts +++ b/projects/f-flow/src/f-draggable/index.ts @@ -24,6 +24,8 @@ export * from './f-selection-change-event'; export * from './i-can-run-outside-angular'; +export * from './i-f-drag-and-drop-plugin'; + export * from './i-draggable-item'; export * from './providers'; diff --git a/projects/f-flow/src/f-draggable/providers.ts b/projects/f-flow/src/f-draggable/providers.ts index ac9ee4c..c49b6d0 100644 --- a/projects/f-flow/src/f-draggable/providers.ts +++ b/projects/f-flow/src/f-draggable/providers.ts @@ -5,6 +5,7 @@ import { EXTERNAL_ITEM_PROVIDERS } from './external-item'; import { NODE_PROVIDERS } from './node'; import { NODE_RESIZE_PROVIDERS } from './node-resize'; import { SELECTION_AREA_PROVIDERS } from './selection-area'; +import { F_MINIMAP_DRAG_AND_DROP_PROVIDERS } from '../f-minimap'; export const F_DRAGGABLE_PROVIDERS = [ @@ -20,5 +21,7 @@ export const F_DRAGGABLE_PROVIDERS = [ ...NODE_RESIZE_PROVIDERS, - ...SELECTION_AREA_PROVIDERS + ...SELECTION_AREA_PROVIDERS, + + ...F_MINIMAP_DRAG_AND_DROP_PROVIDERS ]; diff --git a/projects/f-flow/src/f-draggable/selection-area/selection-area.drag-handle.ts b/projects/f-flow/src/f-draggable/selection-area/selection-area.drag-handle.ts index 2a217cb..cf483d6 100644 --- a/projects/f-flow/src/f-draggable/selection-area/selection-area.drag-handle.ts +++ b/projects/f-flow/src/f-draggable/selection-area/selection-area.drag-handle.ts @@ -3,7 +3,7 @@ import { IDraggableItem } from '../i-draggable-item'; import { EFDraggableType } from '../e-f-draggable-type'; import { FComponentsStore } from '../../f-storage'; import { FDraggableDataContext } from '../f-draggable-data-context'; -import { ISelectableWithRect } from '../../domain'; +import { EmitTransformChangesRequest, ISelectableWithRect } from '../../domain'; import { ISelectable } from '../../f-connection'; import { FFlowMediator } from '../../infrastructure'; import { GetCanBeSelectedItemsRequest } from '../../domain/get-can-be-selected-items/get-can-be-selected-items-request'; @@ -61,6 +61,7 @@ export class SelectionAreaDragHandle implements IDraggableItem { this.selectedByMove.push(item.element); } }); + this.fMediator.send(new EmitTransformChangesRequest()); } public complete(): void { diff --git a/projects/f-flow/src/f-draggable/single-select/single-select.validator.ts b/projects/f-flow/src/f-draggable/single-select/single-select.validator.ts index 7d32cb6..20c86cb 100644 --- a/projects/f-flow/src/f-draggable/single-select/single-select.validator.ts +++ b/projects/f-flow/src/f-draggable/single-select/single-select.validator.ts @@ -2,17 +2,20 @@ import { Injectable } from '@angular/core'; import { FValidatorRegister, IValidator } from '../../infrastructure'; import { SingleSelectRequest } from './single-select.request'; import { FComponentsStore } from '../../f-storage'; +import { FDraggableDataContext } from '../f-draggable-data-context'; @Injectable() @FValidatorRegister(SingleSelectRequest) export class SingleSelectValidator implements IValidator { constructor( - private fComponentsStore: FComponentsStore + private fComponentsStore: FComponentsStore, + private fDraggableDataContext: FDraggableDataContext ) { } public handle(request: SingleSelectRequest): boolean { - return this.fComponentsStore.fFlow!.hostElement.contains(request.event.targetElement); + return this.fComponentsStore.fFlow!.hostElement.contains(request.event.targetElement) + && !this.fDraggableDataContext.draggableItems.length; } } diff --git a/projects/f-flow/src/f-flow.module.ts b/projects/f-flow/src/f-flow.module.ts index ffaa38e..f92adda 100644 --- a/projects/f-flow/src/f-flow.module.ts +++ b/projects/f-flow/src/f-flow.module.ts @@ -10,16 +10,20 @@ import { FDraggableDirective } from './f-draggable'; import { F_EXTERNAL_ITEM_PROVIDERS } from './f-external-item'; import { F_SELECTION_AREA_PROVIDERS } from './f-selection-area'; import { F_LINE_ALIGNMENT_PROVIDERS } from './f-line-alignment'; +import { F_MINIMAP_PROVIDERS } from './f-minimap'; +import { F_ZOOM_PROVIDERS } from './f-zoom'; @NgModule({ declarations: [ ...F_BACKGROUND_PROVIDERS, ...F_CANVAS_PROVIDERS, + ...F_ZOOM_PROVIDERS, ...F_CONNECTION_PROVIDERS, ...F_CONNECTORS_PROVIDERS, ...F_EXTERNAL_ITEM_PROVIDERS, ...F_FLOW_PROVIDERS, ...F_LINE_ALIGNMENT_PROVIDERS, + ...F_MINIMAP_PROVIDERS, ...F_NODE_PROVIDERS, ...F_SELECTION_AREA_PROVIDERS, @@ -31,11 +35,13 @@ import { F_LINE_ALIGNMENT_PROVIDERS } from './f-line-alignment'; exports: [ ...F_BACKGROUND_PROVIDERS, ...F_CANVAS_PROVIDERS, + ...F_ZOOM_PROVIDERS, ...F_CONNECTION_PROVIDERS, ...F_CONNECTORS_PROVIDERS, ...F_EXTERNAL_ITEM_PROVIDERS, ...F_FLOW_PROVIDERS, ...F_LINE_ALIGNMENT_PROVIDERS, + ...F_MINIMAP_PROVIDERS, ...F_NODE_PROVIDERS, ...F_SELECTION_AREA_PROVIDERS, diff --git a/projects/f-flow/src/f-flow/f-flow.component.html b/projects/f-flow/src/f-flow/f-flow.component.html index 45dffb9..7f1a10a 100644 --- a/projects/f-flow/src/f-flow/f-flow.component.html +++ b/projects/f-flow/src/f-flow/f-flow.component.html @@ -9,5 +9,5 @@ - + diff --git a/projects/f-flow/src/f-flow/f-flow.component.ts b/projects/f-flow/src/f-flow/f-flow.component.ts index f8c6d91..82b6530 100644 --- a/projects/f-flow/src/f-flow/f-flow.component.ts +++ b/projects/f-flow/src/f-flow/f-flow.component.ts @@ -20,7 +20,7 @@ import { FDraggableDataContext, FSelectionChangeEvent } from '../f-draggable'; import { FConnectionFactory } from '../f-connection'; -import { FComponentsStore } from '../f-storage'; +import { FComponentsStore, FTransformStore } from '../f-storage'; let uniqueId: number = 0; @@ -35,6 +35,7 @@ let uniqueId: number = 0; providers: [ FFlowMediator, FComponentsStore, + FTransformStore, FDraggableDataContext, FConnectionFactory, ...F_DRAGGABLE_PROVIDERS, diff --git a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts new file mode 100644 index 0000000..8dfc27e --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts @@ -0,0 +1,39 @@ +import { IPoint, Point, PointExtensions, RectExtensions } from '@foblex/core'; +import { CalculateFlowPointFromMinimapPointRequest } from './calculate-flow-point-from-minimap-point.request'; +import { Injectable } from '@angular/core'; +import { FComponentsStore } from '../../../f-storage'; +import { FExecutionRegister, IExecution } from '../../../infrastructure'; +import { FMinimapData } from '../f-minimap-data'; + +@Injectable() +@FExecutionRegister(CalculateFlowPointFromMinimapPointRequest) +export class CalculateFlowPointFromMinimapPointExecution + implements IExecution { + + private get canvasScale(): number { + return this.fComponentsStore.fCanvas!.transform.scale; + } + + constructor( + private fComponentsStore: FComponentsStore + ) { + } + + public handle(payload: CalculateFlowPointFromMinimapPointRequest): IPoint { + const positionInFlow = PointExtensions.sum( + RectExtensions.mult(payload.minimap.viewBox, this.canvasScale), + this.getScaledPoint(payload.eventPoint, payload.minimap) + ); + + return PointExtensions.sub( + payload.canvasPosition, + PointExtensions.sub(positionInFlow, payload.flowRect.gravityCenter) + ); + } + + public getScaledPoint(point: IPoint, minimap: FMinimapData): Point { + return Point.fromPoint(point).sub( + RectExtensions.fromElement(minimap.element) + ).mult(minimap.scale).mult(this.canvasScale); + } +} diff --git a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.request.ts b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.request.ts new file mode 100644 index 0000000..fadd147 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.request.ts @@ -0,0 +1,13 @@ +import { IPoint, IRect } from '@foblex/core'; +import { FMinimapData } from '../f-minimap-data'; + +export class CalculateFlowPointFromMinimapPointRequest { + + constructor( + public flowRect: IRect, + public canvasPosition: IPoint, + public eventPoint: IPoint, + public minimap: FMinimapData + ) { + } +} diff --git a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/index.ts b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/index.ts new file mode 100644 index 0000000..bd1be67 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/index.ts @@ -0,0 +1,3 @@ +export * from './calculate-flow-point-from-minimap-point.request'; + +export * from './calculate-flow-point-from-minimap-point.execution'; diff --git a/projects/f-flow/src/f-minimap/domain/f-minimap-data.ts b/projects/f-flow/src/f-minimap/domain/f-minimap-data.ts new file mode 100644 index 0000000..3a3af04 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/f-minimap-data.ts @@ -0,0 +1,11 @@ +import { IRect, RectExtensions } from '@foblex/core'; + +export class FMinimapData { + + constructor( + public element: SVGSVGElement, + public scale: number = 1, + public viewBox: IRect = RectExtensions.initialize(0, 0, 0, 0) + ) { + } +} diff --git a/projects/f-flow/src/f-minimap/domain/f-minimap.drag-handler.ts b/projects/f-flow/src/f-minimap/domain/f-minimap.drag-handler.ts new file mode 100644 index 0000000..f7f67e7 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/f-minimap.drag-handler.ts @@ -0,0 +1,52 @@ +import { IPoint, IRect, Point } from '@foblex/core'; +import { FComponentsStore } from '../../f-storage'; +import { EFDraggableType, IDraggableItem } from '../../f-draggable'; +import { FFlowMediator } from '../../infrastructure'; +import { CalculateFlowPointFromMinimapPointRequest } from './calculate-flow-point-from-minimap-point'; +import { FMinimapData } from './f-minimap-data'; + +export class FMinimapDragHandler implements IDraggableItem { + + public readonly type: EFDraggableType = EFDraggableType.MINI_MAP; + + private lastDifference: IPoint | null = null; + + constructor( + private fComponentsStore: FComponentsStore, + private fMediator: FFlowMediator, + private flowRect: IRect, + private canvasPosition: IPoint, + private eventPoint: IPoint, + private minimap: FMinimapData + ) { + } + + public initialize(): void { + this.fComponentsStore.fCanvas?.hostElement.classList.add('f-scaled-animate'); + } + + public move(difference: IPoint): void { + if (this.lastDifference && this.isSamePoint(difference, this.lastDifference)) { + return; + } + + this.lastDifference = difference; + this.fComponentsStore.fCanvas!.setPosition(this.getNewPosition(Point.fromPoint(this.eventPoint).add(difference))); + this.fComponentsStore.fCanvas!.redraw(); + } + + private isSamePoint(point1: IPoint, point2: IPoint): boolean { + return point1.x === point2.x && point1.y === point2.y; + } + + private getNewPosition(eventPoint: IPoint): IPoint { + return this.fMediator.send(new CalculateFlowPointFromMinimapPointRequest( + this.flowRect, this.canvasPosition, eventPoint, this.minimap + )); + } + + public complete(): void { + this.fComponentsStore.fCanvas?.hostElement.classList.remove('f-scaled-animate'); + this.fComponentsStore.fCanvas!.completeDrag(); + } +} diff --git a/projects/f-flow/src/f-minimap/domain/index.ts b/projects/f-flow/src/f-minimap/domain/index.ts new file mode 100644 index 0000000..916f821 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/index.ts @@ -0,0 +1,11 @@ +export * from './calculate-flow-point-from-minimap-point'; + +export * from './minimap-drag-finalize'; + +export * from './minimap-drag-preparation'; + +export * from './f-minimap.drag-handler'; + +export * from './f-minimap-data'; + +export * from './providers'; diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/index.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/index.ts new file mode 100644 index 0000000..1b4eb98 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/index.ts @@ -0,0 +1,7 @@ +export * from './providers'; + +export * from './minimap-drag-finalize.execution'; + +export * from './minimap-drag-finalize.request'; + +export * from './minimap-drag-finalize.validator'; diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.execution.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.execution.ts new file mode 100644 index 0000000..cf4e289 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.execution.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { MinimapDragFinalizeRequest } from './minimap-drag-finalize.request'; +import { FExecutionRegister, IExecution } from '../../../infrastructure'; +import { FDraggableDataContext } from '../../../f-draggable'; + +@Injectable() +@FExecutionRegister(MinimapDragFinalizeRequest) +export class MinimapDragFinalizeExecution implements IExecution { + + constructor( + private fDraggableDataContext: FDraggableDataContext, + ) { + } + + public handle(request: MinimapDragFinalizeRequest): void { + this.fDraggableDataContext.draggableItems.forEach((x) => { + x.complete?.(); + }); + } +} diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.request.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.request.ts new file mode 100644 index 0000000..df99d04 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.request.ts @@ -0,0 +1,9 @@ +import { IPointerEvent } from '@foblex/core'; + +export class MinimapDragFinalizeRequest { + + constructor( + public event: IPointerEvent + ) { + } +} diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.validator.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.validator.ts new file mode 100644 index 0000000..285e9b1 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/minimap-drag-finalize.validator.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { MinimapDragFinalizeRequest } from './minimap-drag-finalize.request'; +import { FValidatorRegister, IValidator } from '../../../infrastructure'; +import { FDraggableDataContext } from '../../../f-draggable'; +import { FMinimapDragHandler } from '../f-minimap.drag-handler'; + +@Injectable() +@FValidatorRegister(MinimapDragFinalizeRequest) +export class MinimapDragFinalizeValidator implements IValidator { + + constructor( + private fDraggableDataContext: FDraggableDataContext + ) { + } + + public handle(request: MinimapDragFinalizeRequest): boolean { + return this.fDraggableDataContext.draggableItems.some( + (x) => x.constructor.name === FMinimapDragHandler.name + ); + } +} diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/providers.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/providers.ts new file mode 100644 index 0000000..e8cc376 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-finalize/providers.ts @@ -0,0 +1,9 @@ +import { MinimapDragFinalizeExecution } from './minimap-drag-finalize.execution'; +import { MinimapDragFinalizeValidator } from './minimap-drag-finalize.validator'; + +export const MINIMAP_DRAG_FINALIZE_PROVIDERS = [ + + MinimapDragFinalizeExecution, + + MinimapDragFinalizeValidator, +]; diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/index.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/index.ts new file mode 100644 index 0000000..b9a6305 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/index.ts @@ -0,0 +1,7 @@ +export * from './providers'; + +export * from './minimap-drag-preparation.execution'; + +export * from './minimap-drag-preparation.request'; + +export * from './minimap-drag-preparation.validator'; diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts new file mode 100644 index 0000000..b097745 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@angular/core'; +import { MinimapDragPreparationRequest } from './minimap-drag-preparation.request'; +import { IPoint, IRect, Point, RectExtensions } from '@foblex/core'; +import { FExecutionRegister, FFlowMediator, IExecution } from '../../../infrastructure'; +import { FComponentsStore } from '../../../f-storage'; +import { FMinimapDragHandler } from '../f-minimap.drag-handler'; +import { FDraggableDataContext } from '../../../f-draggable'; +import { CalculateFlowPointFromMinimapPointRequest } from '../calculate-flow-point-from-minimap-point'; +import { FMinimapData } from '../f-minimap-data'; + +@Injectable() +@FExecutionRegister(MinimapDragPreparationRequest) +export class MinimapDragPreparationExecution implements IExecution { + + private get flowHost(): HTMLElement { + return this.fComponentsStore.fFlow!.hostElement; + } + + constructor( + private fComponentsStore: FComponentsStore, + private fMediator: FFlowMediator, + private fDraggableDataContext: FDraggableDataContext, + ) { + } + + public handle(request: MinimapDragPreparationRequest): void { + const eventPoint = request.event.getPosition(); + const startCanvasPosition = Point.fromPoint(this.fComponentsStore.fCanvas!.transform.position); + + + this.fComponentsStore.fCanvas!.setPosition(this.getNewPosition(eventPoint, request.minimap)); + this.fComponentsStore.fCanvas!.redraw(); + this.fComponentsStore.fCanvas!.completeDrag(); + + this.fDraggableDataContext.onPointerDownScale = 1; + this.fDraggableDataContext.onPointerDownPosition = Point.fromPoint(eventPoint).elementTransform(this.flowHost); + this.fDraggableDataContext.draggableItems = [ + new FMinimapDragHandler( + this.fComponentsStore, this.fMediator, this.getFlowRect(), + startCanvasPosition, eventPoint, request.minimap, + ) + ]; + } + + private getNewPosition(eventPoint: IPoint, minimap: FMinimapData): IPoint { + return this.fMediator.send(new CalculateFlowPointFromMinimapPointRequest( + this.getFlowRect(), Point.fromPoint(this.fComponentsStore.fCanvas!.transform.position), + eventPoint, minimap, + )); + } + + private getFlowRect(): IRect { + return RectExtensions.fromElement(this.flowHost); + } +} diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.request.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.request.ts new file mode 100644 index 0000000..4ec5dd4 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.request.ts @@ -0,0 +1,11 @@ +import { IPointerEvent } from '@foblex/core'; +import { FMinimapData } from '../f-minimap-data'; + +export class MinimapDragPreparationRequest { + + constructor( + public event: IPointerEvent, + public minimap: FMinimapData + ) { + } +} diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts new file mode 100644 index 0000000..93d07f6 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { MinimapDragPreparationRequest } from './minimap-drag-preparation.request'; +import { FValidatorRegister, IValidator } from '../../../infrastructure'; +import { FDraggableDataContext } from '../../../f-draggable'; + +@Injectable() +@FValidatorRegister(MinimapDragPreparationRequest) +export class MinimapDragPreparationValidator implements IValidator { + + constructor( + private fDraggableDataContext: FDraggableDataContext + ) { + } + + public handle(request: MinimapDragPreparationRequest): boolean { + return !this.fDraggableDataContext.draggableItems.length && + !!request.event.targetElement.closest('.f-minimap'); + } +} diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/providers.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/providers.ts new file mode 100644 index 0000000..5d54260 --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/providers.ts @@ -0,0 +1,9 @@ +import { MinimapDragPreparationExecution } from './minimap-drag-preparation.execution'; +import { MinimapDragPreparationValidator } from './minimap-drag-preparation.validator'; + +export const MINIMAP_DRAG_PREPARATION_PROVIDERS = [ + + MinimapDragPreparationExecution, + + MinimapDragPreparationValidator, +]; diff --git a/projects/f-flow/src/f-minimap/domain/providers.ts b/projects/f-flow/src/f-minimap/domain/providers.ts new file mode 100644 index 0000000..0cb821b --- /dev/null +++ b/projects/f-flow/src/f-minimap/domain/providers.ts @@ -0,0 +1,12 @@ +import { MINIMAP_DRAG_PREPARATION_PROVIDERS } from './minimap-drag-preparation'; +import { MINIMAP_DRAG_FINALIZE_PROVIDERS } from './minimap-drag-finalize'; +import { CalculateFlowPointFromMinimapPointExecution } from './calculate-flow-point-from-minimap-point'; + +export const F_MINIMAP_DRAG_AND_DROP_PROVIDERS = [ + + CalculateFlowPointFromMinimapPointExecution, + + ...MINIMAP_DRAG_FINALIZE_PROVIDERS, + + ...MINIMAP_DRAG_PREPARATION_PROVIDERS, +]; diff --git a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts new file mode 100644 index 0000000..15b86e7 --- /dev/null +++ b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts @@ -0,0 +1,69 @@ +import { + Directive, ElementRef, +} from "@angular/core"; +import { FComponentsStore } from '../f-storage'; +import { + DomElementExtensions, IRect, + RectExtensions +} from '@foblex/core'; +import { FFlowMediator } from '../infrastructure'; +import { FNodeBase } from '../f-node'; + +@Directive({ + selector: 'g[f-minimap-canvas]' +}) +export class FMinimapCanvasDirective { + + public get hostElement(): SVGGElement { + return this.elementReference.nativeElement; + } + + private get flowScale(): number { + return this.fComponentsStore.transform.scale; + } + + constructor( + private elementReference: ElementRef, + private fMediator: FFlowMediator, + private fComponentsStore: FComponentsStore + ) { + } + + public redraw(): void { + this.clearCanvas(); + this.fComponentsStore.fNodes.forEach((x) => this.renderNode(x)); + } + + private clearCanvas(): void { + this.hostElement.innerHTML = ''; + } + + private renderNode(node: FNodeBase): void { + const element = DomElementExtensions.createSvgElement('rect'); + this.configureNodeElement(element, node); + this.hostElement.appendChild(element); + } + + private configureNodeElement(element: SVGRectElement, node: FNodeBase): void { + this.setElementAttributes(element, this.getNodeRect(node)); + this.applyClassList(element, node); + } + + private getNodeRect(node: FNodeBase): IRect { + return RectExtensions.div(RectExtensions.fromElement(node.hostElement), this.flowScale); + } + + private setElementAttributes(element: SVGRectElement, rect: IRect): void { + element.setAttribute('x', rect.x.toString()); + element.setAttribute('y', rect.y.toString()); + element.setAttribute('width', rect.width.toString()); + element.setAttribute('height', rect.height.toString()); + } + + private applyClassList(element: SVGRectElement, node: FNodeBase): void { + element.classList.add('f-component', 'f-minimap-node'); + if (node.isSelected()) { + element.classList.add('f-selected'); + } + } +} diff --git a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts new file mode 100644 index 0000000..81af38e --- /dev/null +++ b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts @@ -0,0 +1,88 @@ +import { + Directive, ElementRef, Input, +} from "@angular/core"; +import { FComponentsStore } from '../f-storage'; +import { IRect, ISize, RectExtensions, SizeExtensions } from '@foblex/core'; +import { FFlowMediator } from '../infrastructure'; +import { FMinimapData } from './domain'; +import { GetNodesRectRequest } from '../domain'; + +@Directive({ + selector: 'svg[f-minimap-flow]' +}) +export class FMinimapFlowDirective { + + public model: FMinimapData; + + @Input() + public fMinSize: number = 3000; + + public get hostElement(): SVGSVGElement { + return this.elementReference.nativeElement; + } + + constructor( + private elementReference: ElementRef, + private fMediator: FFlowMediator, + private fComponentsStore: FComponentsStore + ) { + this.model = new FMinimapData(this.hostElement); + } + + public update(): void { + const nodesRect = this.getProcessedNodesRect(); + const minimapRect = this.getMinimapRect(); + + const scale = this.calculateViewScale(nodesRect, minimapRect); + const viewBox = this.calculateViewBox(nodesRect, minimapRect, scale); + + this.model = new FMinimapData(this.hostElement, scale, viewBox); + this.setViewBox(viewBox); + } + + private getProcessedNodesRect(): IRect { + const rawRect = this.fMediator.send(new GetNodesRectRequest()); + const normalizedRect = this.normalizeRect(rawRect); + return this.ensureMinimumSize(normalizedRect); + } + + private normalizeRect(rect: IRect): IRect { + return RectExtensions.div(rect, this.fComponentsStore.transform.scale); + } + + private ensureMinimumSize(rect: IRect): IRect { + return RectExtensions.initialize( + rect.x - (Math.max(rect.width, this.fMinSize) - rect.width) / 2, + rect.y - (Math.max(rect.height, this.fMinSize) - rect.height) / 2, + Math.max(rect.width, this.fMinSize), + Math.max(rect.height, this.fMinSize) + ); + } + + private getMinimapRect(): IRect { + return RectExtensions.fromElement(this.hostElement); + } + + private calculateViewScale(nodesRect: IRect, minimapRect: IRect): number { + return Math.max(nodesRect.width / minimapRect.width, nodesRect.height / minimapRect.height); + } + + private calculateViewBox(nodesRect: IRect, minimapRect: IRect, scale: number): IRect { + const viewSize = this.calculateViewSize(minimapRect, scale); + return this.calculateCenteredViewBox(nodesRect, viewSize, minimapRect, scale); + } + + private calculateViewSize(minimapRect: IRect, scale: number): ISize { + return SizeExtensions.initialize(minimapRect.width * scale, minimapRect.height * scale); + } + + private calculateCenteredViewBox(nodesRect: IRect, viewSize: ISize, minimapRect: IRect, scale: number): IRect { + const centeredX = nodesRect.x - (viewSize.width - nodesRect.width) / 2 + (minimapRect.width * scale - viewSize.width) / 2; + const centeredY = nodesRect.y - (viewSize.height - nodesRect.height) / 2 + (minimapRect.height * scale - viewSize.height) / 2; + return RectExtensions.initialize(centeredX, centeredY, viewSize.width, viewSize.height); + } + + private setViewBox(viewBox: IRect): void { + this.hostElement.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`); + } +} diff --git a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts new file mode 100644 index 0000000..80054be --- /dev/null +++ b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts @@ -0,0 +1,69 @@ +import { Directive, ElementRef } from "@angular/core"; +import { FComponentsStore } from '../f-storage'; +import { FFlowMediator } from '../infrastructure'; +import { IPoint, IRect, ISize, PointExtensions, RectExtensions, SizeExtensions } from '@foblex/core'; +import { FMinimapFlowDirective } from './f-minimap-flow.directive'; + +@Directive({ + selector: 'rect[f-minimap-view]', + host: { + 'class': 'f-component f-minimap-view', + } +}) +export class FMinimapViewDirective { + + public get hostElement(): SVGRectElement { + return this.elementReference.nativeElement; + } + + private get flowScale(): number { + return this.fComponentsStore!.transform!.scale!; + } + + constructor( + private elementReference: ElementRef, + private fMinimapFlow: FMinimapFlowDirective, + private fMediator: FFlowMediator, + private fComponentsStore: FComponentsStore + ) { + } + + public update(): void { + const targetRect = RectExtensions.fromElement(this.fComponentsStore.flowHost); + const minimapRect = RectExtensions.fromElement(this.fMinimapFlow.hostElement); + + const viewScale = this.calculateViewScale(targetRect, minimapRect); + const viewBox = this.calculateViewBox(targetRect, minimapRect, viewScale); + + this.setAttributes(viewBox); + } + + private calculateViewScale(targetRect: IRect, minimapRect: IRect): number { + return Math.max(targetRect.width / minimapRect.width, targetRect.height / minimapRect.height); + } + + private calculateViewBox(targetRect: IRect, minimapRect: IRect, viewScale: number): IRect { + const viewSize = this.calculateViewSize(minimapRect, viewScale); + const position = this.calculateViewBoxPosition(targetRect, viewSize, minimapRect, viewScale); + const viewBox = RectExtensions.initialize(position.x, position.y, viewSize.width, viewSize.height); + return RectExtensions.div(viewBox, this.flowScale); + } + + private calculateViewSize(minimapRect: IRect, viewScale: number): ISize { + return SizeExtensions.initialize(minimapRect.width * viewScale, minimapRect.height * viewScale); + } + + private calculateViewBoxPosition(targetRect: IRect, viewSize: ISize, minimapRect: IRect, viewScale: number): IPoint { + return PointExtensions.initialize( + targetRect.x - (viewSize.width - targetRect.width) / 2 + (minimapRect.width * viewScale - viewSize.width) / 2, + targetRect.y - (viewSize.height - targetRect.height) / 2 + (minimapRect.height * viewScale - viewSize.height) / 2 + ); + } + + private setAttributes(viewBox: IRect): void { + this.hostElement.setAttribute('x', viewBox.x.toString()); + this.hostElement.setAttribute('y', viewBox.y.toString()); + this.hostElement.setAttribute('width', viewBox.width.toString()); + this.hostElement.setAttribute('height', viewBox.height.toString()); + } +} diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.html b/projects/f-flow/src/f-minimap/f-minimap.component.html new file mode 100644 index 0000000..be5973c --- /dev/null +++ b/projects/f-flow/src/f-minimap/f-minimap.component.html @@ -0,0 +1,6 @@ + + + + + diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.scss b/projects/f-flow/src/f-minimap/f-minimap.component.scss new file mode 100644 index 0000000..7cdb848 --- /dev/null +++ b/projects/f-flow/src/f-minimap/f-minimap.component.scss @@ -0,0 +1,8 @@ +:host { + display: block; + position: absolute; + + svg { + overflow: hidden; + } +} diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.ts b/projects/f-flow/src/f-minimap/f-minimap.component.ts new file mode 100644 index 0000000..d642406 --- /dev/null +++ b/projects/f-flow/src/f-minimap/f-minimap.component.ts @@ -0,0 +1,82 @@ +import { + AfterViewInit, ChangeDetectionStrategy, Component, + ElementRef, Input, OnDestroy, ViewChild, +} from "@angular/core"; +import { FFlowMediator } from '../infrastructure'; +import { debounceTime, merge, Observable, Subscription } from 'rxjs'; +import { SubscribeOnTransformChangesRequest } from '../domain'; +import { FMinimapFlowDirective } from './f-minimap-flow.directive'; +import { FMinimapCanvasDirective } from './f-minimap-canvas.directive'; +import { FMinimapViewDirective } from './f-minimap-view.directive'; +import { FComponentsStore } from '../f-storage'; +import { IPointerEvent } from '@foblex/core'; +import { F_DRAG_AND_DROP_PLUGIN, IFDragAndDropPlugin } from '../f-draggable'; +import { MinimapDragFinalizeRequest, MinimapDragPreparationRequest } from './domain'; + +@Component({ + selector: 'f-minimap', + templateUrl: './f-minimap.component.html', + styleUrls: [ './f-minimap.component.scss' ], + exportAs: 'fComponent', + host: { + 'class': 'f-component f-minimap', + }, + providers: [ + { provide: F_DRAG_AND_DROP_PLUGIN, useExisting: FMinimapComponent }, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FMinimapComponent implements AfterViewInit, OnDestroy, IFDragAndDropPlugin { + + private subscriptions$: Subscription = new Subscription(); + + @ViewChild(FMinimapCanvasDirective, { static: true }) + public fMinimapCanvas!: FMinimapCanvasDirective; + + @ViewChild(FMinimapFlowDirective, { static: true }) + public fMinimapFlow!: FMinimapFlowDirective; + + @ViewChild(FMinimapViewDirective, { static: true }) + public fMinimapView!: FMinimapViewDirective; + + @Input() + public fMinSize: number = 3000; + + constructor( + private elementReference: ElementRef, + private fComponentsStore: FComponentsStore, + private fMediator: FFlowMediator + ) { + } + + public ngAfterViewInit(): void { + this.subscriptions$.add(this.subscribeOnTransformChanges()); + } + + private subscribeOnTransformChanges(): Subscription { + return this.getTransformChanges().pipe(debounceTime(5)).subscribe(() => { + this.fMinimapFlow.update(); + this.fMinimapView.update(); + this.fMinimapCanvas.redraw(); + }); + } + + private getTransformChanges(): Observable { + return merge( + this.fMediator.send>(new SubscribeOnTransformChangesRequest()), + this.fComponentsStore.changes + ); + } + + public onPointerDown(event: IPointerEvent): void { + this.fMediator.send(new MinimapDragPreparationRequest(event, this.fMinimapFlow.model)); + } + + public onPointerUp(event: IPointerEvent): void { + this.fMediator.send(new MinimapDragFinalizeRequest(event)); + } + + public ngOnDestroy(): void { + this.subscriptions$.unsubscribe(); + } +} diff --git a/projects/f-flow/src/f-minimap/index.ts b/projects/f-flow/src/f-minimap/index.ts new file mode 100644 index 0000000..6b5319e --- /dev/null +++ b/projects/f-flow/src/f-minimap/index.ts @@ -0,0 +1,13 @@ +export * from './domain'; + +export * from './f-minimap.component'; + +export * from './f-minimap-canvas.directive'; + +export * from './f-minimap-view.directive'; + +export * from './f-minimap-flow.directive'; + +export * from './providers'; + + diff --git a/projects/f-flow/src/f-minimap/providers.ts b/projects/f-flow/src/f-minimap/providers.ts new file mode 100644 index 0000000..6746459 --- /dev/null +++ b/projects/f-flow/src/f-minimap/providers.ts @@ -0,0 +1,15 @@ +import { FMinimapComponent } from './f-minimap.component'; +import { FMinimapViewDirective } from './f-minimap-view.directive'; +import { FMinimapFlowDirective } from './f-minimap-flow.directive'; +import { FMinimapCanvasDirective } from './f-minimap-canvas.directive'; + +export const F_MINIMAP_PROVIDERS = [ + + FMinimapComponent, + + FMinimapCanvasDirective, + + FMinimapViewDirective, + + FMinimapFlowDirective, +]; diff --git a/projects/f-flow/src/f-node/f-node.directive.ts b/projects/f-flow/src/f-node/f-node.directive.ts index a5fa8db..7446be7 100644 --- a/projects/f-flow/src/f-node/f-node.directive.ts +++ b/projects/f-flow/src/f-node/f-node.directive.ts @@ -19,6 +19,8 @@ import { CalculateConnectorConnectableSideRequest, FConnectorBase } from '../f-connectors'; +import { FFlowMediator } from '../infrastructure'; +import { EmitTransformChangesRequest } from '../domain'; let uniqueId: number = 0; @@ -82,7 +84,8 @@ export class FNodeDirective extends FNodeBase implements OnInit, AfterViewInit, constructor( private elementReference: ElementRef, private renderer: Renderer2, - private fComponentsStore: FComponentsStore + private fComponentsStore: FComponentsStore, + private fMediator: FFlowMediator ) { super(); } @@ -102,6 +105,11 @@ export class FNodeDirective extends FNodeBase implements OnInit, AfterViewInit, this.renderer.setStyle(this.hostElement, styleName, value); } + public override redraw(): void { + super.redraw(); + this.fMediator.send(new EmitTransformChangesRequest()); + } + public ngAfterViewInit(): void { this.subscriptions$.add( this.subscribeOnResizeChanges() diff --git a/projects/f-flow/src/f-storage/f-components-store.ts b/projects/f-flow/src/f-storage/f-components-store.ts index e04765e..9d20cf4 100644 --- a/projects/f-flow/src/f-storage/f-components-store.ts +++ b/projects/f-flow/src/f-storage/f-components-store.ts @@ -29,7 +29,7 @@ export class FComponentsStore { public fBackground: FBackgroundBase | undefined; public fNodes: FNodeBase[] = []; - + public fConnections: FConnectionBase[] = []; public fTempConnection: FConnectionBase | undefined; diff --git a/projects/f-flow/src/f-storage/f-transform-store.ts b/projects/f-flow/src/f-storage/f-transform-store.ts new file mode 100644 index 0000000..2ef7240 --- /dev/null +++ b/projects/f-flow/src/f-storage/f-transform-store.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable() +export class FTransformStore { + + public readonly changes: Subject = new Subject(); +} diff --git a/projects/f-flow/src/f-storage/index.ts b/projects/f-flow/src/f-storage/index.ts index 960450f..aba2bbe 100644 --- a/projects/f-flow/src/f-storage/index.ts +++ b/projects/f-flow/src/f-storage/index.ts @@ -1 +1,3 @@ export * from './f-components-store'; + +export * from './f-transform-store'; diff --git a/projects/f-flow/src/f-canvas/f-zoom/f-zoom-base.ts b/projects/f-flow/src/f-zoom/f-zoom-base.ts similarity index 89% rename from projects/f-flow/src/f-canvas/f-zoom/f-zoom-base.ts rename to projects/f-flow/src/f-zoom/f-zoom-base.ts index 4331c37..c6ba751 100644 --- a/projects/f-flow/src/f-canvas/f-zoom/f-zoom-base.ts +++ b/projects/f-flow/src/f-zoom/f-zoom-base.ts @@ -1,8 +1,8 @@ import { EventExtensions, IPoint, Point, RectExtensions } from '@foblex/core'; import { InjectionToken } from '@angular/core'; -import { FComponentsStore } from '../../f-storage'; -import { isNode } from '../../f-node'; -import { FCanvasBase } from '../f-canvas-base'; +import { FCanvasBase } from '../f-canvas'; +import { FComponentsStore } from '../f-storage'; +import { isNode } from '../f-node'; export const F_ZOOM = new InjectionToken('F_ZOOM'); @@ -59,8 +59,9 @@ export abstract class FZoomBase { private onWheel(event: WheelEvent): void { event.preventDefault(); + const targetElement = event.target as HTMLElement; - if (this.fComponentsStore.fDraggable?.isDragStarted) { + if (this.fComponentsStore.fDraggable?.isDragStarted || targetElement?.closest('[fLockedContext]')) { return; } @@ -81,8 +82,9 @@ export abstract class FZoomBase { private onDoubleClick(event: MouseEvent): void { event.preventDefault(); + const targetElement = event.target as HTMLElement; - if (this.fComponentsStore.fDraggable?.isDragStarted || isNode(event.target as HTMLElement)) { + if (this.fComponentsStore.fDraggable?.isDragStarted || isNode(targetElement) || targetElement?.closest('[fLockedContext]')) { return; } diff --git a/projects/f-flow/src/f-canvas/f-zoom/f-zoom.directive.ts b/projects/f-flow/src/f-zoom/f-zoom.directive.ts similarity index 96% rename from projects/f-flow/src/f-canvas/f-zoom/f-zoom.directive.ts rename to projects/f-flow/src/f-zoom/f-zoom.directive.ts index 51ff77c..34e00e5 100644 --- a/projects/f-flow/src/f-canvas/f-zoom/f-zoom.directive.ts +++ b/projects/f-flow/src/f-zoom/f-zoom.directive.ts @@ -4,7 +4,7 @@ import { } from "@angular/core"; import { BooleanExtensions } from '@foblex/core'; import { F_ZOOM, FZoomBase } from './f-zoom-base'; -import { FComponentsStore } from '../../f-storage'; +import { FComponentsStore } from '../f-storage'; @Directive({ selector: "f-canvas[fZoom]", diff --git a/projects/f-flow/src/f-canvas/f-zoom/index.ts b/projects/f-flow/src/f-zoom/index.ts similarity index 69% rename from projects/f-flow/src/f-canvas/f-zoom/index.ts rename to projects/f-flow/src/f-zoom/index.ts index 7855b45..401f63f 100644 --- a/projects/f-flow/src/f-canvas/f-zoom/index.ts +++ b/projects/f-flow/src/f-zoom/index.ts @@ -1,3 +1,5 @@ export * from './f-zoom-base'; export * from './f-zoom.directive'; + +export * from './providers'; diff --git a/projects/f-flow/src/f-zoom/providers.ts b/projects/f-flow/src/f-zoom/providers.ts new file mode 100644 index 0000000..4ac435a --- /dev/null +++ b/projects/f-flow/src/f-zoom/providers.ts @@ -0,0 +1,6 @@ +import { FZoomDirective } from './f-zoom.directive'; + +export const F_ZOOM_PROVIDERS = [ + + FZoomDirective +]; diff --git a/projects/f-flow/src/file.ts b/projects/f-flow/src/file.ts deleted file mode 100644 index 08cb84c..0000000 --- a/projects/f-flow/src/file.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface IEntity { - - - key: T; - - -} diff --git a/projects/f-flow/src/public-api.ts b/projects/f-flow/src/public-api.ts index 275b7b3..01cebbe 100644 --- a/projects/f-flow/src/public-api.ts +++ b/projects/f-flow/src/public-api.ts @@ -14,6 +14,8 @@ export * from './f-external-item'; export * from './f-line-alignment'; +export * from './f-minimap'; + export * from './f-selection-area'; export * from './f-flow'; @@ -22,6 +24,8 @@ export * from './f-node'; export * from './f-storage'; +export * from './f-zoom'; + export * from './infrastructure'; export * from './f-flow.module'; diff --git a/projects/f-pro-examples/visual-programming/components/flow/flow.component.html b/projects/f-pro-examples/visual-programming/components/flow/flow.component.html index c6db502..f8b4cfb 100644 --- a/projects/f-pro-examples/visual-programming/components/flow/flow.component.html +++ b/projects/f-pro-examples/visual-programming/components/flow/flow.component.html @@ -48,5 +48,6 @@ } + diff --git a/projects/f-pro-examples/visual-programming/components/flow/flow.component.scss b/projects/f-pro-examples/visual-programming/components/flow/flow.component.scss index ca73f26..d8d7cd0 100644 --- a/projects/f-pro-examples/visual-programming/components/flow/flow.component.scss +++ b/projects/f-pro-examples/visual-programming/components/flow/flow.component.scss @@ -4,7 +4,6 @@ height: 100%; } - ::ng-deep .visual-programming-flow { .f-line-alignment { @@ -60,7 +59,6 @@ .f-connection-for-create { .f-connection-path { - stroke: #28374e; stroke-width: 1px; fill: none; } @@ -69,4 +67,24 @@ .f-selection-area { background-color: var(--primary-soft); } + + .f-minimap { + background-color: var(--background-color); + bottom: 16px; + right: 16px; + width: 140px; + height: 120px; + + .f-minimap-node { + fill: var(--primary-text); + + &.f-selected { + fill: var(--primary-1); + } + } + + .f-minimap-view { + fill: var(--primary-soft) + } + } } diff --git a/public/docs/en/environment.ts b/public/docs/en/environment.ts index 88651f6..c31cba3 100644 --- a/public/docs/en/environment.ts +++ b/public/docs/en/environment.ts @@ -44,19 +44,7 @@ function createEnvironment(): IDocsEnvironment { connectorGroup(), connectionGroup(), extendsGroup(), - { - text: 'Pro Examples', - items: [{ - text: 'Visual Programming Flow', - link: 'f-visual-programming-flow', - }, { - text: 'Call Center Flow', - link: 'https://github.com/Foblex/f-flow-example', - }, { - text: 'Scheme Editor', - link: 'https://github.com/Foblex/f-scheme-editor', - }] - } + proExamplesGroup() ], footerNavigation: { editLink: { @@ -197,3 +185,19 @@ function extendsGroup(): INavigationGroup { } } +function proExamplesGroup(): INavigationGroup { + return { + text: 'Pro Examples', + items: [{ + text: 'Visual Programming Flow', + link: 'f-visual-programming-flow', + }, { + text: 'Call Center Flow', + link: 'https://github.com/Foblex/f-flow-example', + }, { + text: 'Scheme Editor', + link: 'https://github.com/Foblex/f-scheme-editor', + }] + } +} + diff --git a/public/docs/en/f-external-item-directive.md b/public/docs/en/f-external-item-directive.md new file mode 100644 index 0000000..daebbc5 --- /dev/null +++ b/public/docs/en/f-external-item-directive.md @@ -0,0 +1,71 @@ +# Input + +**Selector:** [fExternalItem] + +The **FNodeOutputDirective** is a directive that marks an element as an input within a [fNode](f-node-directive). It manages input-specific behaviours, such as allowing multiple connections, handling disabled state, and determining connectability. + +## Inputs + + - `fInputId: string;` The unique identifier for the directive instance. Automatically generated. Default: `f-node-input-${uniqueId++}` + + - `fInputDisabled: boolean;` Indicates whether the input is disabled. A disabled input may have a different visual representation and interaction behavior. Default: `false` + + - `fInputMultiple: boolean;` Determines whether the input allows multiple connectionsDefault: `Default: true` + + - `fOutputConnectableSide: EFConnectableSide;` Indicates the side of the output where the connection can be created. Accepts a value from [EFConnectableSide](e-f-connectable-side) enum. Default: `EFConnectableSide.AUTO` + +## Outputs + + - `isConnected: boolean;` Indicates whether the input is connected. + +## Methods + + - `refresh(): void;` Refreshes the state of the node, typically triggering a re-render or update. + +## Styles + + - `.f-component` A general class applied to all F components for shared styling. + + - `.f-node-input` Specific class for styling the node input element. + + - `.f-node-input-disabled` Applied when the input is disabled. + + - `.f-node-input-multiple` Applied when the input allows multiple connections. + + - `.f-node-input-not-connectable` Applied when the input is not connectable. + + - `.f-node-input-connected` Applied when the input is connected, indicating an active connection. + +## Usage + +```html + + +
+ |:|
|:| +
+
+
+``` + +You can also add **fNodeInput** directive to the element containing the [fNode](f-node-directive) directive +```html + + + |:|
|:| +
+
+``` + +::: info INFO +The [f-connection](f-connection-component) component takes the border-radius of the component into account when connecting +::: + +## Examples + +Example of how to use the [fOutputConnectableSide](f-output-connectable-side) and [fInputConnectableSide](f-input-connectable-side) directives to specify the side of the node that can be connected to. Valid values are top, right, bottom, left, and auto from [EFConnectableSide](e-f-connectable-side) enum. +::: ng-component +[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.html +[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.ts +[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.scss +::: diff --git a/public/docs/en/f-minimap-component.md b/public/docs/en/f-minimap-component.md new file mode 100644 index 0000000..640fc18 --- /dev/null +++ b/public/docs/en/f-minimap-component.md @@ -0,0 +1,62 @@ +# Flow + +**Selector:** f-minimap + +The **FFlowComponent** manages the flow of draggable and connectable elements within a visual canvas. It allows for dynamic creation, positioning, and interaction of elements, supporting features such as element connections, layout calculation, and event handling. + +## Inputs + + - `fFlowId: string;` The unique identifier for the component instance. Automatically generated. Default: `f-flow-${uniqueId++}` + +## Outputs + + - `fLoaded: EventEmitter;` Emits an event when the flow has fully loaded and initialized. + +## Methods + + - `getNodesRect(): IRect;` Returns the bounding rectangle of all nodes in the flow. + + - `getSelection(): FSelectionChangeEvent;` Returns the current selection state of the flow. + + - `selectAll(): void;` Selects all items in the flow. + + - `select(node: string[], connections:[]): void;` Selects the specified nodes and connections in the flow. + + - `clearSelection(): void;` Clears the selection state of all nodes and connections in the flow. + + - `redraw(): void;` Calls the redraw method on all nodes and connections in the flow. + + - `getPositionInFlow(position: IPoint): void;` Returns the position of the point relative to the flow. + +## Styles + + - `.f-component` A general class applied to all F components for shared styling. + + - `.f-flow` Specifically targets the **FFlowComponent**, allowing for unique styling. + +## Usage + +```html + +``` + +## Examples + +#### Basic Example + +Example of two connected nodes without dragging functionality. The nodes are connected by a connection line from the output of the first node to the input of the second node. + +::: ng-component +[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.html +[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.ts +[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.scss +::: + +#### Adding Dragging Functionality + +Let's add the [fDraggable](f-draggable-directive) directive to the f-stream to enable dragging functionality. Also, we need to add the [fDragHandle](f-drag-handle-directive) directive inside [fNode](f-node-directive) to specify the handle for dragging. +::: ng-component +[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.html +[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.ts +[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.scss +::: diff --git a/public/docs/en/f-visual-programming-flow.md b/public/docs/en/f-visual-programming-flow.md index a3b0b04..b1e3a8f 100644 --- a/public/docs/en/f-visual-programming-flow.md +++ b/public/docs/en/f-visual-programming-flow.md @@ -18,6 +18,7 @@ This example demonstrates how to use the Foblex Flow to create a visual programm - Set the background using the [f-background](f-background-component) component. - Line alignment using [f-line-alignment](f-line-alignment-component) component. - Select multiple items using the [f-selection](f-selection-component) component with the `mouse` and by holding down the `Shift key`. +- Minimap using the [f-minimap](f-minimap-component) component. ## Source Code diff --git a/public/docs/en/f-zoom-directive.md b/public/docs/en/f-zoom-directive.md index bf4f84b..846a2fb 100644 --- a/public/docs/en/f-zoom-directive.md +++ b/public/docs/en/f-zoom-directive.md @@ -93,7 +93,7 @@ class Component { ## Example -The following example shows how to enable **zoom** and **pan** functionality in the canvas. Use the **mouse wheel** to **zoom in** and **out**, and **double click** to **reset** the **zoom level**. +The following example shows how to enable **zoom** and **pan** functionality in the canvas. Use the **mouse wheel** to **zoom in** and **out**, and **double click** to **zoom in**. ::: ng-component [component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/zoom-example/zoom-example.component.html diff --git a/src/index.html b/src/index.html index 73fd5b5..c79ef43 100644 --- a/src/index.html +++ b/src/index.html @@ -8,10 +8,6 @@ - - - - @@ -31,9 +27,9 @@ - + From 8a70f48299969d694b287cb28ecdc66052f2c428 Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sat, 10 Aug 2024 16:19:37 +0200 Subject: [PATCH 02/10] feat: added zoneless support --- projects/f-flow/src/infrastructure/pipeline/pipeline.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts index 1c13cfc..881a760 100644 --- a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts +++ b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts @@ -8,8 +8,7 @@ export class Pipeline private validator?: Type>; private execution!: Type>; - - + public handle(request: TRequest, injector: Injector): TResponse | void { let isValid: boolean = true; if (this.validator) { From 22603d6054aa5def511c36247b377f3f5c9a4737 Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sat, 10 Aug 2024 16:32:09 +0200 Subject: [PATCH 03/10] docs: Added minimap documentation --- .../src/f-minimap/f-minimap-flow.directive.ts | 2 +- .../src/f-minimap/f-minimap.component.ts | 2 +- .../src/infrastructure/pipeline/pipeline.ts | 2 +- public/docs/en/environment.ts | 4 ++ public/docs/en/f-minimap-component.md | 45 +++++-------------- 5 files changed, 17 insertions(+), 38 deletions(-) diff --git a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts index 81af38e..31e2fa2 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts @@ -15,7 +15,7 @@ export class FMinimapFlowDirective { public model: FMinimapData; @Input() - public fMinSize: number = 3000; + public fMinSize: number = 1000; public get hostElement(): SVGSVGElement { return this.elementReference.nativeElement; diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.ts b/projects/f-flow/src/f-minimap/f-minimap.component.ts index d642406..84b8f55 100644 --- a/projects/f-flow/src/f-minimap/f-minimap.component.ts +++ b/projects/f-flow/src/f-minimap/f-minimap.component.ts @@ -40,7 +40,7 @@ export class FMinimapComponent implements AfterViewInit, OnDestroy, IFDragAndDro public fMinimapView!: FMinimapViewDirective; @Input() - public fMinSize: number = 3000; + public fMinSize: number = 1000; constructor( private elementReference: ElementRef, diff --git a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts index 881a760..b55238c 100644 --- a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts +++ b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts @@ -8,7 +8,7 @@ export class Pipeline private validator?: Type>; private execution!: Type>; - + public handle(request: TRequest, injector: Injector): TResponse | void { let isValid: boolean = true; if (this.validator) { diff --git a/public/docs/en/environment.ts b/public/docs/en/environment.ts index c31cba3..c9e58f6 100644 --- a/public/docs/en/environment.ts +++ b/public/docs/en/environment.ts @@ -181,6 +181,10 @@ function extendsGroup(): INavigationGroup { link: 'f-line-alignment-component', text: 'Line Alignment', }, + { + link: 'f-minimap-component', + text: 'Minimap', + } ], } } diff --git a/public/docs/en/f-minimap-component.md b/public/docs/en/f-minimap-component.md index 640fc18..c95558e 100644 --- a/public/docs/en/f-minimap-component.md +++ b/public/docs/en/f-minimap-component.md @@ -1,50 +1,33 @@ -# Flow +# Minimap **Selector:** f-minimap -The **FFlowComponent** manages the flow of draggable and connectable elements within a visual canvas. It allows for dynamic creation, positioning, and interaction of elements, supporting features such as element connections, layout calculation, and event handling. +The **FMinimapComponent** provides a miniature view of the larger flow, allowing users to navigate and interact with the flow efficiently. It supports features like zooming, panning, and visual representation of the flow layout. The minimap dynamically updates based on the changes in the main flow, ensuring an accurate representation. ## Inputs - - `fFlowId: string;` The unique identifier for the component instance. Automatically generated. Default: `f-flow-${uniqueId++}` - -## Outputs - - - `fLoaded: EventEmitter;` Emits an event when the flow has fully loaded and initialized. - -## Methods - - - `getNodesRect(): IRect;` Returns the bounding rectangle of all nodes in the flow. - - - `getSelection(): FSelectionChangeEvent;` Returns the current selection state of the flow. - - - `selectAll(): void;` Selects all items in the flow. - - - `select(node: string[], connections:[]): void;` Selects the specified nodes and connections in the flow. - - - `clearSelection(): void;` Clears the selection state of all nodes and connections in the flow. - - - `redraw(): void;` Calls the redraw method on all nodes and connections in the flow. - - - `getPositionInFlow(position: IPoint): void;` Returns the position of the point relative to the flow. + - `fMinSize: number;` The minimum size of the bounding box that encloses all nodes in the minimap. It ensures that the minimap does not shrink below this size, even if the actual flow is smaller. This helps maintain the usability and visibility of the minimap. `Default: 1000`. ## Styles - `.f-component` A general class applied to all F components for shared styling. - - `.f-flow` Specifically targets the **FFlowComponent**, allowing for unique styling. + - `.f-minimap` Specifically targets the **FMinimapComponent**, allowing for unique styling. ## Usage ```html - + + ...// Other components + |:||:| + ``` ## Examples #### Basic Example - -Example of two connected nodes without dragging functionality. The nodes are connected by a connection line from the output of the first node to the input of the second node. + +This example shows a basic implementation of the minimap component within a larger flow, providing users with an overview and easy navigation capabilities. ::: ng-component [component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.html @@ -52,11 +35,3 @@ Example of two connected nodes without dragging functionality. The nodes are con [component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.scss ::: -#### Adding Dragging Functionality - -Let's add the [fDraggable](f-draggable-directive) directive to the f-stream to enable dragging functionality. Also, we need to add the [fDragHandle](f-drag-handle-directive) directive inside [fNode](f-node-directive) to specify the handle for dragging. -::: ng-component -[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.html -[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.ts -[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.scss -::: From a9dc22c27e3928b946e994ab47c1fcdca08158b7 Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sun, 11 Aug 2024 15:36:35 +0200 Subject: [PATCH 04/10] docs: added minimap examples --- package.json | 2 +- .../minimap-basic-example.component.html | 8 +++ .../minimap-basic-example.component.scss | 52 +++++++++++++++++++ .../minimap-basic-example.component.ts | 16 ++++++ .../minimap-scaled-example.component.html | 8 +++ .../minimap-scaled-example.component.scss | 52 +++++++++++++++++++ .../minimap-scaled-example.component.ts | 16 ++++++ projects/f-flow/package.json | 2 +- .../f-connection-drag-handle.component.ts | 2 +- ...flow-point-from-minimap-point.execution.ts | 36 +++++++++---- .../minimap-drag-preparation.execution.ts | 4 +- .../f-minimap/f-minimap-canvas.directive.ts | 9 +++- .../src/f-minimap/f-minimap-flow.directive.ts | 35 +++++++------ .../src/f-minimap/f-minimap-view.directive.ts | 39 ++------------ public/docs/en/environment.ts | 8 +++ public/docs/en/f-minimap-component.md | 47 +++++++++++++++-- public/docs/en/f-zoom-directive.md | 6 +++ 17 files changed, 269 insertions(+), 73 deletions(-) create mode 100644 projects/f-examples/minimap-basic-example/minimap-basic-example.component.html create mode 100644 projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss create mode 100644 projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts create mode 100644 projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html create mode 100644 projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss create mode 100644 projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts diff --git a/package.json b/package.json index 4616145..c1f0a21 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@angular/platform-browser": "^18.1.0", "@angular/platform-browser-dynamic": "^18.1.0", "@angular/router": "^18.1.0", - "@foblex/core": "^1.1.4", + "@foblex/core": "^1.1.6", "@foblex/f-docs": "^1.2.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", diff --git a/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html new file mode 100644 index 0000000..d4bc461 --- /dev/null +++ b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html @@ -0,0 +1,8 @@ + + + +
Node 1
+
Node 2
+
+ +
diff --git a/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss new file mode 100644 index 0000000..d9c0500 --- /dev/null +++ b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss @@ -0,0 +1,52 @@ +:host ::ng-deep { + + .f-connection { + .f-connection-drag-handle { + fill: transparent; + } + + .f-connection-selection { + fill: none; + } + + .f-connection-path { + fill: none; + stroke: #585858; + stroke-width: 2; + } + } + + .f-minimap { + background-color: var(--background-color); + bottom: 16px; + right: 16px; + width: 120px; + height: 120px; + + .f-minimap-node { + fill: var(--primary-text); + + &.f-selected { + fill: var(--primary-1); + } + } + + .f-minimap-view { + fill: var(--primary-soft) + } + } +} + +.f-node { + width: 100px; + padding: 12px; + color: #585858; + display: inline-flex; + justify-content: center; + align-items: center; + text-align: center; + background: #FCFDFE; + border-radius: 4px; + box-shadow: 0 0 1px 1px #E4E3E6; +} + diff --git a/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts new file mode 100644 index 0000000..632ac7a --- /dev/null +++ b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts @@ -0,0 +1,16 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { FFlowModule } from '@foblex/flow'; + +@Component({ + selector: 'minimap-basic-example', + styleUrls: [ './minimap-basic-example.component.scss' ], + templateUrl: './minimap-basic-example.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + FFlowModule + ] +}) +export class MinimapBasicExampleComponent { + +} diff --git a/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html new file mode 100644 index 0000000..30e00a8 --- /dev/null +++ b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html @@ -0,0 +1,8 @@ + + + +
Node 1
+
Node 2
+
+ +
diff --git a/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss new file mode 100644 index 0000000..d9c0500 --- /dev/null +++ b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss @@ -0,0 +1,52 @@ +:host ::ng-deep { + + .f-connection { + .f-connection-drag-handle { + fill: transparent; + } + + .f-connection-selection { + fill: none; + } + + .f-connection-path { + fill: none; + stroke: #585858; + stroke-width: 2; + } + } + + .f-minimap { + background-color: var(--background-color); + bottom: 16px; + right: 16px; + width: 120px; + height: 120px; + + .f-minimap-node { + fill: var(--primary-text); + + &.f-selected { + fill: var(--primary-1); + } + } + + .f-minimap-view { + fill: var(--primary-soft) + } + } +} + +.f-node { + width: 100px; + padding: 12px; + color: #585858; + display: inline-flex; + justify-content: center; + align-items: center; + text-align: center; + background: #FCFDFE; + border-radius: 4px; + box-shadow: 0 0 1px 1px #E4E3E6; +} + diff --git a/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts new file mode 100644 index 0000000..9309b79 --- /dev/null +++ b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts @@ -0,0 +1,16 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { FFlowModule } from '@foblex/flow'; + +@Component({ + selector: 'minimap-scaled-example', + styleUrls: [ './minimap-scaled-example.component.scss' ], + templateUrl: './minimap-scaled-example.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + FFlowModule + ] +}) +export class MinimapScaledExampleComponent { + +} diff --git a/projects/f-flow/package.json b/projects/f-flow/package.json index e5ca258..3bc5e8d 100644 --- a/projects/f-flow/package.json +++ b/projects/f-flow/package.json @@ -31,7 +31,7 @@ "peerDependencies": { "@angular/common": ">=12.0.0", "@angular/core": ">=12.0.0", - "@foblex/core": ">=1.1.4", + "@foblex/core": ">=1.1.6", "rxjs": ">=6.6.0" }, "dependencies": { diff --git a/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts b/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts index b4c0748..435c16f 100644 --- a/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts +++ b/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts @@ -35,7 +35,7 @@ export class FConnectionDragHandleComponent implements IHasHostElement { private calculateCircleCenter(start: IPoint, end: IPoint, radius: number): IPoint { const direction = { x: end.x - start.x, y: end.y - start.y }; - const length = Math.sqrt(direction.x * direction.x + direction.y * direction.y); + const length = Math.sqrt(direction.x * direction.x + direction.y * direction.y) || 1; const unitDirection = { x: direction.x / length, y: direction.y / length }; const scaledDirection = { x: unitDirection.x * radius, y: unitDirection.y * radius }; return { x: end.x - scaledDirection.x, y: end.y - scaledDirection.y }; diff --git a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts index 8dfc27e..f553eb4 100644 --- a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts +++ b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts @@ -1,4 +1,4 @@ -import { IPoint, Point, PointExtensions, RectExtensions } from '@foblex/core'; +import { IPoint, IRect, Point, PointExtensions, RectExtensions } from '@foblex/core'; import { CalculateFlowPointFromMinimapPointRequest } from './calculate-flow-point-from-minimap-point.request'; import { Injectable } from '@angular/core'; import { FComponentsStore } from '../../../f-storage'; @@ -14,26 +14,40 @@ export class CalculateFlowPointFromMinimapPointExecution return this.fComponentsStore.fCanvas!.transform.scale; } + constructor( private fComponentsStore: FComponentsStore ) { } public handle(payload: CalculateFlowPointFromMinimapPointRequest): IPoint { - const positionInFlow = PointExtensions.sum( - RectExtensions.mult(payload.minimap.viewBox, this.canvasScale), - this.getScaledPoint(payload.eventPoint, payload.minimap) - ); - return PointExtensions.sub( payload.canvasPosition, - PointExtensions.sub(positionInFlow, payload.flowRect.gravityCenter) + PointExtensions.sub( + this.getPositionInViewBox(payload.eventPoint, payload.minimap), + this.getNormalizedFlowCenter(payload.flowRect) + ) + ); + } + + private getNormalizedFlowCenter(flowRect: IRect): IPoint { + return Point.fromPoint(flowRect.gravityCenter).sub(flowRect); + } + + private getPositionInViewBox(eventPoint: IPoint, minimap: FMinimapData): IPoint { + const eventPointInFlow = this.normalizeEventPoint(eventPoint, minimap); + return PointExtensions.sum( + eventPointInFlow, + RectExtensions.mult(minimap.viewBox, this.canvasScale) ); } - public getScaledPoint(point: IPoint, minimap: FMinimapData): Point { - return Point.fromPoint(point).sub( - RectExtensions.fromElement(minimap.element) - ).mult(minimap.scale).mult(this.canvasScale); + public normalizeEventPoint(point: IPoint, minimap: FMinimapData): Point { + return this.getEventPointInMinimap(point, minimap) + .mult(minimap.scale).mult(this.canvasScale); + } + + private getEventPointInMinimap(eventPoint: IPoint, minimap: FMinimapData): Point { + return Point.fromPoint(eventPoint).elementTransform(minimap.element as unknown as HTMLElement); } } diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts index b097745..b3da6a5 100644 --- a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts @@ -27,7 +27,6 @@ export class MinimapDragPreparationExecution implements IExecution(new CalculateFlowPointFromMinimapPointRequest( - this.getFlowRect(), Point.fromPoint(this.fComponentsStore.fCanvas!.transform.position), + this.getFlowRect(), + Point.fromPoint(this.fComponentsStore.fCanvas!.transform.position), eventPoint, minimap, )); } diff --git a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts index 15b86e7..9dd9229 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts @@ -3,7 +3,7 @@ import { } from "@angular/core"; import { FComponentsStore } from '../f-storage'; import { - DomElementExtensions, IRect, + DomElementExtensions, IRect, Point, RectExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; @@ -18,6 +18,10 @@ export class FMinimapCanvasDirective { return this.elementReference.nativeElement; } + private get flowHost(): HTMLElement { + return this.fComponentsStore.flowHost; + } + private get flowScale(): number { return this.fComponentsStore.transform.scale; } @@ -50,7 +54,8 @@ export class FMinimapCanvasDirective { } private getNodeRect(node: FNodeBase): IRect { - return RectExtensions.div(RectExtensions.fromElement(node.hostElement), this.flowScale); + const nodeRectInFlow = RectExtensions.elementTransform(RectExtensions.fromElement(node.hostElement), this.flowHost); + return RectExtensions.div(nodeRectInFlow, this.flowScale); } private setElementAttributes(element: SVGRectElement, rect: IRect): void { diff --git a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts index 31e2fa2..e67c650 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts @@ -2,7 +2,7 @@ import { Directive, ElementRef, Input, } from "@angular/core"; import { FComponentsStore } from '../f-storage'; -import { IRect, ISize, RectExtensions, SizeExtensions } from '@foblex/core'; +import { IRect, ISize, Point, RectExtensions, SizeExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; import { FMinimapData } from './domain'; import { GetNodesRectRequest } from '../domain'; @@ -17,6 +17,10 @@ export class FMinimapFlowDirective { @Input() public fMinSize: number = 1000; + private get flowHost(): HTMLElement { + return this.fComponentsStore.flowHost; + } + public get hostElement(): SVGSVGElement { return this.elementReference.nativeElement; } @@ -35,17 +39,23 @@ export class FMinimapFlowDirective { const scale = this.calculateViewScale(nodesRect, minimapRect); const viewBox = this.calculateViewBox(nodesRect, minimapRect, scale); - this.model = new FMinimapData(this.hostElement, scale, viewBox); this.setViewBox(viewBox); } private getProcessedNodesRect(): IRect { - const rawRect = this.fMediator.send(new GetNodesRectRequest()); - const normalizedRect = this.normalizeRect(rawRect); + const normalizedRect = this.normalizeRect(this.getNodesRect()); return this.ensureMinimumSize(normalizedRect); } + private getNodesRect(): IRect { + return RectExtensions.elementTransform(this.fMediator.send(new GetNodesRectRequest()), this.flowHost) + } + + private getMinimapRect(): IRect { + return RectExtensions.elementTransform(RectExtensions.fromElement(this.hostElement), this.flowHost); + } + private normalizeRect(rect: IRect): IRect { return RectExtensions.div(rect, this.fComponentsStore.transform.scale); } @@ -59,30 +69,25 @@ export class FMinimapFlowDirective { ); } - private getMinimapRect(): IRect { - return RectExtensions.fromElement(this.hostElement); - } - private calculateViewScale(nodesRect: IRect, minimapRect: IRect): number { return Math.max(nodesRect.width / minimapRect.width, nodesRect.height / minimapRect.height); } private calculateViewBox(nodesRect: IRect, minimapRect: IRect, scale: number): IRect { - const viewSize = this.calculateViewSize(minimapRect, scale); - return this.calculateCenteredViewBox(nodesRect, viewSize, minimapRect, scale); + return this.calculateCenteredViewBox(nodesRect, this.calculateViewSize(minimapRect, scale)); } private calculateViewSize(minimapRect: IRect, scale: number): ISize { - return SizeExtensions.initialize(minimapRect.width * scale, minimapRect.height * scale); + return SizeExtensions.initialize(minimapRect.width * scale || 0, minimapRect.height * scale || 0); } - private calculateCenteredViewBox(nodesRect: IRect, viewSize: ISize, minimapRect: IRect, scale: number): IRect { - const centeredX = nodesRect.x - (viewSize.width - nodesRect.width) / 2 + (minimapRect.width * scale - viewSize.width) / 2; - const centeredY = nodesRect.y - (viewSize.height - nodesRect.height) / 2 + (minimapRect.height * scale - viewSize.height) / 2; + private calculateCenteredViewBox(nodesRect: IRect, viewSize: ISize): IRect { + const centeredX = nodesRect.x - (viewSize.width - nodesRect.width) / 2; + const centeredY = nodesRect.y - (viewSize.height - nodesRect.height) / 2; return RectExtensions.initialize(centeredX, centeredY, viewSize.width, viewSize.height); } private setViewBox(viewBox: IRect): void { - this.hostElement.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`); + this.hostElement.setAttribute('viewBox', `${ viewBox.x } ${ viewBox.y } ${ viewBox.width } ${ viewBox.height }`); } } diff --git a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts index 80054be..0d8e8ab 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts @@ -1,8 +1,6 @@ import { Directive, ElementRef } from "@angular/core"; import { FComponentsStore } from '../f-storage'; -import { FFlowMediator } from '../infrastructure'; -import { IPoint, IRect, ISize, PointExtensions, RectExtensions, SizeExtensions } from '@foblex/core'; -import { FMinimapFlowDirective } from './f-minimap-flow.directive'; +import { IRect, RectExtensions } from '@foblex/core'; @Directive({ selector: 'rect[f-minimap-view]', @@ -22,47 +20,18 @@ export class FMinimapViewDirective { constructor( private elementReference: ElementRef, - private fMinimapFlow: FMinimapFlowDirective, - private fMediator: FFlowMediator, private fComponentsStore: FComponentsStore ) { } public update(): void { - const targetRect = RectExtensions.fromElement(this.fComponentsStore.flowHost); - const minimapRect = RectExtensions.fromElement(this.fMinimapFlow.hostElement); - - const viewScale = this.calculateViewScale(targetRect, minimapRect); - const viewBox = this.calculateViewBox(targetRect, minimapRect, viewScale); - + const viewBox = RectExtensions.div(RectExtensions.fromElement(this.fComponentsStore.flowHost), this.flowScale); this.setAttributes(viewBox); } - private calculateViewScale(targetRect: IRect, minimapRect: IRect): number { - return Math.max(targetRect.width / minimapRect.width, targetRect.height / minimapRect.height); - } - - private calculateViewBox(targetRect: IRect, minimapRect: IRect, viewScale: number): IRect { - const viewSize = this.calculateViewSize(minimapRect, viewScale); - const position = this.calculateViewBoxPosition(targetRect, viewSize, minimapRect, viewScale); - const viewBox = RectExtensions.initialize(position.x, position.y, viewSize.width, viewSize.height); - return RectExtensions.div(viewBox, this.flowScale); - } - - private calculateViewSize(minimapRect: IRect, viewScale: number): ISize { - return SizeExtensions.initialize(minimapRect.width * viewScale, minimapRect.height * viewScale); - } - - private calculateViewBoxPosition(targetRect: IRect, viewSize: ISize, minimapRect: IRect, viewScale: number): IPoint { - return PointExtensions.initialize( - targetRect.x - (viewSize.width - targetRect.width) / 2 + (minimapRect.width * viewScale - viewSize.width) / 2, - targetRect.y - (viewSize.height - targetRect.height) / 2 + (minimapRect.height * viewScale - viewSize.height) / 2 - ); - } - private setAttributes(viewBox: IRect): void { - this.hostElement.setAttribute('x', viewBox.x.toString()); - this.hostElement.setAttribute('y', viewBox.y.toString()); + this.hostElement.setAttribute('x', '0'); + this.hostElement.setAttribute('y', '0'); this.hostElement.setAttribute('width', viewBox.width.toString()); this.hostElement.setAttribute('height', viewBox.height.toString()); } diff --git a/public/docs/en/environment.ts b/public/docs/en/environment.ts index c9e58f6..67e3059 100644 --- a/public/docs/en/environment.ts +++ b/public/docs/en/environment.ts @@ -25,6 +25,12 @@ import { } from '../../../projects/f-examples/line-alignment-example/line-alignment-example.component'; import { DragToReassignComponent } from '../../../projects/f-examples/drag-to-reassign/drag-to-reassign.component'; import { FlowComponent } from '../../../projects/f-pro-examples/visual-programming/components/flow/flow.component'; +import { + MinimapBasicExampleComponent +} from '../../../projects/f-examples/minimap-basic-example/minimap-basic-example.component'; +import { + MinimapScaledExampleComponent +} from '../../../projects/f-examples/minimap-scaled-example/minimap-scaled-example.component'; export const ENGLISH_ENVIRONMENT: IDocsEnvironment = createEnvironment(); @@ -69,6 +75,8 @@ function createEnvironment(): IDocsEnvironment { { tag: 'background-example', component: BackgroundExampleComponent }, { tag: 'line-alignment-example', component: LineAlignmentExampleComponent }, { tag: 'visual-programming-flow', component: FlowComponent }, + { tag: 'minimap-basic-example', component: MinimapBasicExampleComponent }, + { tag: 'minimap-scaled-example', component: MinimapScaledExampleComponent }, ], socialLinks: [ { icon: 'github', link: 'https://github.com/Foblex/f-flow' }, diff --git a/public/docs/en/f-minimap-component.md b/public/docs/en/f-minimap-component.md index c95558e..377cc5f 100644 --- a/public/docs/en/f-minimap-component.md +++ b/public/docs/en/f-minimap-component.md @@ -16,10 +16,37 @@ The **FMinimapComponent** provides a miniature view of the larger flow, allowing ## Usage +#### Basic Usage + +To add a minimap to your flow, simply include the `FMinimapComponent` within the [f-flow](f-flow-component) component. This provides users with an overview of the flow layout and enhances navigation capabilities. + ```html ...// Other components - |:||:| + |:||:| + +``` + +#### Navigation and Interaction + +For navigation and interaction you need to add [f-draggable](f-draggable-directive) directive to the [f-flow](f-flow-component) component. + +```html + + ...// Other components + |:||:| + +``` + + +#### Custom Scale + +You can set a custom scale for the minimap by using the `fMinSize` input. This allows you to control the size of the minimap based on your requirements. + +```html + + ...// Other components + ``` @@ -29,9 +56,19 @@ The **FMinimapComponent** provides a miniature view of the larger flow, allowing This example shows a basic implementation of the minimap component within a larger flow, providing users with an overview and easy navigation capabilities. -::: ng-component -[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.html -[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.ts -[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.scss +::: ng-component +[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html +[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts +[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss +::: + +#### Custom Scale Example + +This example demonstrates the use of a custom scale for the minimap, allowing you to control the size of the minimap based on your requirements. + +::: ng-component +[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html +[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts +[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss ::: diff --git a/public/docs/en/f-zoom-directive.md b/public/docs/en/f-zoom-directive.md index 846a2fb..fcb550d 100644 --- a/public/docs/en/f-zoom-directive.md +++ b/public/docs/en/f-zoom-directive.md @@ -38,6 +38,8 @@ The **FZoomDirective** directive is used to control the zoom and pan of the canv ## Usage +#### Basic Usage + To enable zoom and pan functionality, set `fZoom` directive to [f-canvas](f-canvas-component) component. ```html @@ -48,6 +50,8 @@ To enable zoom and pan functionality, set `fZoom` directive to [f-canvas](f-canv ``` +#### Tracking Zoom Changes + To track zoom changes, use the `fCanvasChange` output. ```html @@ -58,6 +62,8 @@ To track zoom changes, use the `fCanvasChange` output. ``` +#### Programmatically Controlling Zoom + You can also control the zoom level programmatically by using the methods provided by the directive. ::: code-group From 904f760ba081827559dbc6b823b75dc787f3b4e2 Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sun, 11 Aug 2024 22:09:38 +0200 Subject: [PATCH 05/10] feat: Update to f-docs v1.2.1 --- package.json | 2 +- projects/f-flow/package.json | 2 +- ...ubscribe-on-transform-changes.execution.ts | 10 ++- .../src/f-draggable/f-draggable.directive.ts | 2 +- projects/f-flow/src/f-draggable/providers.ts | 2 +- ...flow-point-from-minimap-point.execution.ts | 1 - .../minimap-drag-preparation.validator.ts | 5 +- .../f-minimap/f-minimap-canvas.directive.ts | 4 +- .../src/f-minimap/f-minimap-flow.directive.ts | 4 +- .../src/f-minimap/f-minimap-view.directive.ts | 2 +- .../src/f-minimap/f-minimap.component.html | 7 +- .../src/f-minimap/f-minimap.component.ts | 9 +-- public/docs/en/f-external-item-directive.md | 71 ------------------- src/styles/styles.scss | 2 +- 14 files changed, 26 insertions(+), 97 deletions(-) delete mode 100644 public/docs/en/f-external-item-directive.md diff --git a/package.json b/package.json index c1f0a21..9c17958 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@angular/platform-browser-dynamic": "^18.1.0", "@angular/router": "^18.1.0", "@foblex/core": "^1.1.6", - "@foblex/f-docs": "^1.2.0", + "@foblex/f-docs": "^1.2.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" diff --git a/projects/f-flow/package.json b/projects/f-flow/package.json index 3bc5e8d..3f8c57b 100644 --- a/projects/f-flow/package.json +++ b/projects/f-flow/package.json @@ -1,6 +1,6 @@ { "name": "@foblex/flow", - "version": "12.4.0", + "version": "12.5.0", "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", diff --git a/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts index 03e8edb..d7d578b 100644 --- a/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts +++ b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts @@ -1,7 +1,7 @@ import { SubscribeOnTransformChangesRequest } from './subscribe-on-transform-changes.request'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { FTransformStore } from '../../f-storage'; +import { merge, Observable } from 'rxjs'; +import { FComponentsStore, FTransformStore } from '../../f-storage'; import { FExecutionRegister, IExecution } from '../../infrastructure'; @Injectable() @@ -11,10 +11,14 @@ export class SubscribeOnTransformChangesExecution constructor( private fTransformStore: FTransformStore, + private fComponentsStore: FComponentsStore ) { } public handle(request: SubscribeOnTransformChangesRequest): Observable { - return this.fTransformStore.changes; + return merge( + this.fTransformStore.changes, + this.fComponentsStore.changes + ); } } diff --git a/projects/f-flow/src/f-draggable/f-draggable.directive.ts b/projects/f-flow/src/f-draggable/f-draggable.directive.ts index fc32c48..f01f093 100644 --- a/projects/f-flow/src/f-draggable/f-draggable.directive.ts +++ b/projects/f-flow/src/f-draggable/f-draggable.directive.ts @@ -1,5 +1,5 @@ import { - AfterViewInit, ContentChild, ContentChildren, + AfterViewInit, ContentChildren, Directive, ElementRef, EventEmitter, Inject, diff --git a/projects/f-flow/src/f-draggable/providers.ts b/projects/f-flow/src/f-draggable/providers.ts index c49b6d0..fe432b7 100644 --- a/projects/f-flow/src/f-draggable/providers.ts +++ b/projects/f-flow/src/f-draggable/providers.ts @@ -5,7 +5,7 @@ import { EXTERNAL_ITEM_PROVIDERS } from './external-item'; import { NODE_PROVIDERS } from './node'; import { NODE_RESIZE_PROVIDERS } from './node-resize'; import { SELECTION_AREA_PROVIDERS } from './selection-area'; -import { F_MINIMAP_DRAG_AND_DROP_PROVIDERS } from '../f-minimap'; +import { F_MINIMAP_DRAG_AND_DROP_PROVIDERS } from '../f-minimap/domain/providers'; export const F_DRAGGABLE_PROVIDERS = [ diff --git a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts index f553eb4..cb0d0b6 100644 --- a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts +++ b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts @@ -14,7 +14,6 @@ export class CalculateFlowPointFromMinimapPointExecution return this.fComponentsStore.fCanvas!.transform.scale; } - constructor( private fComponentsStore: FComponentsStore ) { diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts index 93d07f6..27dc0e7 100644 --- a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts @@ -2,18 +2,21 @@ import { Injectable } from '@angular/core'; import { MinimapDragPreparationRequest } from './minimap-drag-preparation.request'; import { FValidatorRegister, IValidator } from '../../../infrastructure'; import { FDraggableDataContext } from '../../../f-draggable'; +import { FComponentsStore } from '../../../f-storage'; @Injectable() @FValidatorRegister(MinimapDragPreparationRequest) export class MinimapDragPreparationValidator implements IValidator { constructor( + private fComponentsStore: FComponentsStore, private fDraggableDataContext: FDraggableDataContext ) { } public handle(request: MinimapDragPreparationRequest): boolean { return !this.fDraggableDataContext.draggableItems.length && - !!request.event.targetElement.closest('.f-minimap'); + !!request.event.targetElement.closest('.f-minimap') && + this.fComponentsStore.flowHost.contains(request.event.targetElement); } } diff --git a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts index 9dd9229..09f7b69 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts @@ -3,14 +3,14 @@ import { } from "@angular/core"; import { FComponentsStore } from '../f-storage'; import { - DomElementExtensions, IRect, Point, + DomElementExtensions, IRect, RectExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; import { FNodeBase } from '../f-node'; @Directive({ - selector: 'g[f-minimap-canvas]' + selector: 'g[fMinimapCanvas]' }) export class FMinimapCanvasDirective { diff --git a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts index e67c650..e4d221a 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts @@ -2,13 +2,13 @@ import { Directive, ElementRef, Input, } from "@angular/core"; import { FComponentsStore } from '../f-storage'; -import { IRect, ISize, Point, RectExtensions, SizeExtensions } from '@foblex/core'; +import { IRect, ISize, RectExtensions, SizeExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; import { FMinimapData } from './domain'; import { GetNodesRectRequest } from '../domain'; @Directive({ - selector: 'svg[f-minimap-flow]' + selector: 'svg[fMinimapFlow]' }) export class FMinimapFlowDirective { diff --git a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts index 0d8e8ab..897910d 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts @@ -3,7 +3,7 @@ import { FComponentsStore } from '../f-storage'; import { IRect, RectExtensions } from '@foblex/core'; @Directive({ - selector: 'rect[f-minimap-view]', + selector: 'rect[fMinimapView]', host: { 'class': 'f-component f-minimap-view', } diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.html b/projects/f-flow/src/f-minimap/f-minimap.component.html index be5973c..c821849 100644 --- a/projects/f-flow/src/f-minimap/f-minimap.component.html +++ b/projects/f-flow/src/f-minimap/f-minimap.component.html @@ -1,6 +1,5 @@ - - - + + + diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.ts b/projects/f-flow/src/f-minimap/f-minimap.component.ts index 84b8f55..3220245 100644 --- a/projects/f-flow/src/f-minimap/f-minimap.component.ts +++ b/projects/f-flow/src/f-minimap/f-minimap.component.ts @@ -3,12 +3,11 @@ import { ElementRef, Input, OnDestroy, ViewChild, } from "@angular/core"; import { FFlowMediator } from '../infrastructure'; -import { debounceTime, merge, Observable, Subscription } from 'rxjs'; +import { debounceTime, Observable, Subscription } from 'rxjs'; import { SubscribeOnTransformChangesRequest } from '../domain'; import { FMinimapFlowDirective } from './f-minimap-flow.directive'; import { FMinimapCanvasDirective } from './f-minimap-canvas.directive'; import { FMinimapViewDirective } from './f-minimap-view.directive'; -import { FComponentsStore } from '../f-storage'; import { IPointerEvent } from '@foblex/core'; import { F_DRAG_AND_DROP_PLUGIN, IFDragAndDropPlugin } from '../f-draggable'; import { MinimapDragFinalizeRequest, MinimapDragPreparationRequest } from './domain'; @@ -44,7 +43,6 @@ export class FMinimapComponent implements AfterViewInit, OnDestroy, IFDragAndDro constructor( private elementReference: ElementRef, - private fComponentsStore: FComponentsStore, private fMediator: FFlowMediator ) { } @@ -62,10 +60,7 @@ export class FMinimapComponent implements AfterViewInit, OnDestroy, IFDragAndDro } private getTransformChanges(): Observable { - return merge( - this.fMediator.send>(new SubscribeOnTransformChangesRequest()), - this.fComponentsStore.changes - ); + return this.fMediator.send>(new SubscribeOnTransformChangesRequest()); } public onPointerDown(event: IPointerEvent): void { diff --git a/public/docs/en/f-external-item-directive.md b/public/docs/en/f-external-item-directive.md deleted file mode 100644 index daebbc5..0000000 --- a/public/docs/en/f-external-item-directive.md +++ /dev/null @@ -1,71 +0,0 @@ -# Input - -**Selector:** [fExternalItem] - -The **FNodeOutputDirective** is a directive that marks an element as an input within a [fNode](f-node-directive). It manages input-specific behaviours, such as allowing multiple connections, handling disabled state, and determining connectability. - -## Inputs - - - `fInputId: string;` The unique identifier for the directive instance. Automatically generated. Default: `f-node-input-${uniqueId++}` - - - `fInputDisabled: boolean;` Indicates whether the input is disabled. A disabled input may have a different visual representation and interaction behavior. Default: `false` - - - `fInputMultiple: boolean;` Determines whether the input allows multiple connectionsDefault: `Default: true` - - - `fOutputConnectableSide: EFConnectableSide;` Indicates the side of the output where the connection can be created. Accepts a value from [EFConnectableSide](e-f-connectable-side) enum. Default: `EFConnectableSide.AUTO` - -## Outputs - - - `isConnected: boolean;` Indicates whether the input is connected. - -## Methods - - - `refresh(): void;` Refreshes the state of the node, typically triggering a re-render or update. - -## Styles - - - `.f-component` A general class applied to all F components for shared styling. - - - `.f-node-input` Specific class for styling the node input element. - - - `.f-node-input-disabled` Applied when the input is disabled. - - - `.f-node-input-multiple` Applied when the input allows multiple connections. - - - `.f-node-input-not-connectable` Applied when the input is not connectable. - - - `.f-node-input-connected` Applied when the input is connected, indicating an active connection. - -## Usage - -```html - - -
- |:|
|:| -
- - -``` - -You can also add **fNodeInput** directive to the element containing the [fNode](f-node-directive) directive -```html - - - |:|
|:| -
-
-``` - -::: info INFO -The [f-connection](f-connection-component) component takes the border-radius of the component into account when connecting -::: - -## Examples - -Example of how to use the [fOutputConnectableSide](f-output-connectable-side) and [fInputConnectableSide](f-input-connectable-side) directives to specify the side of the node that can be connected to. Valid values are top, right, bottom, left, and auto from [EFConnectableSide](e-f-connectable-side) enum. -::: ng-component -[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.html -[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.ts -[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.scss -::: diff --git a/src/styles/styles.scss b/src/styles/styles.scss index bdf704c..0bc83ec 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -1 +1 @@ -@import "@foblex/f-docs/assets/styles/styles"; +@import "@foblex/f-docs/assets/styles/styles.scss"; From a8c581285d8c9e6f246cbd2f2a07c346ce1ba131 Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sat, 10 Aug 2024 16:19:37 +0200 Subject: [PATCH 06/10] feat: Added zoneless support --- projects/f-flow/src/infrastructure/pipeline/pipeline.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts index 1c13cfc..881a760 100644 --- a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts +++ b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts @@ -8,8 +8,7 @@ export class Pipeline private validator?: Type>; private execution!: Type>; - - + public handle(request: TRequest, injector: Injector): TResponse | void { let isValid: boolean = true; if (this.validator) { From ccbdb990935d0d9ae04d92de47752b50a6b11190 Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sat, 10 Aug 2024 16:32:09 +0200 Subject: [PATCH 07/10] docs: Added minimap documentation --- .../src/f-minimap/f-minimap-flow.directive.ts | 2 +- .../src/f-minimap/f-minimap.component.ts | 2 +- .../src/infrastructure/pipeline/pipeline.ts | 2 +- public/docs/en/environment.ts | 4 ++ public/docs/en/f-minimap-component.md | 45 +++++-------------- 5 files changed, 17 insertions(+), 38 deletions(-) diff --git a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts index 81af38e..31e2fa2 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts @@ -15,7 +15,7 @@ export class FMinimapFlowDirective { public model: FMinimapData; @Input() - public fMinSize: number = 3000; + public fMinSize: number = 1000; public get hostElement(): SVGSVGElement { return this.elementReference.nativeElement; diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.ts b/projects/f-flow/src/f-minimap/f-minimap.component.ts index d642406..84b8f55 100644 --- a/projects/f-flow/src/f-minimap/f-minimap.component.ts +++ b/projects/f-flow/src/f-minimap/f-minimap.component.ts @@ -40,7 +40,7 @@ export class FMinimapComponent implements AfterViewInit, OnDestroy, IFDragAndDro public fMinimapView!: FMinimapViewDirective; @Input() - public fMinSize: number = 3000; + public fMinSize: number = 1000; constructor( private elementReference: ElementRef, diff --git a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts index 881a760..b55238c 100644 --- a/projects/f-flow/src/infrastructure/pipeline/pipeline.ts +++ b/projects/f-flow/src/infrastructure/pipeline/pipeline.ts @@ -8,7 +8,7 @@ export class Pipeline private validator?: Type>; private execution!: Type>; - + public handle(request: TRequest, injector: Injector): TResponse | void { let isValid: boolean = true; if (this.validator) { diff --git a/public/docs/en/environment.ts b/public/docs/en/environment.ts index c31cba3..c9e58f6 100644 --- a/public/docs/en/environment.ts +++ b/public/docs/en/environment.ts @@ -181,6 +181,10 @@ function extendsGroup(): INavigationGroup { link: 'f-line-alignment-component', text: 'Line Alignment', }, + { + link: 'f-minimap-component', + text: 'Minimap', + } ], } } diff --git a/public/docs/en/f-minimap-component.md b/public/docs/en/f-minimap-component.md index 640fc18..c95558e 100644 --- a/public/docs/en/f-minimap-component.md +++ b/public/docs/en/f-minimap-component.md @@ -1,50 +1,33 @@ -# Flow +# Minimap **Selector:** f-minimap -The **FFlowComponent** manages the flow of draggable and connectable elements within a visual canvas. It allows for dynamic creation, positioning, and interaction of elements, supporting features such as element connections, layout calculation, and event handling. +The **FMinimapComponent** provides a miniature view of the larger flow, allowing users to navigate and interact with the flow efficiently. It supports features like zooming, panning, and visual representation of the flow layout. The minimap dynamically updates based on the changes in the main flow, ensuring an accurate representation. ## Inputs - - `fFlowId: string;` The unique identifier for the component instance. Automatically generated. Default: `f-flow-${uniqueId++}` - -## Outputs - - - `fLoaded: EventEmitter;` Emits an event when the flow has fully loaded and initialized. - -## Methods - - - `getNodesRect(): IRect;` Returns the bounding rectangle of all nodes in the flow. - - - `getSelection(): FSelectionChangeEvent;` Returns the current selection state of the flow. - - - `selectAll(): void;` Selects all items in the flow. - - - `select(node: string[], connections:[]): void;` Selects the specified nodes and connections in the flow. - - - `clearSelection(): void;` Clears the selection state of all nodes and connections in the flow. - - - `redraw(): void;` Calls the redraw method on all nodes and connections in the flow. - - - `getPositionInFlow(position: IPoint): void;` Returns the position of the point relative to the flow. + - `fMinSize: number;` The minimum size of the bounding box that encloses all nodes in the minimap. It ensures that the minimap does not shrink below this size, even if the actual flow is smaller. This helps maintain the usability and visibility of the minimap. `Default: 1000`. ## Styles - `.f-component` A general class applied to all F components for shared styling. - - `.f-flow` Specifically targets the **FFlowComponent**, allowing for unique styling. + - `.f-minimap` Specifically targets the **FMinimapComponent**, allowing for unique styling. ## Usage ```html - + + ...// Other components + |:||:| + ``` ## Examples #### Basic Example - -Example of two connected nodes without dragging functionality. The nodes are connected by a connection line from the output of the first node to the input of the second node. + +This example shows a basic implementation of the minimap component within a larger flow, providing users with an overview and easy navigation capabilities. ::: ng-component [component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.html @@ -52,11 +35,3 @@ Example of two connected nodes without dragging functionality. The nodes are con [component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.scss ::: -#### Adding Dragging Functionality - -Let's add the [fDraggable](f-draggable-directive) directive to the f-stream to enable dragging functionality. Also, we need to add the [fDragHandle](f-drag-handle-directive) directive inside [fNode](f-node-directive) to specify the handle for dragging. -::: ng-component -[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.html -[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.ts -[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/draggable-flow/draggable-flow.component.scss -::: From 56460662d015f4ba0e6269d8f6bbc0de6e7b058f Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sun, 11 Aug 2024 15:36:35 +0200 Subject: [PATCH 08/10] docs: Added minimap examples --- package.json | 2 +- .../minimap-basic-example.component.html | 8 +++ .../minimap-basic-example.component.scss | 52 +++++++++++++++++++ .../minimap-basic-example.component.ts | 16 ++++++ .../minimap-scaled-example.component.html | 8 +++ .../minimap-scaled-example.component.scss | 52 +++++++++++++++++++ .../minimap-scaled-example.component.ts | 16 ++++++ projects/f-flow/package.json | 2 +- .../f-connection-drag-handle.component.ts | 2 +- ...flow-point-from-minimap-point.execution.ts | 36 +++++++++---- .../minimap-drag-preparation.execution.ts | 4 +- .../f-minimap/f-minimap-canvas.directive.ts | 9 +++- .../src/f-minimap/f-minimap-flow.directive.ts | 35 +++++++------ .../src/f-minimap/f-minimap-view.directive.ts | 39 ++------------ public/docs/en/environment.ts | 8 +++ public/docs/en/f-minimap-component.md | 47 +++++++++++++++-- public/docs/en/f-zoom-directive.md | 6 +++ 17 files changed, 269 insertions(+), 73 deletions(-) create mode 100644 projects/f-examples/minimap-basic-example/minimap-basic-example.component.html create mode 100644 projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss create mode 100644 projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts create mode 100644 projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html create mode 100644 projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss create mode 100644 projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts diff --git a/package.json b/package.json index 4616145..c1f0a21 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@angular/platform-browser": "^18.1.0", "@angular/platform-browser-dynamic": "^18.1.0", "@angular/router": "^18.1.0", - "@foblex/core": "^1.1.4", + "@foblex/core": "^1.1.6", "@foblex/f-docs": "^1.2.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", diff --git a/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html new file mode 100644 index 0000000..d4bc461 --- /dev/null +++ b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html @@ -0,0 +1,8 @@ + + + +
Node 1
+
Node 2
+
+ +
diff --git a/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss new file mode 100644 index 0000000..d9c0500 --- /dev/null +++ b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss @@ -0,0 +1,52 @@ +:host ::ng-deep { + + .f-connection { + .f-connection-drag-handle { + fill: transparent; + } + + .f-connection-selection { + fill: none; + } + + .f-connection-path { + fill: none; + stroke: #585858; + stroke-width: 2; + } + } + + .f-minimap { + background-color: var(--background-color); + bottom: 16px; + right: 16px; + width: 120px; + height: 120px; + + .f-minimap-node { + fill: var(--primary-text); + + &.f-selected { + fill: var(--primary-1); + } + } + + .f-minimap-view { + fill: var(--primary-soft) + } + } +} + +.f-node { + width: 100px; + padding: 12px; + color: #585858; + display: inline-flex; + justify-content: center; + align-items: center; + text-align: center; + background: #FCFDFE; + border-radius: 4px; + box-shadow: 0 0 1px 1px #E4E3E6; +} + diff --git a/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts new file mode 100644 index 0000000..632ac7a --- /dev/null +++ b/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts @@ -0,0 +1,16 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { FFlowModule } from '@foblex/flow'; + +@Component({ + selector: 'minimap-basic-example', + styleUrls: [ './minimap-basic-example.component.scss' ], + templateUrl: './minimap-basic-example.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + FFlowModule + ] +}) +export class MinimapBasicExampleComponent { + +} diff --git a/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html new file mode 100644 index 0000000..30e00a8 --- /dev/null +++ b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html @@ -0,0 +1,8 @@ + + + +
Node 1
+
Node 2
+
+ +
diff --git a/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss new file mode 100644 index 0000000..d9c0500 --- /dev/null +++ b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss @@ -0,0 +1,52 @@ +:host ::ng-deep { + + .f-connection { + .f-connection-drag-handle { + fill: transparent; + } + + .f-connection-selection { + fill: none; + } + + .f-connection-path { + fill: none; + stroke: #585858; + stroke-width: 2; + } + } + + .f-minimap { + background-color: var(--background-color); + bottom: 16px; + right: 16px; + width: 120px; + height: 120px; + + .f-minimap-node { + fill: var(--primary-text); + + &.f-selected { + fill: var(--primary-1); + } + } + + .f-minimap-view { + fill: var(--primary-soft) + } + } +} + +.f-node { + width: 100px; + padding: 12px; + color: #585858; + display: inline-flex; + justify-content: center; + align-items: center; + text-align: center; + background: #FCFDFE; + border-radius: 4px; + box-shadow: 0 0 1px 1px #E4E3E6; +} + diff --git a/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts new file mode 100644 index 0000000..9309b79 --- /dev/null +++ b/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts @@ -0,0 +1,16 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { FFlowModule } from '@foblex/flow'; + +@Component({ + selector: 'minimap-scaled-example', + styleUrls: [ './minimap-scaled-example.component.scss' ], + templateUrl: './minimap-scaled-example.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + FFlowModule + ] +}) +export class MinimapScaledExampleComponent { + +} diff --git a/projects/f-flow/package.json b/projects/f-flow/package.json index e5ca258..3bc5e8d 100644 --- a/projects/f-flow/package.json +++ b/projects/f-flow/package.json @@ -31,7 +31,7 @@ "peerDependencies": { "@angular/common": ">=12.0.0", "@angular/core": ">=12.0.0", - "@foblex/core": ">=1.1.4", + "@foblex/core": ">=1.1.6", "rxjs": ">=6.6.0" }, "dependencies": { diff --git a/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts b/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts index b4c0748..435c16f 100644 --- a/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts +++ b/projects/f-flow/src/f-connection/common/f-drag-handle/f-connection-drag-handle.component.ts @@ -35,7 +35,7 @@ export class FConnectionDragHandleComponent implements IHasHostElement { private calculateCircleCenter(start: IPoint, end: IPoint, radius: number): IPoint { const direction = { x: end.x - start.x, y: end.y - start.y }; - const length = Math.sqrt(direction.x * direction.x + direction.y * direction.y); + const length = Math.sqrt(direction.x * direction.x + direction.y * direction.y) || 1; const unitDirection = { x: direction.x / length, y: direction.y / length }; const scaledDirection = { x: unitDirection.x * radius, y: unitDirection.y * radius }; return { x: end.x - scaledDirection.x, y: end.y - scaledDirection.y }; diff --git a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts index 8dfc27e..f553eb4 100644 --- a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts +++ b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts @@ -1,4 +1,4 @@ -import { IPoint, Point, PointExtensions, RectExtensions } from '@foblex/core'; +import { IPoint, IRect, Point, PointExtensions, RectExtensions } from '@foblex/core'; import { CalculateFlowPointFromMinimapPointRequest } from './calculate-flow-point-from-minimap-point.request'; import { Injectable } from '@angular/core'; import { FComponentsStore } from '../../../f-storage'; @@ -14,26 +14,40 @@ export class CalculateFlowPointFromMinimapPointExecution return this.fComponentsStore.fCanvas!.transform.scale; } + constructor( private fComponentsStore: FComponentsStore ) { } public handle(payload: CalculateFlowPointFromMinimapPointRequest): IPoint { - const positionInFlow = PointExtensions.sum( - RectExtensions.mult(payload.minimap.viewBox, this.canvasScale), - this.getScaledPoint(payload.eventPoint, payload.minimap) - ); - return PointExtensions.sub( payload.canvasPosition, - PointExtensions.sub(positionInFlow, payload.flowRect.gravityCenter) + PointExtensions.sub( + this.getPositionInViewBox(payload.eventPoint, payload.minimap), + this.getNormalizedFlowCenter(payload.flowRect) + ) + ); + } + + private getNormalizedFlowCenter(flowRect: IRect): IPoint { + return Point.fromPoint(flowRect.gravityCenter).sub(flowRect); + } + + private getPositionInViewBox(eventPoint: IPoint, minimap: FMinimapData): IPoint { + const eventPointInFlow = this.normalizeEventPoint(eventPoint, minimap); + return PointExtensions.sum( + eventPointInFlow, + RectExtensions.mult(minimap.viewBox, this.canvasScale) ); } - public getScaledPoint(point: IPoint, minimap: FMinimapData): Point { - return Point.fromPoint(point).sub( - RectExtensions.fromElement(minimap.element) - ).mult(minimap.scale).mult(this.canvasScale); + public normalizeEventPoint(point: IPoint, minimap: FMinimapData): Point { + return this.getEventPointInMinimap(point, minimap) + .mult(minimap.scale).mult(this.canvasScale); + } + + private getEventPointInMinimap(eventPoint: IPoint, minimap: FMinimapData): Point { + return Point.fromPoint(eventPoint).elementTransform(minimap.element as unknown as HTMLElement); } } diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts index b097745..b3da6a5 100644 --- a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.execution.ts @@ -27,7 +27,6 @@ export class MinimapDragPreparationExecution implements IExecution(new CalculateFlowPointFromMinimapPointRequest( - this.getFlowRect(), Point.fromPoint(this.fComponentsStore.fCanvas!.transform.position), + this.getFlowRect(), + Point.fromPoint(this.fComponentsStore.fCanvas!.transform.position), eventPoint, minimap, )); } diff --git a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts index 15b86e7..9dd9229 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts @@ -3,7 +3,7 @@ import { } from "@angular/core"; import { FComponentsStore } from '../f-storage'; import { - DomElementExtensions, IRect, + DomElementExtensions, IRect, Point, RectExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; @@ -18,6 +18,10 @@ export class FMinimapCanvasDirective { return this.elementReference.nativeElement; } + private get flowHost(): HTMLElement { + return this.fComponentsStore.flowHost; + } + private get flowScale(): number { return this.fComponentsStore.transform.scale; } @@ -50,7 +54,8 @@ export class FMinimapCanvasDirective { } private getNodeRect(node: FNodeBase): IRect { - return RectExtensions.div(RectExtensions.fromElement(node.hostElement), this.flowScale); + const nodeRectInFlow = RectExtensions.elementTransform(RectExtensions.fromElement(node.hostElement), this.flowHost); + return RectExtensions.div(nodeRectInFlow, this.flowScale); } private setElementAttributes(element: SVGRectElement, rect: IRect): void { diff --git a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts index 31e2fa2..e67c650 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts @@ -2,7 +2,7 @@ import { Directive, ElementRef, Input, } from "@angular/core"; import { FComponentsStore } from '../f-storage'; -import { IRect, ISize, RectExtensions, SizeExtensions } from '@foblex/core'; +import { IRect, ISize, Point, RectExtensions, SizeExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; import { FMinimapData } from './domain'; import { GetNodesRectRequest } from '../domain'; @@ -17,6 +17,10 @@ export class FMinimapFlowDirective { @Input() public fMinSize: number = 1000; + private get flowHost(): HTMLElement { + return this.fComponentsStore.flowHost; + } + public get hostElement(): SVGSVGElement { return this.elementReference.nativeElement; } @@ -35,17 +39,23 @@ export class FMinimapFlowDirective { const scale = this.calculateViewScale(nodesRect, minimapRect); const viewBox = this.calculateViewBox(nodesRect, minimapRect, scale); - this.model = new FMinimapData(this.hostElement, scale, viewBox); this.setViewBox(viewBox); } private getProcessedNodesRect(): IRect { - const rawRect = this.fMediator.send(new GetNodesRectRequest()); - const normalizedRect = this.normalizeRect(rawRect); + const normalizedRect = this.normalizeRect(this.getNodesRect()); return this.ensureMinimumSize(normalizedRect); } + private getNodesRect(): IRect { + return RectExtensions.elementTransform(this.fMediator.send(new GetNodesRectRequest()), this.flowHost) + } + + private getMinimapRect(): IRect { + return RectExtensions.elementTransform(RectExtensions.fromElement(this.hostElement), this.flowHost); + } + private normalizeRect(rect: IRect): IRect { return RectExtensions.div(rect, this.fComponentsStore.transform.scale); } @@ -59,30 +69,25 @@ export class FMinimapFlowDirective { ); } - private getMinimapRect(): IRect { - return RectExtensions.fromElement(this.hostElement); - } - private calculateViewScale(nodesRect: IRect, minimapRect: IRect): number { return Math.max(nodesRect.width / minimapRect.width, nodesRect.height / minimapRect.height); } private calculateViewBox(nodesRect: IRect, minimapRect: IRect, scale: number): IRect { - const viewSize = this.calculateViewSize(minimapRect, scale); - return this.calculateCenteredViewBox(nodesRect, viewSize, minimapRect, scale); + return this.calculateCenteredViewBox(nodesRect, this.calculateViewSize(minimapRect, scale)); } private calculateViewSize(minimapRect: IRect, scale: number): ISize { - return SizeExtensions.initialize(minimapRect.width * scale, minimapRect.height * scale); + return SizeExtensions.initialize(minimapRect.width * scale || 0, minimapRect.height * scale || 0); } - private calculateCenteredViewBox(nodesRect: IRect, viewSize: ISize, minimapRect: IRect, scale: number): IRect { - const centeredX = nodesRect.x - (viewSize.width - nodesRect.width) / 2 + (minimapRect.width * scale - viewSize.width) / 2; - const centeredY = nodesRect.y - (viewSize.height - nodesRect.height) / 2 + (minimapRect.height * scale - viewSize.height) / 2; + private calculateCenteredViewBox(nodesRect: IRect, viewSize: ISize): IRect { + const centeredX = nodesRect.x - (viewSize.width - nodesRect.width) / 2; + const centeredY = nodesRect.y - (viewSize.height - nodesRect.height) / 2; return RectExtensions.initialize(centeredX, centeredY, viewSize.width, viewSize.height); } private setViewBox(viewBox: IRect): void { - this.hostElement.setAttribute('viewBox', `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`); + this.hostElement.setAttribute('viewBox', `${ viewBox.x } ${ viewBox.y } ${ viewBox.width } ${ viewBox.height }`); } } diff --git a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts index 80054be..0d8e8ab 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts @@ -1,8 +1,6 @@ import { Directive, ElementRef } from "@angular/core"; import { FComponentsStore } from '../f-storage'; -import { FFlowMediator } from '../infrastructure'; -import { IPoint, IRect, ISize, PointExtensions, RectExtensions, SizeExtensions } from '@foblex/core'; -import { FMinimapFlowDirective } from './f-minimap-flow.directive'; +import { IRect, RectExtensions } from '@foblex/core'; @Directive({ selector: 'rect[f-minimap-view]', @@ -22,47 +20,18 @@ export class FMinimapViewDirective { constructor( private elementReference: ElementRef, - private fMinimapFlow: FMinimapFlowDirective, - private fMediator: FFlowMediator, private fComponentsStore: FComponentsStore ) { } public update(): void { - const targetRect = RectExtensions.fromElement(this.fComponentsStore.flowHost); - const minimapRect = RectExtensions.fromElement(this.fMinimapFlow.hostElement); - - const viewScale = this.calculateViewScale(targetRect, minimapRect); - const viewBox = this.calculateViewBox(targetRect, minimapRect, viewScale); - + const viewBox = RectExtensions.div(RectExtensions.fromElement(this.fComponentsStore.flowHost), this.flowScale); this.setAttributes(viewBox); } - private calculateViewScale(targetRect: IRect, minimapRect: IRect): number { - return Math.max(targetRect.width / minimapRect.width, targetRect.height / minimapRect.height); - } - - private calculateViewBox(targetRect: IRect, minimapRect: IRect, viewScale: number): IRect { - const viewSize = this.calculateViewSize(minimapRect, viewScale); - const position = this.calculateViewBoxPosition(targetRect, viewSize, minimapRect, viewScale); - const viewBox = RectExtensions.initialize(position.x, position.y, viewSize.width, viewSize.height); - return RectExtensions.div(viewBox, this.flowScale); - } - - private calculateViewSize(minimapRect: IRect, viewScale: number): ISize { - return SizeExtensions.initialize(minimapRect.width * viewScale, minimapRect.height * viewScale); - } - - private calculateViewBoxPosition(targetRect: IRect, viewSize: ISize, minimapRect: IRect, viewScale: number): IPoint { - return PointExtensions.initialize( - targetRect.x - (viewSize.width - targetRect.width) / 2 + (minimapRect.width * viewScale - viewSize.width) / 2, - targetRect.y - (viewSize.height - targetRect.height) / 2 + (minimapRect.height * viewScale - viewSize.height) / 2 - ); - } - private setAttributes(viewBox: IRect): void { - this.hostElement.setAttribute('x', viewBox.x.toString()); - this.hostElement.setAttribute('y', viewBox.y.toString()); + this.hostElement.setAttribute('x', '0'); + this.hostElement.setAttribute('y', '0'); this.hostElement.setAttribute('width', viewBox.width.toString()); this.hostElement.setAttribute('height', viewBox.height.toString()); } diff --git a/public/docs/en/environment.ts b/public/docs/en/environment.ts index c9e58f6..67e3059 100644 --- a/public/docs/en/environment.ts +++ b/public/docs/en/environment.ts @@ -25,6 +25,12 @@ import { } from '../../../projects/f-examples/line-alignment-example/line-alignment-example.component'; import { DragToReassignComponent } from '../../../projects/f-examples/drag-to-reassign/drag-to-reassign.component'; import { FlowComponent } from '../../../projects/f-pro-examples/visual-programming/components/flow/flow.component'; +import { + MinimapBasicExampleComponent +} from '../../../projects/f-examples/minimap-basic-example/minimap-basic-example.component'; +import { + MinimapScaledExampleComponent +} from '../../../projects/f-examples/minimap-scaled-example/minimap-scaled-example.component'; export const ENGLISH_ENVIRONMENT: IDocsEnvironment = createEnvironment(); @@ -69,6 +75,8 @@ function createEnvironment(): IDocsEnvironment { { tag: 'background-example', component: BackgroundExampleComponent }, { tag: 'line-alignment-example', component: LineAlignmentExampleComponent }, { tag: 'visual-programming-flow', component: FlowComponent }, + { tag: 'minimap-basic-example', component: MinimapBasicExampleComponent }, + { tag: 'minimap-scaled-example', component: MinimapScaledExampleComponent }, ], socialLinks: [ { icon: 'github', link: 'https://github.com/Foblex/f-flow' }, diff --git a/public/docs/en/f-minimap-component.md b/public/docs/en/f-minimap-component.md index c95558e..377cc5f 100644 --- a/public/docs/en/f-minimap-component.md +++ b/public/docs/en/f-minimap-component.md @@ -16,10 +16,37 @@ The **FMinimapComponent** provides a miniature view of the larger flow, allowing ## Usage +#### Basic Usage + +To add a minimap to your flow, simply include the `FMinimapComponent` within the [f-flow](f-flow-component) component. This provides users with an overview of the flow layout and enhances navigation capabilities. + ```html ...// Other components - |:||:| + |:||:| + +``` + +#### Navigation and Interaction + +For navigation and interaction you need to add [f-draggable](f-draggable-directive) directive to the [f-flow](f-flow-component) component. + +```html + + ...// Other components + |:||:| + +``` + + +#### Custom Scale + +You can set a custom scale for the minimap by using the `fMinSize` input. This allows you to control the size of the minimap based on your requirements. + +```html + + ...// Other components + ``` @@ -29,9 +56,19 @@ The **FMinimapComponent** provides a miniature view of the larger flow, allowing This example shows a basic implementation of the minimap component within a larger flow, providing users with an overview and easy navigation capabilities. -::: ng-component -[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.html -[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.ts -[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/simple-flow/simple-flow.component.scss +::: ng-component +[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-basic-example/minimap-basic-example.component.html +[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-basic-example/minimap-basic-example.component.ts +[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-basic-example/minimap-basic-example.component.scss +::: + +#### Custom Scale Example + +This example demonstrates the use of a custom scale for the minimap, allowing you to control the size of the minimap based on your requirements. + +::: ng-component +[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.html +[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.ts +[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/minimap-scaled-example/minimap-scaled-example.component.scss ::: diff --git a/public/docs/en/f-zoom-directive.md b/public/docs/en/f-zoom-directive.md index 846a2fb..fcb550d 100644 --- a/public/docs/en/f-zoom-directive.md +++ b/public/docs/en/f-zoom-directive.md @@ -38,6 +38,8 @@ The **FZoomDirective** directive is used to control the zoom and pan of the canv ## Usage +#### Basic Usage + To enable zoom and pan functionality, set `fZoom` directive to [f-canvas](f-canvas-component) component. ```html @@ -48,6 +50,8 @@ To enable zoom and pan functionality, set `fZoom` directive to [f-canvas](f-canv ``` +#### Tracking Zoom Changes + To track zoom changes, use the `fCanvasChange` output. ```html @@ -58,6 +62,8 @@ To track zoom changes, use the `fCanvasChange` output. ``` +#### Programmatically Controlling Zoom + You can also control the zoom level programmatically by using the methods provided by the directive. ::: code-group From 80677640c5d917b564964987b575d9fb28e28ce5 Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Sun, 11 Aug 2024 22:09:38 +0200 Subject: [PATCH 09/10] feat: Update to f-docs v1.2.1 --- package.json | 2 +- projects/f-flow/package.json | 2 +- ...ubscribe-on-transform-changes.execution.ts | 10 ++- .../src/f-draggable/f-draggable.directive.ts | 2 +- projects/f-flow/src/f-draggable/providers.ts | 2 +- ...flow-point-from-minimap-point.execution.ts | 1 - .../minimap-drag-preparation.validator.ts | 5 +- .../f-minimap/f-minimap-canvas.directive.ts | 4 +- .../src/f-minimap/f-minimap-flow.directive.ts | 4 +- .../src/f-minimap/f-minimap-view.directive.ts | 2 +- .../src/f-minimap/f-minimap.component.html | 7 +- .../src/f-minimap/f-minimap.component.ts | 9 +-- public/docs/en/f-external-item-directive.md | 71 ------------------- src/styles/styles.scss | 2 +- 14 files changed, 26 insertions(+), 97 deletions(-) delete mode 100644 public/docs/en/f-external-item-directive.md diff --git a/package.json b/package.json index c1f0a21..9c17958 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@angular/platform-browser-dynamic": "^18.1.0", "@angular/router": "^18.1.0", "@foblex/core": "^1.1.6", - "@foblex/f-docs": "^1.2.0", + "@foblex/f-docs": "^1.2.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" diff --git a/projects/f-flow/package.json b/projects/f-flow/package.json index 3bc5e8d..3f8c57b 100644 --- a/projects/f-flow/package.json +++ b/projects/f-flow/package.json @@ -1,6 +1,6 @@ { "name": "@foblex/flow", - "version": "12.4.0", + "version": "12.5.0", "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", diff --git a/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts index 03e8edb..d7d578b 100644 --- a/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts +++ b/projects/f-flow/src/domain/subscribe-on-transform-changes/subscribe-on-transform-changes.execution.ts @@ -1,7 +1,7 @@ import { SubscribeOnTransformChangesRequest } from './subscribe-on-transform-changes.request'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { FTransformStore } from '../../f-storage'; +import { merge, Observable } from 'rxjs'; +import { FComponentsStore, FTransformStore } from '../../f-storage'; import { FExecutionRegister, IExecution } from '../../infrastructure'; @Injectable() @@ -11,10 +11,14 @@ export class SubscribeOnTransformChangesExecution constructor( private fTransformStore: FTransformStore, + private fComponentsStore: FComponentsStore ) { } public handle(request: SubscribeOnTransformChangesRequest): Observable { - return this.fTransformStore.changes; + return merge( + this.fTransformStore.changes, + this.fComponentsStore.changes + ); } } diff --git a/projects/f-flow/src/f-draggable/f-draggable.directive.ts b/projects/f-flow/src/f-draggable/f-draggable.directive.ts index fc32c48..f01f093 100644 --- a/projects/f-flow/src/f-draggable/f-draggable.directive.ts +++ b/projects/f-flow/src/f-draggable/f-draggable.directive.ts @@ -1,5 +1,5 @@ import { - AfterViewInit, ContentChild, ContentChildren, + AfterViewInit, ContentChildren, Directive, ElementRef, EventEmitter, Inject, diff --git a/projects/f-flow/src/f-draggable/providers.ts b/projects/f-flow/src/f-draggable/providers.ts index c49b6d0..fe432b7 100644 --- a/projects/f-flow/src/f-draggable/providers.ts +++ b/projects/f-flow/src/f-draggable/providers.ts @@ -5,7 +5,7 @@ import { EXTERNAL_ITEM_PROVIDERS } from './external-item'; import { NODE_PROVIDERS } from './node'; import { NODE_RESIZE_PROVIDERS } from './node-resize'; import { SELECTION_AREA_PROVIDERS } from './selection-area'; -import { F_MINIMAP_DRAG_AND_DROP_PROVIDERS } from '../f-minimap'; +import { F_MINIMAP_DRAG_AND_DROP_PROVIDERS } from '../f-minimap/domain/providers'; export const F_DRAGGABLE_PROVIDERS = [ diff --git a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts index f553eb4..cb0d0b6 100644 --- a/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts +++ b/projects/f-flow/src/f-minimap/domain/calculate-flow-point-from-minimap-point/calculate-flow-point-from-minimap-point.execution.ts @@ -14,7 +14,6 @@ export class CalculateFlowPointFromMinimapPointExecution return this.fComponentsStore.fCanvas!.transform.scale; } - constructor( private fComponentsStore: FComponentsStore ) { diff --git a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts index 93d07f6..27dc0e7 100644 --- a/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts +++ b/projects/f-flow/src/f-minimap/domain/minimap-drag-preparation/minimap-drag-preparation.validator.ts @@ -2,18 +2,21 @@ import { Injectable } from '@angular/core'; import { MinimapDragPreparationRequest } from './minimap-drag-preparation.request'; import { FValidatorRegister, IValidator } from '../../../infrastructure'; import { FDraggableDataContext } from '../../../f-draggable'; +import { FComponentsStore } from '../../../f-storage'; @Injectable() @FValidatorRegister(MinimapDragPreparationRequest) export class MinimapDragPreparationValidator implements IValidator { constructor( + private fComponentsStore: FComponentsStore, private fDraggableDataContext: FDraggableDataContext ) { } public handle(request: MinimapDragPreparationRequest): boolean { return !this.fDraggableDataContext.draggableItems.length && - !!request.event.targetElement.closest('.f-minimap'); + !!request.event.targetElement.closest('.f-minimap') && + this.fComponentsStore.flowHost.contains(request.event.targetElement); } } diff --git a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts index 9dd9229..09f7b69 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-canvas.directive.ts @@ -3,14 +3,14 @@ import { } from "@angular/core"; import { FComponentsStore } from '../f-storage'; import { - DomElementExtensions, IRect, Point, + DomElementExtensions, IRect, RectExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; import { FNodeBase } from '../f-node'; @Directive({ - selector: 'g[f-minimap-canvas]' + selector: 'g[fMinimapCanvas]' }) export class FMinimapCanvasDirective { diff --git a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts index e67c650..e4d221a 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-flow.directive.ts @@ -2,13 +2,13 @@ import { Directive, ElementRef, Input, } from "@angular/core"; import { FComponentsStore } from '../f-storage'; -import { IRect, ISize, Point, RectExtensions, SizeExtensions } from '@foblex/core'; +import { IRect, ISize, RectExtensions, SizeExtensions } from '@foblex/core'; import { FFlowMediator } from '../infrastructure'; import { FMinimapData } from './domain'; import { GetNodesRectRequest } from '../domain'; @Directive({ - selector: 'svg[f-minimap-flow]' + selector: 'svg[fMinimapFlow]' }) export class FMinimapFlowDirective { diff --git a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts index 0d8e8ab..897910d 100644 --- a/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts +++ b/projects/f-flow/src/f-minimap/f-minimap-view.directive.ts @@ -3,7 +3,7 @@ import { FComponentsStore } from '../f-storage'; import { IRect, RectExtensions } from '@foblex/core'; @Directive({ - selector: 'rect[f-minimap-view]', + selector: 'rect[fMinimapView]', host: { 'class': 'f-component f-minimap-view', } diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.html b/projects/f-flow/src/f-minimap/f-minimap.component.html index be5973c..c821849 100644 --- a/projects/f-flow/src/f-minimap/f-minimap.component.html +++ b/projects/f-flow/src/f-minimap/f-minimap.component.html @@ -1,6 +1,5 @@ - - - + + + diff --git a/projects/f-flow/src/f-minimap/f-minimap.component.ts b/projects/f-flow/src/f-minimap/f-minimap.component.ts index 84b8f55..3220245 100644 --- a/projects/f-flow/src/f-minimap/f-minimap.component.ts +++ b/projects/f-flow/src/f-minimap/f-minimap.component.ts @@ -3,12 +3,11 @@ import { ElementRef, Input, OnDestroy, ViewChild, } from "@angular/core"; import { FFlowMediator } from '../infrastructure'; -import { debounceTime, merge, Observable, Subscription } from 'rxjs'; +import { debounceTime, Observable, Subscription } from 'rxjs'; import { SubscribeOnTransformChangesRequest } from '../domain'; import { FMinimapFlowDirective } from './f-minimap-flow.directive'; import { FMinimapCanvasDirective } from './f-minimap-canvas.directive'; import { FMinimapViewDirective } from './f-minimap-view.directive'; -import { FComponentsStore } from '../f-storage'; import { IPointerEvent } from '@foblex/core'; import { F_DRAG_AND_DROP_PLUGIN, IFDragAndDropPlugin } from '../f-draggable'; import { MinimapDragFinalizeRequest, MinimapDragPreparationRequest } from './domain'; @@ -44,7 +43,6 @@ export class FMinimapComponent implements AfterViewInit, OnDestroy, IFDragAndDro constructor( private elementReference: ElementRef, - private fComponentsStore: FComponentsStore, private fMediator: FFlowMediator ) { } @@ -62,10 +60,7 @@ export class FMinimapComponent implements AfterViewInit, OnDestroy, IFDragAndDro } private getTransformChanges(): Observable { - return merge( - this.fMediator.send>(new SubscribeOnTransformChangesRequest()), - this.fComponentsStore.changes - ); + return this.fMediator.send>(new SubscribeOnTransformChangesRequest()); } public onPointerDown(event: IPointerEvent): void { diff --git a/public/docs/en/f-external-item-directive.md b/public/docs/en/f-external-item-directive.md deleted file mode 100644 index daebbc5..0000000 --- a/public/docs/en/f-external-item-directive.md +++ /dev/null @@ -1,71 +0,0 @@ -# Input - -**Selector:** [fExternalItem] - -The **FNodeOutputDirective** is a directive that marks an element as an input within a [fNode](f-node-directive). It manages input-specific behaviours, such as allowing multiple connections, handling disabled state, and determining connectability. - -## Inputs - - - `fInputId: string;` The unique identifier for the directive instance. Automatically generated. Default: `f-node-input-${uniqueId++}` - - - `fInputDisabled: boolean;` Indicates whether the input is disabled. A disabled input may have a different visual representation and interaction behavior. Default: `false` - - - `fInputMultiple: boolean;` Determines whether the input allows multiple connectionsDefault: `Default: true` - - - `fOutputConnectableSide: EFConnectableSide;` Indicates the side of the output where the connection can be created. Accepts a value from [EFConnectableSide](e-f-connectable-side) enum. Default: `EFConnectableSide.AUTO` - -## Outputs - - - `isConnected: boolean;` Indicates whether the input is connected. - -## Methods - - - `refresh(): void;` Refreshes the state of the node, typically triggering a re-render or update. - -## Styles - - - `.f-component` A general class applied to all F components for shared styling. - - - `.f-node-input` Specific class for styling the node input element. - - - `.f-node-input-disabled` Applied when the input is disabled. - - - `.f-node-input-multiple` Applied when the input allows multiple connections. - - - `.f-node-input-not-connectable` Applied when the input is not connectable. - - - `.f-node-input-connected` Applied when the input is connected, indicating an active connection. - -## Usage - -```html - - -
- |:|
|:| -
- - -``` - -You can also add **fNodeInput** directive to the element containing the [fNode](f-node-directive) directive -```html - - - |:|
|:| -
-
-``` - -::: info INFO -The [f-connection](f-connection-component) component takes the border-radius of the component into account when connecting -::: - -## Examples - -Example of how to use the [fOutputConnectableSide](f-output-connectable-side) and [fInputConnectableSide](f-input-connectable-side) directives to specify the side of the node that can be connected to. Valid values are top, right, bottom, left, and auto from [EFConnectableSide](e-f-connectable-side) enum. -::: ng-component -[component.html] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.html -[component.ts] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.ts -[component.scss] <<< https://raw.githubusercontent.com/Foblex/f-flow/main/projects/f-examples/connectable-side/connectable-side.component.scss -::: diff --git a/src/styles/styles.scss b/src/styles/styles.scss index bdf704c..0bc83ec 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -1 +1 @@ -@import "@foblex/f-docs/assets/styles/styles"; +@import "@foblex/f-docs/assets/styles/styles.scss"; From 7d05574237a2a641b553d2615a76d6330d4ba70c Mon Sep 17 00:00:00 2001 From: Siarhei Huzarevich Date: Mon, 12 Aug 2024 00:10:53 +0200 Subject: [PATCH 10/10] update to release 12.5.0 --- CHANGELOG.md | 18 ++++++++++++++++++ package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b749ddd..d0e31ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +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. + +## [12.5.0](https://github.com/foblex/flow/compare/v12.3.9...v12.5.0) (2024-08-11) + +### Features + +* Add various site icons and upgrade library to Zoneless ([e561e69](https://github.com/foblex/flow/commit/e561e6972a723ea299005518057963ba260961af)) +* Added minimap functionality ([f5eece6](https://github.com/foblex/flow/commit/f5eece6df6e556a0ec0bbfa26ec623ce368f60ef)) +* Added Zoneless support ([a8c5812](https://github.com/foblex/flow/commit/a8c581285d8c9e6f246cbd2f2a07c346ce1ba131)) +* Update to f-docs v1.2.1 ([8067764](https://github.com/foblex/flow/commit/80677640c5d917b564964987b575d9fb28e28ce5)) + + +### Documentation + +* Added minimap documentation ([ccbdb99](https://github.com/foblex/flow/commit/ccbdb990935d0d9ae04d92de47752b50a6b11190)) +* Added minimap examples ([5646066](https://github.com/foblex/flow/commit/56460662d015f4ba0e6269d8f6bbc0de6e7b058f)) +* Remove old changelogs and switch to standard-version ([ff5b9e5](https://github.com/foblex/flow/commit/ff5b9e53c342ee7403813ce807e38d55a350725b)) + ## [12.4.0] - 2024-08-05 ### Bug Fixes diff --git a/package.json b/package.json index 9c17958..864c0fe 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "12.4.0", + "version": "12.5.0", "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.", "author": "Siarhei Huzarevich", "homepage": "https://flow.foblex.com",