feat: ngx-graph automatic fit to screen and dynamic data

This commit is contained in:
Michal Szczepanski 2024-07-25 02:00:59 +02:00
parent 93b7cd3959
commit 34fccf5982
2 changed files with 160 additions and 110 deletions

@ -1,122 +1,101 @@
<div> <div>
<h1>Slow shit</h1> <h1>Slow shit</h1>
<app-graph-component></app-graph-component> <app-graph-component></app-graph-component>
<ngx-graph <!--
class="chart-container" <ngx-graph [links]="links" [nodes]="nodes" [clusters]="clusters" [curve]="curve" [layout]="layout"
[view]="[800, 550]" [layoutSettings]="layoutSettings" [enableZoom]="false" [draggingEnabled]="false" [panningEnabled]="false"
[links]="[ [autoZoom]="true" [autoCenter]="true" [animate]="false" [update$]="update$" [center$]="center$"
[zoomToFit$]="zoomToFit$" (window:resize)="onResize($event)">
-->
<button (click)="handleAddNode()">+</button>
<div style="border: 1px solid #ff0000;width: 800px;height: 500px;position: relative">
<ngx-graph
class="chart-container"
[view]="[800, 500]"
[draggingEnabled]="false"
[links]="links"
[nodes]="nodes"
[update$]="update$"
[zoomToFit$]="zoom$"
[clusters]="[
{ {
id: 'a', id: 'asdf',
source: 'first',
target: 'second',
label: 'is parent of'
}, {
id: 'b',
source: 'first',
target: 'c1',
label: 'custom label'
}, {
id: 'd',
source: 'first',
target: 'c2',
label: 'custom label'
}, {
id: 'e',
source: 'c1',
target: 'd',
label: 'first link'
}, {
id: 'f',
source: 'c1',
target: 'd',
label: 'second link'
}
]"
[nodes]="[
{
id: 'first',
label: 'A'
}, {
id: 'second',
label: 'B'
}, {
id: 'c1',
label: 'C1'
}, {
id: 'c2',
label: 'C2'
}, {
id: 'd',
label: 'D'
}
]"
[clusters]="[
{
id: 'third',
label: 'Cluster node', label: 'Cluster node',
childNodeIds: ['c1', 'c2'] childNodeIds: ['c1', 'd'],
data: {
fill: '#fff000'
}
},
{
id: 'asdfg',
label: 'Cluster node 2',
childNodeIds: ['first', 'c2'],
data: {
fill: '#ff0000'
}
} }
]" ]"
layout="dagreCluster" layout="dagreCluster"
(select)="handleNodeSelect($event)" (select)="handleNodeSelect($event)"
> >
<ng-template #defsTemplate> <ng-template #defsTemplate>
<svg:marker id="arrow" viewBox="0 -5 10 10" refX="8" refY="0" markerWidth="4" markerHeight="4" orient="auto"> <svg:marker id="arrow" viewBox="0 -5 10 10" refX="8" refY="0" markerWidth="4" markerHeight="4" orient="auto">
<svg:path d="M0,-5L10,0L0,5" class="arrow-head" /> <svg:path d="M0,-5L10,0L0,5" class="arrow-head" />
</svg:marker> </svg:marker>
</ng-template> </ng-template>
<ng-template #clusterTemplate let-cluster> <ng-template #clusterTemplate let-cluster>
<svg:g class="node cluster"> <svg:g class="node cluster">
<svg:rect <svg:rect
rx="5" rx="5"
ry="5" ry="5"
[attr.width]="cluster.dimension.width" [attr.width]="cluster.dimension.width"
[attr.height]="cluster.dimension.height" [attr.height]="cluster.dimension.height"
[attr.fill]="cluster.data.color" [attr.fill]="cluster.data.fill"
/> />
</svg:g> </svg:g>
</ng-template> </ng-template>
<ng-template #nodeTemplate let-node> <ng-template #nodeTemplate let-node>
<svg:g class="node"> <svg:g class="node">
<svg:rect <svg:rect
[attr.width]="node.dimension.width" [attr.width]="node.dimension.width"
[attr.height]="node.dimension.height" [attr.height]="node.dimension.height"
[attr.fill]="node.data.color" [attr.fill]="node.data.color"
/> />
<svg:text alignment-baseline="central" [attr.x]="10" [attr.y]="node.dimension.height / 2"> <svg:text alignment-baseline="central" [attr.x]="10" [attr.y]="node.dimension.height / 2">
{{node.label}} {{node.label}}
</svg:text> </svg:text>
<!--<foreignObject width="100%" height="100%"> <!--<foreignObject width="100%" height="100%">
<xhtml:div> <xhtml:div>
<xhtml:div #source style="border:1px green solid;width: fit-content;margin-left:200px;margin-top:50px;"> <xhtml:div #source style="border:1px green solid;width: fit-content;margin-left:200px;margin-top:50px;">
I'm a div inside a SVG. I'm a div inside a SVG.
</xhtml:div>
<xhtml:p #target style="border:1px green solid;width: fit-content;margin-left:10px;margin-top:120px">
I'm a second div inside a SVG.
</xhtml:p>
</xhtml:div> </xhtml:div>
<xhtml:p #target style="border:1px green solid;width: fit-content;margin-left:10px;margin-top:120px"> </foreignObject>-->
I'm a second div inside a SVG. </svg:g>
</xhtml:p> </ng-template>
</xhtml:div>
</foreignObject>-->
</svg:g>
</ng-template>
<ng-template #linkTemplate let-link> <ng-template #linkTemplate let-link>
<svg:g class="edge"> <svg:g class="edge">
<svg:path class="line" stroke-width="2" marker-end="url(#arrow)"></svg:path> <svg:path class="line" stroke-width="2" marker-end="url(#arrow)"></svg:path>
<svg:text class="edge-label" text-anchor="middle"> <svg:text class="edge-label" text-anchor="middle">
<textPath <textPath
class="text-path" class="text-path"
[attr.href]="'#' + link.id" [attr.href]="'#' + link.id"
[style.dominant-baseline]="link.dominantBaseline" [style.dominant-baseline]="link.dominantBaseline"
startOffset="50%" startOffset="50%"
> >
{{link.label}} {{link.label}}
</textPath> </textPath>
</svg:text> </svg:text>
</svg:g> </svg:g>
</ng-template> </ng-template>
</ngx-graph> </ngx-graph>
</div>
</div> </div>
<router-outlet /> <router-outlet />

