Skip to content

Commit e0bd2bf

Browse files
committed
feat: add plugin support to ngu-flow
1 parent a30c971 commit e0bd2bf

15 files changed

+327
-142
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"start": "ng serve --port 52666",
77
"build": "ng build",
88
"watch": "ng build --watch --configuration development",
9+
"gh": "ng deploy --dir=dist/angular-flow/browser --base-href=/ngu-flow/",
910
"test": "jest"
1011
},
1112
"private": true,

projects/flow/src/lib/flow-child.component.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ export class FlowChildComponent implements OnInit, OnChanges, OnDestroy {
9595
});
9696

9797
this.positionChange.subscribe((x) => {
98-
const { left, top } = this.flow.zRect;
98+
// const { left, top } = this.flow.zRect;
9999
// if (!this.position) console.log(this.position);
100-
this.updatePosition(this.position.x + left, this.position.y + top);
100+
this.updatePosition(this.position.x, this.position.y);
101101
});
102102
}
103103

@@ -135,8 +135,8 @@ export class FlowChildComponent implements OnInit, OnChanges, OnDestroy {
135135
(this.flow.gridSize * this.flow.scale)
136136
) * this.flow.gridSize;
137137

138-
this.position.x = x - zRect.left;
139-
this.position.y = y - zRect.top;
138+
this.position.x = x;
139+
this.position.y = y;
140140
this.positionChange.next(this.position);
141141
this.flow.arrowsChange.next(this.position);
142142
}

