Skip to content

Commit

Permalink
feat: add plugin support to ngu-flow
Browse files Browse the repository at this point in the history
  • Loading branch information
sheikalthaf committed Feb 16, 2024
1 parent a30c971 commit e0bd2bf
Show file tree
Hide file tree
Showing 15 changed files with 327 additions and 142 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"start": "ng serve --port 52666",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"gh": "ng deploy --dir=dist/angular-flow/browser --base-href=/ngu-flow/",
"test": "jest"
},
"private": true,
Expand Down
8 changes: 4 additions & 4 deletions projects/flow/src/lib/flow-child.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ export class FlowChildComponent implements OnInit, OnChanges, OnDestroy {
});

this.positionChange.subscribe((x) => {
const { left, top } = this.flow.zRect;
// const { left, top } = this.flow.zRect;
// if (!this.position) console.log(this.position);
this.updatePosition(this.position.x + left, this.position.y + top);
this.updatePosition(this.position.x, this.position.y);
});
}

Expand Down Expand Up @@ -135,8 +135,8 @@ export class FlowChildComponent implements OnInit, OnChanges, OnDestroy {
(this.flow.gridSize * this.flow.scale)
) * this.flow.gridSize;

this.position.x = x - zRect.left;
this.position.y = y - zRect.top;
this.position.x = x;
this.position.y = y;
this.positionChange.next(this.position);
this.flow.arrowsChange.next(this.position);
}
Expand Down
9 changes: 9 additions & 0 deletions projects/flow/src/lib/flow-interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FlowComponent } from './flow.component';

export interface ChildInfo {
position: FlowOptions;
dots?: DOMRect[];
Expand Down Expand Up @@ -26,6 +28,7 @@ export interface DotOptions extends FlowOptions {
export class FlowConfig {
Arrows = true;
ArrowSize = 20;
Plugins: { [x: string]: FlowPlugin } = {};
}

export type FlowDirection = 'horizontal' | 'vertical';
Expand All @@ -36,3 +39,9 @@ export type ArrowPathFn = (
arrowSize: number,
strokeWidth: number
) => string;

export interface FlowPlugin {
onInit?(data: FlowComponent): void;
afterInit?(data: FlowComponent): void;
beforeArrowUpdate?(data: FlowComponent): void;
}
61 changes: 24 additions & 37 deletions projects/flow/src/lib/flow.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import {
ElementRef,
NgZone,
ChangeDetectionStrategy,
Input,
OnInit,
} from '@angular/core';
import { startWith } from 'rxjs';
import { Arrangements2 as Arrangements } from './arrangements';
import { Connections } from './connections';
import { FlowChildComponent } from './flow-child.component';
import { FlowService } from './flow.service';
Expand All @@ -22,9 +23,10 @@ import {
FlowDirection,
DotOptions,
ArrowPathFn,
FlowConfig,
FlowPlugin,
} from './flow-interface';
import { blendCorners, flowPath, bezierPath, blendCorners1 } from './svg';
import { FitToWindow } from './fit-to-window';

const BASE_SCALE_AMOUNT = 0.05;

Expand Down Expand Up @@ -125,10 +127,11 @@ const BASE_SCALE_AMOUNT = 0.05;
],
})
export class FlowComponent
implements AfterContentInit, AfterViewInit, OnDestroy
implements OnInit, AfterContentInit, AfterViewInit, OnDestroy
{
@ContentChildren(FlowChildComponent) children: QueryList<FlowChildComponent> =
new QueryList();
@Input() config: FlowConfig = new FlowConfig();
@ContentChildren(FlowChildComponent) children =
new QueryList<FlowChildComponent>();

// @ViewChildren('arrowPaths') arrowPaths: QueryList<ElementRef<SVGPathElement>>;
@ViewChild('zoomContainer') zoomContainer: ElementRef<HTMLDivElement>;
Expand All @@ -143,7 +146,9 @@ export class FlowComponent
public el: ElementRef<HTMLElement>,
public flow: FlowService,
private ngZone: NgZone
) {
) {}

