Skip to content

Commit d8e2fb9

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

14 files changed

+200
-65
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

+2-2
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

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

+8
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: FlowPlugin[] = [];
2932
}
3033

3134
export type FlowDirection = 'horizontal' | 'vertical';
@@ -36,3 +39,8 @@ 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+
}

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

+13-22
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ 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';
17+
import { Arrangements2 as Arrangements } from './plugins/arrangements';
1618
import { Connections } from './connections';
1719
import { FlowChildComponent } from './flow-child.component';
1820
import { FlowService } from './flow.service';
@@ -22,9 +24,9 @@ import {
2224
FlowDirection,
2325
DotOptions,
2426
ArrowPathFn,
27+
FlowConfig,
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,6 +171,7 @@ export class FlowComponent
166171

167172
ngAfterViewInit(): void {
168173
this.createArrows();
174+
this.config.Plugins.forEach((x) => x.afterInit?.(this));
169175
}
170176

171177
ngAfterContentInit() {
@@ -281,22 +287,7 @@ export class FlowComponent
281287
return { scale: newScale, panX: newPanX, panY: newPanY };
282288
}
283289

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() {
290+
updateZoomContainer() {
300291
this.zoomContainer.nativeElement.style.transform = `translate3d(${this.flow.panX}px, ${this.flow.panY}px, 0) scale(${this.flow.scale})`;
301292
}
302293

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Arrangements, Arrangements2 } from './arrangements';
2-
import { ChildInfo } from './flow-interface';
2+
import { ChildInfo } from '../flow-interface';
33

44
export const FLOW_LIST = [
55
{ x: 40, y: 40, id: '1', deps: [] },

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
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

39
export class Arrangements {
410
constructor(
@@ -122,7 +128,7 @@ 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 Arrangements2 implements FlowPlugin {
126132
root: string[] = [];
127133

128134
constructor(
@@ -151,6 +157,10 @@ export class Arrangements2 {
151157
});
152158
}
153159

160+
onInit(data: FlowComponent): void {
161+
this.autoArrange();
162+
}
163+
154164
public autoArrange(): Map<string, FlowOptions> {
155165
this.root.forEach((id) => {
156166
const node = ROOT_DATA.get(id)!;
@@ -162,6 +172,7 @@ export class Arrangements2 {
162172
for (const item of this.list) {
163173
newItems.set(item.position.id, item.position);
164174
}
175+
console.log([...newItems.values()]);
165176
return newItems;
166177
}
167178
}

projects/flow/src/lib/fit-to-window.spec.ts projects/flow/src/lib/plugins/fit-to-window.spec.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,21 @@ describe('FitToWindow', () => {
8080
scale = 1;
8181
panX = 0;
8282
panY = 0;
83-
fitToWindow = new FitToWindow(list, containerRect, scale, panX, panY);
83+
fitToWindow = new FitToWindow();
84+
fitToWindow.onInit({
85+
list,
86+
zoomContainer: {
87+
nativeElement: { getBoundingClientRect: () => containerRect },
88+
},
89+
flow: {
90+
scale,
91+
panX,
92+
panY,
93+
zRect: containerRect,
94+
},
95+
updateZoomContainer: () => {},
96+
} as any);
97+
fitToWindow.run(list, containerRect, scale, panX, panY);
8498
});
8599

86100
it('should return positions', () => {

projects/flow/src/lib/fit-to-window.ts projects/flow/src/lib/plugins/fit-to-window.ts

+49-27
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,64 @@
1-
import { ChildInfo } from './flow-interface';
1+
import { ChildInfo, FlowPlugin } from '../flow-interface';
2+
import { FlowComponent } from '../flow.component';
23

3-
export class FitToWindow {
4+
export class FitToWindow implements FlowPlugin {
45
cRect: CPosition;
56
containerPadding = 0;
7+
data: FlowComponent;
68

7-
constructor(
8-
private list: ChildInfo[],
9-
private containerRect: DOMRect,
10-
private scale: number,
11-
private panX: number,
12-
private panY: number
9+
private list: ChildInfo[];
10+
private containerRect: DOMRect;
11+
private scale: number;
12+
private panX: number;
13+
private panY: number;
14+
15+
constructor(private init = false) {}
16+
17+
onInit(data: FlowComponent): void {
18+
this.data = data;
19+
}
20+
21+
afterInit(data: FlowComponent): void {
22+
this.data = data;
23+
if (this.init) {
24+
this.run(
25+
this.data.list,
26+
this.data.zoomContainer.nativeElement.getBoundingClientRect(),
27+
this.data.flow.scale,
28+
this.data.flow.panX,
29+
this.data.flow.panY
30+
);
31+
}
32+
}
33+
34+
run(
35+
list: ChildInfo[],
36+
cRect: DOMRect,
37+
scale: number,
38+
panX: number,
39+
panY: number
1340
) {
14-
const tt = { list, containerRect, scale, panX, panY };
41+
this.list = list;
42+
this.containerRect = cRect;
43+
this.scale = scale;
44+
this.panX = panX;
45+
this.panY = panY;
46+
this.fitToWindowInternal();
47+
}
48+
49+
fitToWindowInternal() {
1550
this.containerPadding = 30 / this.scale;
1651
this.cRect = {
1752
x: this.containerRect.x / this.scale - this.panX,
1853
y: this.containerRect.y / this.scale - this.panY,
1954
width: this.containerRect.width / this.scale,
2055
height: this.containerRect.height / this.scale,
2156
};
57+
const { scale, panX, panY } = this.fitToWindow();
58+
this.data.flow.scale = scale;
59+
this.data.flow.panX = panX;
60+
this.data.flow.panY = panY;
61+
this.data.updateZoomContainer();
2262
}
2363

2464
fitToWindow() {
@@ -66,24 +106,6 @@ export class FitToWindow {
66106
return Math.min(scaleX, scaleY);
67107
}
68108

69-
// getPanValues(
70-
// adjMaxX: number,
71-
// adjMaxY: number,
72-
// newScale: number,
73-
// minX: number,
74-
// minY: number
75-
// ) {
76-
// const panX =
77-
// this.cRect.x +
78-
// (this.cRect.width - (adjMaxX - this.containerPadding) * newScale) / 2 -
79-
// minX * newScale;
80-
// const panY =
81-
// this.cRect.y +
82-
// (this.cRect.height - (adjMaxY - this.containerPadding) * newScale) / 2 -
83-
// minY * newScale;
84-
// return { panX, panY };
85-
// }
86-
// }
87109
getPanValues(
88110
adjMaxX: number,
89111
adjMaxY: number,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { FlowPlugin } from '../flow-interface';
2+
import { FlowComponent } from '../flow.component';
3+
4+
export class ScrollIntoView implements FlowPlugin {
5+
data: FlowComponent;
6+
constructor(private id: string) {}
7+
8+
afterInit(data: FlowComponent): void {
9+
this.data = data;
10+
this.focus(this.id);
11+
}
12+
13+
focus(id: string) {
14+
const item = this.data.list.find((x) => x.position.id === id);
15+
if (item) {
16+
const { x, y } = item.position;
17+
const { width, height } = item.elRect;
18+
// this.flow.panX = -x * this.flow.scale + this.flow.zRect.width / 2;
19+
if (x + width * this.data.flow.scale > this.data.flow.zRect.width) {
20+
this.data.flow.panX =
21+
-x * this.data.flow.scale + (this.data.flow.zRect.width - width);
22+
} else if (
23+
this.data.flow.panX + x * this.data.flow.scale <
24+
this.data.flow.zRect.width
25+
) {
26+
this.data.flow.panX = -x * this.data.flow.scale;
27+
}
28+
this.data.flow.panY =
29+
-y * this.data.flow.scale + (this.data.flow.zRect.height - height) / 2;
30+
this.data.updateZoomContainer();
31+
}
32+
}
33+
}

projects/flow/src/public-api.ts

+2
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ export {
1212
FlowConfig,
1313
} from './lib/flow-interface';
1414
export * from './lib/svg';
15+
export { FitToWindow } from './lib/plugins/fit-to-window';
16+
export { ScrollIntoView } from './lib/plugins/scroll-into-view';

0 commit comments

Comments
 (0)