@ -1,7 +1,9 @@
import {Component} from "@angular/core"; import {AfterViewInit, Component} from "@angular/core";
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import {GraphModule} from "./view/graph/graph.module"; import {GraphModule} from "./view/graph/graph.module";
import {NgxGraphModule} from "@kr0san89/ngx-graph"; import {NgxGraphModule} from "@kr0san89/ngx-graph";
import {randomString} from "./utils/utils";
import {Subject} from "rxjs";
@Component({ @Component({
@ -11,9 +13,78 @@ import {NgxGraphModule} from "@kr0san89/ngx-graph";
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.scss' styleUrl: './app.component.scss'
}) })
export class AppComponent { export class AppComponent implements AfterViewInit {
update$ = new Subject<boolean>();
zoom$ = new Subject<any>();
links: any = []
nodes: any = []
ngAfterViewInit() {
this.nodes = [
{
id: 'first',
label: 'A'
}, {
id: 'second',
label: 'B'
}, {
id: 'c1',
label: 'C1'
}, {
id: 'c2',
label: 'C2'
}, {
id: 'd',
label: 'D'
}
]
this.links = [
{
id: 'a',
source: 'first',
target: 'second',
label: 'is parent of'
}, {
id: 'b',
source: 'first',
target: 'c1',
label: 'custom label'
}, {
id: 'd',
source: 'first',
target: 'c2',
label: 'custom label'
}, {
id: 'e',
source: 'c1',
target: 'd',
label: 'first link'
}, {
id: 'f',
source: 'c1',
target: 'd',
label: 'second link'
}
]
setTimeout(() => {
this.zoom$.next({ force: true, autoCenter: true })
}, 1)
}
handleAddNode = () => {
const node = {id: randomString(5), label: randomString(5)}
this.nodes.push(node)
console.log('node', node)
this.update$.next(true)
this.zoom$.next(true)
}
handleNodeSelect = (event: any) => { handleNodeSelect = (event: any) => {
console.log('handleNodeSelect', event) console.log('handleNodeSelect', event)
} }
toJSON = (val: any) => {
return JSON.stringify(val)
}
} }