ngOnInit(): void {
this.flow.zoomContainer = this.el.nativeElement;
this.flow.arrowsChange.subscribe((e) => this.updateArrows(e));
this.ngZone.runOutsideAngular(() => {
Expand All @@ -166,14 +171,24 @@ export class FlowComponent

ngAfterViewInit(): void {
this.createArrows();
this.runPlugin((e) => e.afterInit?.(this));
}

private runPlugin(callback: (e: FlowPlugin) => void) {
for (const key in this.config.Plugins) {
if (Object.prototype.hasOwnProperty.call(this.config.Plugins, key)) {
const element = this.config.Plugins[key];
callback(element);
}
}
}

ngAfterContentInit() {
this.children.changes
.pipe(startWith(this.children))
.subscribe((children) => {
this.flow.update(this.children.map((x) => x.position));
this.arrangeChildren();
this.runPlugin((e) => e.beforeArrowUpdate?.(this));
this.createArrows();
});
requestAnimationFrame(() => this.updateArrows()); // this required for angular to render the dot
Expand All @@ -189,7 +204,7 @@ export class FlowComponent

updateDirection(direction: FlowDirection) {
this.flow.direction = direction;
this.arrangeChildren();
this.runPlugin((e) => e.beforeArrowUpdate?.(this));
this.createArrows();
}

Expand Down Expand Up @@ -281,38 +296,10 @@ export class FlowComponent
return { scale: newScale, panX: newPanX, panY: newPanY };
}

fitToWindow() {
const ftw = new FitToWindow(
this.list,
this.zoomContainer.nativeElement.getBoundingClientRect(),
this.flow.scale,
this.flow.panX,
this.flow.panY
);
const { scale, panX, panY } = ftw.fitToWindow();
this.flow.scale = scale;
this.flow.panX = panX;
this.flow.panY = panY;
this.updateZoomContainer();
}

private updateZoomContainer() {
updateZoomContainer() {
this.zoomContainer.nativeElement.style.transform = `translate3d(${this.flow.panX}px, ${this.flow.panY}px, 0) scale(${this.flow.scale})`;
}

arrangeChildren() {
const arrangements = new Arrangements(
this.list,
this.flow.direction,
this.flow.horizontalPadding,
this.flow.verticalPadding,
this.flow.groupPadding
);
const newList = arrangements.autoArrange();
this.flow.update([...newList.values()]);
this.flow.layoutUpdated.next();
}

get list() {
return this.children.toArray().map((x) => {
// calculate the width and height with scale
Expand Down
16 changes: 0 additions & 16 deletions projects/flow/src/lib/flow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export class FlowService {
};

update(children: FlowOptions[]) {
// console.log('update', children);
this.items.clear();
children.forEach((child) => {
this.items.set(child.id, child);
Expand All @@ -62,21 +61,6 @@ export class FlowService {
});
}

// delete(option: FlowOptions) {
// this.items.delete(option.id);
// this.deps.delete(option.id);
// this.deps.forEach((v, k) => {
// const index = v.indexOf(option.id);
// if (index > -1) {
// v.splice(index, 1);
// }
// });
// }

get list() {
return Array.from(this.items.values());
}

get zRect() {
return this.zoomContainer.getBoundingClientRect();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Arrangements, Arrangements2 } from './arrangements';
import { ChildInfo } from './flow-interface';
import { ArrangementsOld, Arrangements } from './arrangements';
import { ChildInfo } from '../flow-interface';
import { FlowComponent } from '../flow.component';

export const FLOW_LIST = [
{ x: 40, y: 40, id: '1', deps: [] },
Expand All @@ -13,15 +14,15 @@ export const FLOW_LIST = [
];

describe('Arrangements', () => {
let arrangements: Arrangements;
let arrangements: ArrangementsOld;

it('should be created', () => {
const childObj: ChildInfo[] = FLOW_LIST.map((x) => ({
position: x,
elRect: { width: 200, height: 200 } as any,
}));

arrangements = new Arrangements(childObj);
arrangements = new ArrangementsOld(childObj);
arrangements.verticalPadding = 20;
arrangements.groupPadding = 100;
const expected = {
Expand All @@ -40,17 +41,23 @@ describe('Arrangements', () => {
});

describe('Arrangements2', () => {
let arrangements: Arrangements2;
let arrangements: Arrangements;

it('should be created', () => {
const childObj: ChildInfo[] = FLOW_LIST.map((x) => ({
position: x,
elRect: { width: 200, height: 200 } as any,
}));

arrangements = new Arrangements2(childObj);
arrangements.verticalPadding = 20;
arrangements.groupPadding = 100;
arrangements = new Arrangements();
arrangements.onInit({
list: childObj,
flow: {
direction: 'vertical',
verticalPadding: 20,
groupPadding: 100,
},
} as Partial<FlowComponent> as any);
const expected = {
'1': { x: 330, y: 0, id: '1', deps: [] },
'2': { x: 110, y: 300, id: '2', deps: ['1'] },
Expand All @@ -61,7 +68,7 @@ describe('Arrangements2', () => {
'7': { x: 660, y: 600, id: '7', deps: ['5'] },
'8': { x: 660, y: 900, id: '8', deps: ['6', '7'] },
};
const actual = Object.fromEntries(arrangements.autoArrange());
const actual = Object.fromEntries(arrangements._autoArrange());
expect(actual).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { FlowOptions, ChildInfo, FlowDirection } from './flow-interface';
import {
FlowOptions,
ChildInfo,
FlowDirection,
FlowPlugin,
} from '../flow-interface';
import { FlowComponent } from '../flow.component';

export class Arrangements {
export class ArrangementsOld {
constructor(
private list: ChildInfo[],
private direction: 'horizontal' | 'vertical' = 'horizontal',
Expand Down Expand Up @@ -122,16 +128,44 @@ const ROOT_DEPS = new Map<string, string[]>();
const HORIZONTAL_PADDING = 100;
const VERTICAL_PADDING = 20;

export class Arrangements2 {
export class Arrangements implements FlowPlugin {
root: string[] = [];
data: FlowComponent;
private list: ChildInfo[];
private direction: FlowDirection = 'vertical';
public horizontalPadding = 100;
public verticalPadding = 20;
public groupPadding = 20;

constructor() {}

onInit(data: FlowComponent): void {
this.data = data;
}

beforeArrowUpdate(data: FlowComponent): void {
this.data = data;
this.runArrange();
}

private runArrange() {
const newList = this._autoArrange();
this.data.flow.update([...newList.values()]);
this.data.flow.layoutUpdated.next();
}

arrange() {
this.runArrange();
this.data.updateArrows();
}

public _autoArrange(): Map<string, FlowOptions> {
this.list = this.data.list;
this.direction = this.data.flow.direction;
this.horizontalPadding = this.data.flow.horizontalPadding;
this.verticalPadding = this.data.flow.verticalPadding;
this.groupPadding = this.data.flow.groupPadding;

constructor(
private list: ChildInfo[],
private direction: FlowDirection = 'vertical',
public horizontalPadding = 100,
public verticalPadding = 20,
public groupPadding = 20
) {
ROOT_DATA.clear();
ROOT_DEPS.clear();
this.list.forEach((item) => {
Expand All @@ -149,9 +183,7 @@ export class Arrangements2 {
this.root.push(item.position.id);
}
});
}

public autoArrange(): Map<string, FlowOptions> {
this.root.forEach((id) => {
const node = ROOT_DATA.get(id)!;
node.arrange(0, 0, this.direction);
Expand All @@ -162,6 +194,7 @@ export class Arrangements2 {
for (const item of this.list) {
newItems.set(item.position.id, item.position);
}
console.log([...newItems.values()]);
return newItems;
}
}
Expand Down
Loading

0 comments on commit e0bd2bf

Please sign in to comment.