projects/flow/src/lib/flow-interface.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { FlowComponent } from './flow.component';
2+
13
export interface ChildInfo {
24
position: FlowOptions;
35
dots?: DOMRect[];
@@ -26,6 +28,7 @@ export interface DotOptions extends FlowOptions {
2628
export class FlowConfig {
2729
Arrows = true;
2830
ArrowSize = 20;
31+
Plugins: { [x: string]: FlowPlugin } = {};
2932
}
3033

3134
export type FlowDirection = 'horizontal' | 'vertical';
@@ -36,3 +39,9 @@ export type ArrowPathFn = (
3639
arrowSize: number,
3740
strokeWidth: number
3841
) => string;
42+
43+
export interface FlowPlugin {
44+
onInit?(data: FlowComponent): void;
45+
afterInit?(data: FlowComponent): void;
46+
beforeArrowUpdate?(data: FlowComponent): void;
47+
}

projects/flow/src/lib/flow.component.ts

+24-37
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import {
1010
ElementRef,
1111
NgZone,
1212
ChangeDetectionStrategy,
13+
Input,
14+
OnInit,
1315
} from '@angular/core';
1416
import { startWith } from 'rxjs';
15-
import { Arrangements2 as Arrangements } from './arrangements';
1617
import { Connections } from './connections';
1718
import { FlowChildComponent } from './flow-child.component';
1819
import { FlowService } from './flow.service';
@@ -22,9 +23,10 @@ import {
2223
FlowDirection,
2324
DotOptions,
2425
ArrowPathFn,
26+
FlowConfig,
27+
FlowPlugin,
2528
} from './flow-interface';
2629
import { blendCorners, flowPath, bezierPath, blendCorners1 } from './svg';
27-
import { FitToWindow } from './fit-to-window';
2830

2931
const BASE_SCALE_AMOUNT = 0.05;
3032

@@ -125,10 +127,11 @@ const BASE_SCALE_AMOUNT = 0.05;
125127
],
126128
})
127129
export class FlowComponent
128-
implements AfterContentInit, AfterViewInit, OnDestroy
130+
implements OnInit, AfterContentInit, AfterViewInit, OnDestroy
129131
{
130-
@ContentChildren(FlowChildComponent) children: QueryList<FlowChildComponent> =
131-
new QueryList();
132+
@Input() config: FlowConfig = new FlowConfig();
133+
@ContentChildren(FlowChildComponent) children =
134+
new QueryList<FlowChildComponent>();
132135

133136
// @ViewChildren('arrowPaths') arrowPaths: QueryList<ElementRef<SVGPathElement>>;
134137
@ViewChild('zoomContainer') zoomContainer: ElementRef<HTMLDivElement>;
@@ -143,7 +146,9 @@ export class FlowComponent
143146
public el: ElementRef<HTMLElement>,
144147
public flow: FlowService,
145148
private ngZone: NgZone
146-
) {
149+
) {}
150+
151+
ngOnInit(): void {
147152
this.flow.zoomContainer = this.el.nativeElement;
148153
this.flow.arrowsChange.subscribe((e) => this.updateArrows(e));
149154
this.ngZone.runOutsideAngular(() => {
@@ -166,14 +171,24 @@ export class FlowComponent
166171

167172
ngAfterViewInit(): void {
168173
this.createArrows();
174+
this.runPlugin((e) => e.afterInit?.(this));
175+
}
176+
177+
private runPlugin(callback: (e: FlowPlugin) => void) {
178+
for (const key in this.config.Plugins) {
179+
if (Object.prototype.hasOwnProperty.call(this.config.Plugins, key)) {
180+
const element = this.config.Plugins[key];
181+
callback(element);
182+
}
183+
}
169184
}
170185

171186
ngAfterContentInit() {
172187
this.children.changes
173188
.pipe(startWith(this.children))
174189
.subscribe((children) => {
175190
this.flow.update(this.children.map((x) => x.position));
176-
this.arrangeChildren();
191+
this.runPlugin((e) => e.beforeArrowUpdate?.(this));
177192
this.createArrows();
178193
});
179194
requestAnimationFrame(() => this.updateArrows()); // this required for angular to render the dot
@@ -189,7 +204,7 @@ export class FlowComponent
189204

190205
updateDirection(direction: FlowDirection) {
191206
this.flow.direction = direction;
192-
this.arrangeChildren();
207+
this.runPlugin((e) => e.beforeArrowUpdate?.(this));
193208
this.createArrows();
194209
}
195210

@@ -281,38 +296,10 @@ export class FlowComponent
281296
return { scale: newScale, panX: newPanX, panY: newPanY };
282297
}
283298

284-
fitToWindow() {
285-
const ftw = new FitToWindow(
286-
this.list,
287-
this.zoomContainer.nativeElement.getBoundingClientRect(),
288-
this.flow.scale,
289-
this.flow.panX,
290-
this.flow.panY
291-
);
292-
const { scale, panX, panY } = ftw.fitToWindow();
293-
this.flow.scale = scale;
294-
this.flow.panX = panX;
295-
this.flow.panY = panY;
296-
this.updateZoomContainer();
297-
}
298-
299-
private updateZoomContainer() {
299+
updateZoomContainer() {
300300
this.zoomContainer.nativeElement.style.transform = `translate3d(${this.flow.panX}px, ${this.flow.panY}px, 0) scale(${this.flow.scale})`;
301301
}
302302

303-
arrangeChildren() {
304-
const arrangements = new Arrangements(
305-
this.list,
306-
this.flow.direction,
307-
this.flow.horizontalPadding,
308-
this.flow.verticalPadding,
309-
this.flow.groupPadding
310-
);
311-
const newList = arrangements.autoArrange();
312-
this.flow.update([...newList.values()]);
313-
this.flow.layoutUpdated.next();
314-
}
315-
316303
get list() {
317304
return this.children.toArray().map((x) => {
318305
// calculate the width and height with scale

projects/flow/src/lib/flow.service.ts

-16
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ export class FlowService {
4747
};
4848

4949
update(children: FlowOptions[]) {
50-
// console.log('update', children);
5150
this.items.clear();
5251
children.forEach((child) => {
5352
this.items.set(child.id, child);
@@ -62,21 +61,6 @@ export class FlowService {
6261
});
6362
}
6463

65-
// delete(option: FlowOptions) {
66-
// this.items.delete(option.id);
67-
// this.deps.delete(option.id);
68-
// this.deps.forEach((v, k) => {
69-
// const index = v.indexOf(option.id);
70-
// if (index > -1) {
71-
// v.splice(index, 1);
72-
// }
73-
// });
74-
// }
75-
76-
get list() {
77-
return Array.from(this.items.values());
78-
}
79-
8064
get zRect() {
8165
return this.zoomContainer.getBoundingClientRect();
8266
}

projects/flow/src/lib/arrangements.spec.ts projects/flow/src/lib/plugins/arrangements.spec.ts

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Arrangements, Arrangements2 } from './arrangements';
2-
import { ChildInfo } from './flow-interface';
1+
import { ArrangementsOld, Arrangements } from './arrangements';
2+
import { ChildInfo } from '../flow-interface';
3+
import { FlowComponent } from '../flow.component';
34

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

1516
describe('Arrangements', () => {
16-
let arrangements: Arrangements;
17+
let arrangements: ArrangementsOld;
1718

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

24-
arrangements = new Arrangements(childObj);
25+
arrangements = new ArrangementsOld(childObj);
2526
arrangements.verticalPadding = 20;
2627
arrangements.groupPadding = 100;
2728
const expected = {
@@ -40,17 +41,23 @@ describe('Arrangements', () => {
4041
});
4142

4243
describe('Arrangements2', () => {
43-
let arrangements: Arrangements2;
44+
let arrangements: Arrangements;
4445

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

51-
arrangements = new Arrangements2(childObj);
52-
arrangements.verticalPadding = 20;
53-
arrangements.groupPadding = 100;
52+
arrangements = new Arrangements();
53+
arrangements.onInit({
54+
list: childObj,
55+
flow: {
56+
direction: 'vertical',
57+
verticalPadding: 20,
58+
groupPadding: 100,
59+
},
60+
} as Partial<FlowComponent> as any);
5461
const expected = {
5562
'1': { x: 330, y: 0, id: '1', deps: [] },
5663
'2': { x: 110, y: 300, id: '2', deps: ['1'] },
@@ -61,7 +68,7 @@ describe('Arrangements2', () => {
6168
'7': { x: 660, y: 600, id: '7', deps: ['5'] },
6269
'8': { x: 660, y: 900, id: '8', deps: ['6', '7'] },
6370
};
64-
const actual = Object.fromEntries(arrangements.autoArrange());
71+
const actual = Object.fromEntries(arrangements._autoArrange());
6572
expect(actual).toEqual(expected);
6673
});
6774
});

projects/flow/src/lib/arrangements.ts projects/flow/src/lib/plugins/arrangements.ts

+45-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { FlowOptions, ChildInfo, FlowDirection } from './flow-interface';
1+
import {
2+
FlowOptions,
3+
ChildInfo,
4+
FlowDirection,
5+
FlowPlugin,
6+
} from '../flow-interface';
7+
import { FlowComponent } from '../flow.component';
28

3-
export class Arrangements {
9+
export class ArrangementsOld {
410
constructor(
511
private list: ChildInfo[],
612
private direction: 'horizontal' | 'vertical' = 'horizontal',
@@ -122,16 +128,44 @@ const ROOT_DEPS = new Map<string, string[]>();
122128
const HORIZONTAL_PADDING = 100;
123129
const VERTICAL_PADDING = 20;
124130

125-
export class Arrangements2 {
131+
export class Arrangements implements FlowPlugin {
126132
root: string[] = [];
133+
data: FlowComponent;
134+
private list: ChildInfo[];
135+
private direction: FlowDirection = 'vertical';
136+
public horizontalPadding = 100;
137+
public verticalPadding = 20;
138+
public groupPadding = 20;
139+
140+
constructor() {}
141+
142+
onInit(data: FlowComponent): void {
143+
this.data = data;
144+
}
145+
146+
beforeArrowUpdate(data: FlowComponent): void {
147+
this.data = data;
148+
this.runArrange();
149+
}
150+
151+
private runArrange() {
152+
const newList = this._autoArrange();
153+
this.data.flow.update([...newList.values()]);
154+
this.data.flow.layoutUpdated.next();
155+
}
156+
157+
arrange() {
158+
this.runArrange();
159+
this.data.updateArrows();
160+
}
161+
162+
public _autoArrange(): Map<string, FlowOptions> {
163+
this.list = this.data.list;
164+
this.direction = this.data.flow.direction;
165+
this.horizontalPadding = this.data.flow.horizontalPadding;
166+
this.verticalPadding = this.data.flow.verticalPadding;
167+
this.groupPadding = this.data.flow.groupPadding;
127168

128-
constructor(
129-
private list: ChildInfo[],
130-
private direction: FlowDirection = 'vertical',
131-
public horizontalPadding = 100,
132-
public verticalPadding = 20,
133-
public groupPadding = 20
134-
) {
135169
ROOT_DATA.clear();
136170
ROOT_DEPS.clear();
137171
this.list.forEach((item) => {
@@ -149,9 +183,7 @@ export class Arrangements2 {
149183
this.root.push(item.position.id);
150184
}
151185
});
152-
}
153186

154-
public autoArrange(): Map<string, FlowOptions> {
155187
this.root.forEach((id) => {
156188
const node = ROOT_DATA.get(id)!;
157189
node.arrange(0, 0, this.direction);
@@ -162,6 +194,7 @@ export class Arrangements2 {
162194
for (const item of this.list) {
163195
newItems.set(item.position.id, item.position);
164196
}
197+
console.log([...newItems.values()]);
165198
return newItems;
166199
}
167200
}

0 commit comments

Comments
 (0)