diff --git a/108.72483ade85a48f0c.js b/108.72483ade85a48f0c.js new file mode 100644 index 00000000..1bebd13c --- /dev/null +++ b/108.72483ade85a48f0c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkngx_vflow_demo=self.webpackChunkngx_vflow_demo||[]).push([[108],{108:(_,g,c)=>{c.r(g),c.d(g,{DynamicComponent:()=>j,default:()=>b});var i=c(6286),f=c(7134),u=c(9143),m=c(2936),o=c(2898),s=c(5879),r=c(8944);let y=(()=>{class a{constructor(){this.nodes=[{id:"1",point:{x:100,y:100},type:"default",text:"1"},{id:"2",point:{x:200,y:200},type:"default",text:"2"}],this.edges=[]}createEdge({source:e,target:n}){this.edges=[...this.edges,{id:`${e} -> ${n}`,source:e,target:n}]}static#s=this.\u0275fac=function(n){return new(n||a)};static#n=this.\u0275cmp=s.Xpm({type:a,selectors:[["ng-component"]],standalone:!0,features:[s.jDz],decls:1,vars:2,consts:[[3,"nodes","edges","onConnect"]],template:function(n,l){1&n&&(s.TgZ(0,"vflow",0),s.NdJ("onConnect",function(p){return l.createEdge(p)}),s.qZA()),2&n&&s.Q6J("nodes",l.nodes)("edges",l.edges)},dependencies:[o.p,r.t],encapsulation:2,changeDetection:0})}return a})();var C=c(3870),w=c(2274);let x=(()=>{class a{constructor(){this.nodes=[{id:"1",point:{x:100,y:100},type:h,data:{text:"Node 1"}},{id:"2",point:{x:200,y:200},type:h,data:{text:"Node 2"}}],this.edges=[],this.connection={mode:"loose"}}createEdge(e){const{source:n,target:l,sourceHandle:t,targetHandle:p}=e;this.edges=[...this.edges,{id:`${n}${t} -> ${l}${p}`,...e,markers:{end:{type:"arrow-closed"}}}]}static#s=this.\u0275fac=function(n){return new(n||a)};static#n=this.\u0275cmp=s.Xpm({type:a,selectors:[["ng-component"]],standalone:!0,features:[s.jDz],decls:1,vars:3,consts:[[3,"nodes","edges","connection","onConnect"]],template:function(n,l){1&n&&(s.TgZ(0,"vflow",0),s.NdJ("onConnect",function(p){return l.createEdge(p)}),s.qZA()),2&n&&s.Q6J("nodes",l.nodes)("edges",l.edges)("connection",l.connection)},dependencies:[o.p,r.t],encapsulation:2,changeDetection:0})}return a})(),h=(()=>{class a extends C.L{static#s=this.\u0275fac=function(){let e;return function(l){return(e||(e=s.n5z(a)))(l||a)}}();static#n=this.\u0275cmp=s.Xpm({type:a,selectors:[["ng-component"]],standalone:!0,features:[s.qOj,s.jDz],decls:6,vars:1,consts:[[1,"node"],["type","source","position","top","id","a"],["type","source","position","right","id","b"],["type","source","position","bottom","id","c"],["type","source","position","left","id","d"]],template:function(n,l){if(1&n&&(s.TgZ(0,"div",0),s._uU(1),s._UZ(2,"handle",1)(3,"handle",2)(4,"handle",3)(5,"handle",4),s.qZA()),2&n){let t;s.xp6(1),s.hij(" ",null==(t=l.data())?null:t.text," ")}},dependencies:[o.p,w.M],styles:[".node[_ngcontent-%COMP%]{width:100px;height:50px;border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}"],changeDetection:0})}return a})();const d={title:"Connection",mdFile:"./index.md",category:m.Z,demos:{DefaultConnectionDemoComponent:y,LooseConnectionDemoComponent:x},order:3},v=[],k={DefaultConnectionDemoComponent:[{title:"TypeScript",code:'
import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Connection, Edge, Node, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `<vflow [nodes]="nodes" [edges]="edges"\n (onConnect)="createEdge($event)"/>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class DefaultConnectionDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n}\n
'}],LooseConnectionDemoComponent:[{title:"TypeScript",code:'import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Connection, ConnectionSettings, CustomNodeComponent, Edge, Node, VflowComponent, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `\n <vflow\n [nodes]="nodes"\n [edges]="edges"\n [connection]="connection"\n (onConnect)="createEdge($event)"\n />\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class LooseConnectionDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: LooseConnectionNode,\n data: {\n text: \'Node 1\'\n }\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: LooseConnectionNode,\n data: {\n text: \'Node 2\'\n }\n },\n ]\n\n public edges: Edge[] = []\n\n public connection: ConnectionSettings = {\n mode: \'loose\'\n }\n\n public createEdge(connection: Connection) {\n const { source, target, sourceHandle, targetHandle } = connection\n\n this.edges = [...this.edges, {\n id: `${source}${sourceHandle} -> ${target}${targetHandle}`,\n ...connection,\n markers: {\n end: {\n type: \'arrow-closed\'\n }\n }\n }]\n }\n}\n\ninterface LooseConnectionNodeData {\n text: string;\n}\n\n@Component({\n template: `<div class="node">\n {{ data()?.text }}\n\n <handle type="source" position="top" id="a" />\n <handle type="source" position="right" id="b" />\n <handle type="source" position="bottom" id="c" />\n <handle type="source" position="left" id="d" />\n </div>`,\n styles: [`\n .node {\n width: 100px;\n height: 50px;\n border: 1.5px solid #1b262c;\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: black;\n background-color: white;\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class LooseConnectionNode extends CustomNodeComponent<LooseConnectionNodeData> { }\n
'}]};let j=(()=>{class a extends i.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='Edges are not creating automatically. To create a new edge, follow these steps:
(onConnect)
eventConnection
argument. Connection
is similar to an Edge
, but it doesn\'t exists in the flow, you need to "convert" it into a new Edge
Edge[]
list with the new edge that was created from the Connection
.In the default \'strict\'
mode
of ConnectionSettings
, edges are created from connections with strict adherence to the source
and target
types of the HandleComponent
. This means connections can only be established in one direction based on these properties.
This is the \'loose\'
mode
of ConnectionSettings
, where the flow ignores the handle type
and allows any handle to connect with any other handle. In this mode, an id
must be provided for the HandleComponent
to function correctly.
import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Connection, Edge, Node, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `<vflow [nodes]="nodes" [edges]="edges"\n (onConnect)="createEdge($event)"/>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class DefaultConnectionDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n}\n
'}],LooseConnectionDemoComponent:[{title:"TypeScript",code:'import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Connection, ConnectionSettings, CustomNodeComponent, Edge, Node, VflowComponent, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `\n <vflow\n [nodes]="nodes"\n [edges]="edges"\n [connection]="connection"\n (onConnect)="createEdge($event)"\n />\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class LooseConnectionDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: LooseConnectionNode,\n data: {\n text: \'Node 1\'\n }\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: LooseConnectionNode,\n data: {\n text: \'Node 2\'\n }\n },\n ]\n\n public edges: Edge[] = []\n\n public connection: ConnectionSettings = {\n mode: \'loose\'\n }\n\n public createEdge(connection: Connection) {\n const { source, target, sourceHandle, targetHandle } = connection\n\n this.edges = [...this.edges, {\n id: `${source}${sourceHandle} -> ${target}${targetHandle}`,\n ...connection,\n markers: {\n end: {\n type: \'arrow-closed\'\n }\n }\n }]\n }\n}\n\ninterface LooseConnectionNodeData {\n text: string;\n}\n\n@Component({\n template: `<div class="node">\n {{ node.data?.text }}\n\n <handle type="source" position="top" id="a" />\n <handle type="source" position="right" id="b" />\n <handle type="source" position="bottom" id="c" />\n <handle type="source" position="left" id="d" />\n </div>`,\n styles: [`\n .node {\n width: 100px;\n height: 50px;\n border: 1.5px solid #1b262c;\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: black;\n background-color: white;\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class LooseConnectionNode extends CustomNodeComponent<LooseConnectionNodeData> { }\n
'}]};let j=(()=>{class a extends i.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='Edges are not creating automatically. To create a new edge, follow these steps:
(onConnect)
eventConnection
argument. Connection
is similar to an Edge
, but it doesn\'t exists in the flow, you need to "convert" it into a new Edge
Edge[]
list with the new edge that was created from the Connection
.In the default \'strict\'
mode
of ConnectionSettings
, edges are created from connections with strict adherence to the source
and target
types of the HandleComponent
. This means connections can only be established in one direction based on these properties.
This is the \'loose\'
mode
of ConnectionSettings
, where the flow ignores the handle type
and allows any handle to connect with any other handle. In this mode, an id
must be provided for the HandleComponent
to function correctly.
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from \'@angular/core\';\nimport { DirectedGraph, VertexRef } from \'@vizdom/vizdom-ts-esm\';\nimport { Edge, Node, VflowComponent, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./vizdom-layout-demo.component.html\',\n styleUrls: [\'./vizdom-layout-demo.component.scss\'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class VizdomLayoutDemoComponent implements OnInit {\n @ViewChild(VflowComponent)\n vflow!: VflowComponent\n\n public nodes: Node[] = []\n\n public edges: Edge[] = []\n\n ngOnInit(): void {\n // default layout with one node\n this.layout(\n [\n {\n id: crypto.randomUUID(),\n point: { x: 0, y: 0 },\n type: \'html-template\',\n data: {\n color: randomHex()\n },\n draggable: false\n }\n ]\n )\n }\n\n onNodeClick(node: Node) {\n const newNodeId = crypto.randomUUID()\n\n const nodes: Node[] = [...this.nodes, {\n id: newNodeId,\n point: { x: 0, y: 0 },\n type: \'html-template\',\n draggable: false,\n data: {\n color: randomHex()\n }\n }]\n\n const edges: Edge[] = [...this.edges, {\n source: node.id,\n target: newNodeId,\n id: `${node.id} -> ${newNodeId}`\n }]\n\n this.layout(nodes, edges)\n }\n\n protected fitView() {\n // do not fit when there is initial node\n if (this.nodes.length > 1) {\n this.vflow.fitView({ duration: 750 })\n }\n }\n\n /**\n * Method that responsible to layout and render passed nodes and edges\n */\n private layout(nodesToLayout: Node[], edgesToLayout: Edge[] = []) {\n const graph = new DirectedGraph({\n layout: {\n margin_x: 75\n }\n })\n\n // DirectedGraph not provide VErtexRef ids so we need to store it somewhere\n // for later access\n const vertices = new Map<string, VertexRef>()\n const nodes = new Map<string, Node>()\n\n nodesToLayout.forEach(n => {\n const v = graph.new_vertex({\n // For now we only can use static sized nodes\n layout: {\n shape_w: 150,\n shape_h: 100\n },\n render: {\n id: n.id\n },\n }, {\n compute_bounding_box: false\n })\n\n vertices.set(n.id, v)\n nodes.set(n.id, n)\n })\n\n edgesToLayout.forEach(e => {\n graph.new_edge(\n vertices.get(e.source)!,\n vertices.get(e.target)!,\n )\n })\n\n // Compute layout with vizdom internal algorythm\n const layout = graph.layout().to_json().to_obj()\n\n // Render nodes and edges based on this layout\n this.nodes = layout.nodes.map(n => {\n return {\n ...nodes.get(n.id)!,\n id: n.id,\n point: {\n x: n.x,\n y: n.y\n },\n }\n })\n this.edges = edgesToLayout\n }\n}\n\nfunction randomHex() {\n const hexValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, \'A\', \'B\', \'C\', \'D\', \'E\', \'F\'];\n\n let hex = \'#\';\n\n for (let i = 0; i < 6; i++) {\n const index = Math.floor(Math.random() * hexValues.length)\n hex += hexValues[index];\n }\n\n return hex\n}\n
'},{title:"HTML",code:'<vflow [minZoom]="0.1" [nodes]="nodes" [edges]="edges" (onNodesChange.add)="fitView()">\n <ng-template nodeHtml let-ctx>\n <div (click)="onNodeClick(ctx.node)" [style.background-color]="ctx.node.data.color" class="custom-node">\n {{ ctx.node.data.color }}\n\n <handle type="source" position="bottom" />\n <handle type="target" position="top" />\n </div>\n </ng-template>\n</vflow>`\n
'},{title:"SCSS",code:'.custom-node {\n width: 100px;\n height: 50px;\n background: #bbe1fa;\n border: 1px solid gray;\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n
'}]}},1901:(F,y,d)=>{d.a(F,async(e,m)=>{try{d.r(y),d.d(y,{DynamicComponent:()=>w,default:()=>b});var o=d(6286),i=d(7134),v=d(9143),_=d(2150),N=d(526),Y=d(623),T=d(5879),Z=e([_]);_=(Z.then?(await Z)():Z)[0];const r='This is an example of using the vizdom library for computing layout.
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from \'@angular/core\';\nimport { DirectedGraph, VertexRef } from \'@vizdom/vizdom-ts-esm\';\nimport { Edge, Node, VflowComponent, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./vizdom-layout-demo.component.html\',\n styleUrls: [\'./vizdom-layout-demo.component.scss\'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class VizdomLayoutDemoComponent implements OnInit {\n @ViewChild(VflowComponent)\n vflow!: VflowComponent\n\n public nodes: Node[] = []\n\n public edges: Edge[] = []\n\n ngOnInit(): void {\n // default layout with one node\n this.layout(\n [\n {\n id: crypto.randomUUID(),\n point: { x: 0, y: 0 },\n type: \'html-template\',\n data: {\n color: randomHex()\n },\n draggable: false\n }\n ]\n )\n }\n\n onNodeClick(node: Node) {\n const newNodeId = crypto.randomUUID()\n\n const nodes: Node[] = [...this.nodes, {\n id: newNodeId,\n point: { x: 0, y: 0 },\n type: \'html-template\',\n draggable: false,\n data: {\n color: randomHex()\n }\n }]\n\n const edges: Edge[] = [...this.edges, {\n source: node.id,\n target: newNodeId,\n id: `${node.id} -> ${newNodeId}`\n }]\n\n this.layout(nodes, edges)\n }\n\n protected fitView() {\n // do not fit when there is initial node\n if (this.nodes.length > 1) {\n this.vflow.fitView({ duration: 750 })\n }\n }\n\n /**\n * Method that responsible to layout and render passed nodes and edges\n */\n private layout(nodesToLayout: Node[], edgesToLayout: Edge[] = []) {\n const graph = new DirectedGraph({\n layout: {\n margin_x: 75\n }\n })\n\n // DirectedGraph not provide VErtexRef ids so we need to store it somewhere\n // for later access\n const vertices = new Map<string, VertexRef>()\n const nodes = new Map<string, Node>()\n\n nodesToLayout.forEach(n => {\n const v = graph.new_vertex({\n // For now we only can use static sized nodes\n layout: {\n shape_w: 150,\n shape_h: 100\n },\n render: {\n id: n.id\n },\n }, {\n compute_bounding_box: false\n })\n\n vertices.set(n.id, v)\n nodes.set(n.id, n)\n })\n\n edgesToLayout.forEach(e => {\n graph.new_edge(\n vertices.get(e.source)!,\n vertices.get(e.target)!,\n )\n })\n\n // Compute layout with vizdom internal algorythm\n const layout = graph.layout().to_json().to_obj()\n\n // Render nodes and edges based on this layout\n this.nodes = layout.nodes.map(n => {\n return {\n ...nodes.get(n.id)!,\n id: n.id,\n point: {\n x: n.x,\n y: n.y\n },\n }\n })\n this.edges = edgesToLayout\n }\n}\n\nfunction randomHex() {\n const hexValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, \'A\', \'B\', \'C\', \'D\', \'E\', \'F\'];\n\n let hex = \'#\';\n\n for (let i = 0; i < 6; i++) {\n const index = Math.floor(Math.random() * hexValues.length)\n hex += hexValues[index];\n }\n\n return hex\n}\n
'},{title:"HTML",code:'<vflow [minZoom]="0.1" [nodes]="nodes" [edges]="edges" (onNodesChange.add)="fitView()">\n <ng-template nodeHtml let-ctx>\n <div (click)="onNodeClick(ctx.node)" [style.background-color]="ctx.node.data.color" class="custom-node">\n {{ ctx.node.data.color }}\n\n <handle type="source" position="bottom" />\n <handle type="target" position="top" />\n </div>\n </ng-template>\n</vflow>`\n
'},{title:"SCSS",code:'.custom-node {\n width: 100px;\n height: 50px;\n background: #bbe1fa;\n border: 1px solid gray;\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n
'}]}},1901:(S,k,p)=>{p.a(S,async(e,m)=>{try{p.r(k),p.d(k,{DynamicComponent:()=>w,default:()=>b});var _=p(6286),i=p(7134),y=p(9143),g=p(2150),F=p(526),Y=p(623),H=p(5879),Z=e([g]);g=(Z.then?(await Z)():Z)[0];const r='This is an example of using the vizdom library for computing layout.
function isDynamicNode(node: Node<unknown> | DynamicNode<unknown>): boolean;\n
Name | Type | Description |
---|---|---|
node | Node<unknown> | DynamicNode<unknown> |
CustomNodeBaseComponent<T>
ImplementsOnInit
Name | Type | Description |
---|---|---|
data inherited from CustomNodeBaseComponent | WritableSignal<T | undefined> | |
p destroyRef inherited from CustomNodeBaseComponent | DestroyRef | |
@Input overrides CustomNodeBaseComponent | ComponentDynamicNode<T> | Reference to node bound to this component |
selected inherited from CustomNodeBaseComponent | WritableSignal<boolean> | Signal with selected state of node |
function isDefaultStaticNode(node: Node<unknown>): boolean;\n
Name | Type | Description |
---|---|---|
node | Node<unknown> |
import { CommonModule } from \'@angular/common\';\nimport { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Edge, Node, VflowModule, Connection } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./multiple-connection-points-demo.component.html\',\n styleUrls: [\'./multiple-connection-points-demo.styles.scss\'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule, CommonModule]\n})\nexport class MultipleConnectionPointsDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 0, y: 150 },\n type: \'html-template\',\n data: {\n type: \'output\',\n output1: \'output1\',\n output2: \'output2\'\n }\n },\n {\n id: \'2\',\n point: { x: 250, y: 100 },\n type: \'html-template\',\n data: {\n type: \'input\',\n input1: \'input1\',\n input2: \'input2\'\n }\n },\n ]\n\n public edges: Edge[] = [\n {\n id: \'1 -> 2 one\',\n source: \'1\',\n target: \'2\',\n sourceHandle: \'output1\',\n targetHandle: \'input1\',\n },\n {\n id: \'1 -> 2 two\',\n source: \'1\',\n target: \'2\',\n sourceHandle: \'output2\',\n targetHandle: \'input2\'\n },\n ]\n\n public createEdge({ source, target, sourceHandle, targetHandle }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}${sourceHandle ?? \'\'}${targetHandle ?? \'\'}`,\n source,\n target,\n sourceHandle,\n targetHandle\n }]\n }\n}\n
'},{title:"HTML",code:'<vflow [nodes]="nodes" [edges]="edges" (onConnect)="createEdge($event)">\n <ng-template nodeHtml let-ctx>\n <ng-container *ngIf="ctx.node.data.type === \'output\'">\n <div class="custom-node">\n <div class="data-block">\n Output 1\n <handle position="right" type="source" [id]="ctx.node.data.output1"/>\n </div>\n\n <div class="data-block">\n Output 2\n <handle position="right" type="source" [id]="ctx.node.data.output2"/>\n </div>\n </div>\n </ng-container>\n\n <ng-container *ngIf="ctx.node.data.type === \'input\'">\n <div class="custom-node">\n <div class="data-block">\n Input 1\n <handle position="left" type="target" [id]="ctx.node.data.input1"/>\n </div>\n\n <div class="data-block">\n Input 2\n <handle position="left" type="target" [id]="ctx.node.data.input2"/>\n </div>\n </div>\n </ng-container>\n </ng-template>\n</vflow>\n
'},{title:"SCSS",code:'.custom-node {\n width: 150px;\n height: 100px;\n background-color: #0f4c75;\n border: 1px solid gray;\n border-radius: 5px;\n align-items: center;\n padding-left: 7px;\n padding-right: 7px;\n}\n\n.data-block {\n background-color: #ffffff;\n color: #1b262c;\n margin-top: 15px;\n border-radius: 5px;\n text-align: center;\n}\n
'}]};let r=(()=>{class n extends i.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='Custom components provide the ability to control handles and their positions.
All you need to do is take HandleComponent
from library and place it somewhere in your custom node. This component automatically computes its position based on parent element\'s position.
import { CommonModule } from \'@angular/common\';\nimport { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Edge, Node, VflowModule, Connection } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./multiple-connection-points-demo.component.html\',\n styleUrls: [\'./multiple-connection-points-demo.styles.scss\'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule, CommonModule]\n})\nexport class MultipleConnectionPointsDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 0, y: 150 },\n type: \'html-template\',\n data: {\n type: \'output\',\n output1: \'output1\',\n output2: \'output2\'\n }\n },\n {\n id: \'2\',\n point: { x: 250, y: 100 },\n type: \'html-template\',\n data: {\n type: \'input\',\n input1: \'input1\',\n input2: \'input2\'\n }\n },\n ]\n\n public edges: Edge[] = [\n {\n id: \'1 -> 2 one\',\n source: \'1\',\n target: \'2\',\n sourceHandle: \'output1\',\n targetHandle: \'input1\',\n },\n {\n id: \'1 -> 2 two\',\n source: \'1\',\n target: \'2\',\n sourceHandle: \'output2\',\n targetHandle: \'input2\'\n },\n ]\n\n public createEdge({ source, target, sourceHandle, targetHandle }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}${sourceHandle ?? \'\'}${targetHandle ?? \'\'}`,\n source,\n target,\n sourceHandle,\n targetHandle\n }]\n }\n}\n
'},{title:"HTML",code:'<vflow [nodes]="nodes" [edges]="edges" (onConnect)="createEdge($event)">\n <ng-template nodeHtml let-ctx>\n <ng-container *ngIf="ctx.node.data.type === \'output\'">\n <div class="custom-node">\n <div class="data-block">\n Output 1\n <handle position="right" type="source" [id]="ctx.node.data.output1"/>\n </div>\n\n <div class="data-block">\n Output 2\n <handle position="right" type="source" [id]="ctx.node.data.output2"/>\n </div>\n </div>\n </ng-container>\n\n <ng-container *ngIf="ctx.node.data.type === \'input\'">\n <div class="custom-node">\n <div class="data-block">\n Input 1\n <handle position="left" type="target" [id]="ctx.node.data.input1"/>\n </div>\n\n <div class="data-block">\n Input 2\n <handle position="left" type="target" [id]="ctx.node.data.input2"/>\n </div>\n </div>\n </ng-container>\n </ng-template>\n</vflow>\n
'},{title:"SCSS",code:'.custom-node {\n width: 150px;\n height: 100px;\n background-color: #0f4c75;\n border: 1px solid gray;\n border-radius: 5px;\n align-items: center;\n padding-left: 7px;\n padding-right: 7px;\n}\n\n.data-block {\n background-color: #ffffff;\n color: #1b262c;\n margin-top: 15px;\n border-radius: 5px;\n text-align: center;\n}\n
'}]};let r=(()=>{class n extends i.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='Custom components provide the ability to control handles and their positions.
All you need to do is take HandleComponent
from library and place it somewhere in your custom node. This component automatically computes its position based on parent element\'s position.
function isStaticNode(node: Node<unknown> | DynamicNode<unknown>): boolean;\n
Name | Type | Description |
---|---|---|
node | Node<unknown> | DynamicNode<unknown> |
import { ChangeDetectionStrategy, Component, signal, ViewChild } from \'@angular/core\';\nimport * as d3 from \'d3-force\';\nimport { DynamicNode, Edge, VflowComponent, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./force-layout-demo.component.html\',\n styleUrls: [\'./force-layout-demo.component.scss\'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class ForceLayoutDemoComponent {\n @ViewChild(VflowComponent)\n vflow!: VflowComponent\n\n protected nodes: DynamicNode[] = [\n {\n id: \'0\',\n point: signal({ x: 0, y: 0 }),\n type: \'html-template\',\n data: signal(randomHex()),\n draggable: signal(false)\n },\n {\n id: \'1\',\n point: signal({ x: 0, y: 0 }),\n type: \'html-template\',\n data: signal(randomHex()),\n draggable: signal(false)\n },\n {\n id: \'2\',\n point: signal({ x: 0, y: 0 }),\n type: \'html-template\',\n data: signal(randomHex()),\n draggable: signal(false)\n },\n {\n id: \'3\',\n point: signal({ x: 0, y: 0 }),\n type: \'html-template\',\n data: signal(randomHex()),\n draggable: signal(false)\n },\n {\n id: \'4\',\n point: signal({ x: 0, y: 0 }),\n type: \'html-template\',\n data: signal(randomHex()),\n draggable: signal(false)\n },\n {\n id: \'5\',\n point: signal({ x: 0, y: 0 }),\n type: \'html-template\',\n data: signal(randomHex()),\n draggable: signal(false)\n },\n ]\n\n protected edges: Edge[] = [\n {\n source: \'0\',\n target: \'1\',\n id: \'0 -> 1\'\n },\n {\n source: \'0\',\n target: \'2\',\n id: \'0 -> 2\'\n },\n {\n source: \'0\',\n target: \'3\',\n id: \'0 -> 3\'\n },\n {\n source: \'0\',\n target: \'4\',\n id: \'0 -> 4\'\n },\n {\n source: \'0\',\n target: \'5\',\n id: \'0 -> 5\'\n },\n ]\n\n // d3-force internally reads/writes to x and y properties\n // so to link d3-force state with ngx-vflow state, we just\n // proxy these properties to point signal\n private simulationNodes = this.nodes.map((n) => {\n return {\n id: n.id,\n\n get x() {\n return n.point().x\n },\n\n set x(x: number) {\n n.point.update(state => ({ ...state, x }))\n },\n\n get y() {\n return n.point().y\n },\n\n set y(y: number) {\n n.point.update(state => ({ ...state, y }))\n },\n }\n })\n\n private linkForce = d3\n .forceLink(this.edges.map(e => ({ source: e.source, target: e.target })))\n // @ts-ignore\n .id((d) => d.id)\n .distance(50)\n\n private simulation = d3.forceSimulation()\n .force("charge", d3.forceManyBody().strength(-100))\n .force("x", d3.forceX())\n .force("y", d3.forceY())\n // center of viewport\n .force("center", d3.forceCenter(200, 200))\n .nodes(this.simulationNodes)\n .force("link", this.linkForce)\n\n protected onDistanceChange(event: Event) {\n const distance = +(event.target as HTMLInputElement).value\n\n this.linkForce.distance(distance)\n this.simulation.alpha(.5).restart()\n\n setTimeout(() => {\n this.vflow.fitView({ duration: 500 })\n }, 250);\n }\n}\n\nfunction randomHex() {\n const hexValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, \'A\', \'B\', \'C\', \'D\', \'E\', \'F\'];\n\n let hex = \'#\';\n\n for (let i = 0; i < 6; i++) {\n const index = Math.floor(Math.random() * hexValues.length)\n hex += hexValues[index];\n }\n\n return hex\n}\n
'},{title:"HTML",code:'<label class="slider">\n <span>Distance</span>\n <input type="range" min="0" step="1" max="200" value="50" (input)="onDistanceChange($event)">\n</label>\n\n<vflow [nodes]="nodes" [edges]="edges">\n <ng-template nodeHtml let-ctx>\n <div [style.background-color]="ctx.node.data()" class="custom-node">\n {{ ctx.node.data() }}\n\n <handle type="source" position="bottom" />\n <handle type="target" position="top" />\n </div>\n </ng-template>\n</vflow>\n
'},{title:"SCSS",code:'.custom-node {\n width: 100px;\n height: 50px;\n background: #bbe1fa;\n border: 1px solid gray;\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.slider {\n position: absolute;\n display: flex;\n padding: 4px;\n gap: 4px;\n\n background-color: #2e414c;\n border-bottom-right-radius: 6px;\n}\n
'}]};let B=(()=>{class s extends H.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='This is a simple example of using the d3-force layout package.
import { ChangeDetectionStrategy, Component, ViewChild } from \'@angular/core\';\nimport { DndDropEvent, DndModule } from \'ngx-drag-drop\';\nimport { Connection, Edge, Node, VflowComponent, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./drag-and-drop-nodes-demo.component.html\',\n styleUrls: [\'./drag-and-drop-nodes-demo.component.scss\'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule, DndModule]\n})\nexport class DragAndDropNodesDemoComponent {\n @ViewChild(VflowComponent)\n public vflow!: VflowComponent\n\n public nodes: Node[] = [\n {\n id: crypto.randomUUID(),\n point: { x: 10, y: 150 },\n type: \'html-template\',\n },\n ]\n\n public edges: Edge[] = []\n\n public createNode({ event }: DndDropEvent) {\n const point = this.vflow.documentPointToFlowPoint({\n x: event.x,\n y: event.y\n })\n\n this.nodes = [...this.nodes, {\n id: crypto.randomUUID(),\n point,\n type: \'html-template\'\n }]\n }\n\n public connect({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n}\n
'},{title:"HTML",code:'<div class="sidebar">\n <div\n dndDraggable\n class="custom-node custom-node_draggable"\n >\n <button class="custom-node__button">Drag here</button>\n </div>\n</div>\n\n<vflow\n dndDropzone\n (dndDrop)="createNode($event)"\n (onConnect)="connect($event)"\n [nodes]="nodes"\n [edges]="edges"\n>\n <ng-template nodeHtml let-ctx>\n <div\n class="custom-node"\n [class.custom-node_selected]="ctx.selected()"\n >\n <button class="custom-node__button" selectable>Select here</button>\n\n <handle type="source" position="right" />\n <handle type="target" position="left" />\n </div>\n </ng-template>\n\n <ng-template edge let-ctx>\n <svg:path\n selectable\n [attr.d]="ctx.path()"\n [attr.stroke]="ctx.selected() ? \'#0f4c75\' : \'#bbe1fa\'"\n fill="none"\n stroke-width="2"\n />\n </ng-template>\n</vflow>`\n
'},{title:"SCSS",code:':host {\n display: flex;\n}\n\n.sidebar {\n padding: 10px;\n height: 400px;\n background-color: #3282b8;\n}\n\n.custom-node {\n width: 150px;\n height: 100px;\n background: #bbe1fa;\n border: 1px solid gray;\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__button {\n border: 0;\n outline: 0;\n cursor: pointer;\n color: white;\n background-color: #1b262c;\n border-radius: 4px;\n font-size: 14px;\n font-weight: 500;\n padding: 4px 8px;\n display: inline-block;\n min-height: 28px;\n }\n\n &_draggable {\n width: 100px;\n height: 75px;\n }\n\n &_selected {\n border-color: #1b262c;\n }\n}\n
'}]};let R=(()=>{class s extends v.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='This workshop will show you how to implement dynamic node creation with basic drag and drop implementation.
This implementation uses documentPointToFlowPoint()
method of VflowComponent
that translates document coordinate to vflow coordinate.
import { ChangeDetectionStrategy, Component, ViewChild } from \'@angular/core\';\nimport { DndDropEvent, DndModule } from \'ngx-drag-drop\';\nimport { Connection, Edge, Node, VflowComponent, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./drag-and-drop-nodes-demo.component.html\',\n styleUrls: [\'./drag-and-drop-nodes-demo.component.scss\'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule, DndModule]\n})\nexport class DragAndDropNodesDemoComponent {\n @ViewChild(VflowComponent)\n public vflow!: VflowComponent\n\n public nodes: Node[] = [\n {\n id: crypto.randomUUID(),\n point: { x: 10, y: 150 },\n type: \'html-template\',\n },\n ]\n\n public edges: Edge[] = []\n\n public createNode({ event }: DndDropEvent) {\n const point = this.vflow.documentPointToFlowPoint({\n x: event.x,\n y: event.y\n })\n\n this.nodes = [...this.nodes, {\n id: crypto.randomUUID(),\n point,\n type: \'html-template\'\n }]\n }\n\n public connect({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n}\n
'},{title:"HTML",code:'<div class="sidebar">\n <div\n dndDraggable\n class="custom-node custom-node_draggable"\n >\n <button class="custom-node__button">Drag here</button>\n </div>\n</div>\n\n<vflow\n dndDropzone\n (dndDrop)="createNode($event)"\n (onConnect)="connect($event)"\n [nodes]="nodes"\n [edges]="edges"\n>\n <ng-template nodeHtml let-ctx>\n <div\n class="custom-node"\n [class.custom-node_selected]="ctx.selected()"\n >\n <button class="custom-node__button" selectable>Select here</button>\n\n <handle type="source" position="right" />\n <handle type="target" position="left" />\n </div>\n </ng-template>\n\n <ng-template edge let-ctx>\n <svg:path\n selectable\n [attr.d]="ctx.path()"\n [attr.stroke]="ctx.selected() ? \'#0f4c75\' : \'#bbe1fa\'"\n fill="none"\n stroke-width="2"\n />\n </ng-template>\n</vflow>`\n
'},{title:"SCSS",code:':host {\n display: flex;\n}\n\n.sidebar {\n padding: 10px;\n height: 400px;\n background-color: #3282b8;\n}\n\n.custom-node {\n width: 150px;\n height: 100px;\n background: #bbe1fa;\n border: 1px solid gray;\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__button {\n border: 0;\n outline: 0;\n cursor: pointer;\n color: white;\n background-color: #1b262c;\n border-radius: 4px;\n font-size: 14px;\n font-weight: 500;\n padding: 4px 8px;\n display: inline-block;\n min-height: 28px;\n }\n\n &_draggable {\n width: 100px;\n height: 75px;\n }\n\n &_selected {\n border-color: #1b262c;\n }\n}\n
'}]};let R=(()=>{class s extends v.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='This workshop will show you how to implement dynamic node creation with basic drag and drop implementation.
This implementation uses documentPointToFlowPoint()
method of VflowComponent
that translates document coordinate to vflow coordinate.
import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Edge, Node, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `<vflow [nodes]="nodes" [edges]="edges" />`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class DefaultEdgesDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 10, y: 200 },\n type: \'default\',\n text: \'1\'\n },\n {\n id: \'2\',\n point: { x: 200, y: 100 },\n type: \'default\',\n text: \'2\'\n },\n {\n id: \'3\',\n point: { x: 200, y: 300 },\n type: \'default\',\n text: \'3\'\n },\n ]\n\n public edges: Edge[] = [\n {\n id: \'1 -> 2\',\n source: \'1\',\n target: \'2\'\n },\n {\n id: \'1 -> 3\',\n source: \'1\',\n target: \'3\'\n },\n ]\n}\n
'}]};let g=(()=>{class s extends c.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='You can link nodes with edges. All you need to do is to create another Edge[]
array and pass it to the vflow
component. Each edge contains the id of the source
and target
nodes, and each edge must have its own id
.
import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Edge, Node, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `<vflow [nodes]="nodes" [edges]="edges" />`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class DefaultEdgesDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 10, y: 200 },\n type: \'default\',\n text: \'1\'\n },\n {\n id: \'2\',\n point: { x: 200, y: 100 },\n type: \'default\',\n text: \'2\'\n },\n {\n id: \'3\',\n point: { x: 200, y: 300 },\n type: \'default\',\n text: \'3\'\n },\n ]\n\n public edges: Edge[] = [\n {\n id: \'1 -> 2\',\n source: \'1\',\n target: \'2\'\n },\n {\n id: \'1 -> 3\',\n source: \'1\',\n target: \'3\'\n },\n ]\n}\n
'}]};let g=(()=>{class s extends c.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='You can link nodes with edges. All you need to do is to create another Edge[]
array and pass it to the vflow
component. Each edge contains the id of the source
and target
nodes, and each edge must have its own id
.
import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Connection, ConnectionSettings, Edge, Node, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `<vflow [nodes]="nodes" [edges]="edges"\n [connection]="conectionSettings"\n (onConnect)="createEdge($event)"/>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class ConnectionValidationDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public conectionSettings: ConnectionSettings = {\n validator: (connection) => connection.source === \'1\' && connection.target === \'2\'\n }\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n}\n
'}]};let d=(()=>{class a extends p.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='ngx-vflow
supports real-time synchronous validation of connections. Validation occurs when a user attempts to create a new edge. By default, every connection is valid, but you can provide a ConnectionSettings
with a validatior
callback where you specify the validation logic.
For example, in this case, validation only passes connections from node 1 to node 2. If the validator
returns false
, the (onConnect)
event won\'t be triggered because there is no valid connection.
import { ChangeDetectionStrategy, Component } from \'@angular/core\';\nimport { Connection, ConnectionSettings, Edge, Node, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n template: `<vflow [nodes]="nodes" [edges]="edges"\n [connection]="conectionSettings"\n (onConnect)="createEdge($event)"/>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule]\n})\nexport class ConnectionValidationDemoComponent {\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public conectionSettings: ConnectionSettings = {\n validator: (connection) => connection.source === \'1\' && connection.target === \'2\'\n }\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n}\n
'}]};let d=(()=>{class a extends p.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='ngx-vflow
supports real-time synchronous validation of connections. Validation occurs when a user attempts to create a new edge. By default, every connection is valid, but you can provide a ConnectionSettings
with a validatior
callback where you specify the validation logic.
For example, in this case, validation only passes connections from node 1 to node 2. If the validator
returns false
, the (onConnect)
event won\'t be triggered because there is no valid connection.
Name | Type | Description |
---|---|---|
draggable | boolean | undefined | |
id | string | |
point | Point |
Name | Type | Description |
---|---|---|
draggable | boolean | undefined | |
id | string | |
point | Point |
import { ChangeDetectionStrategy, Component, TemplateRef, ViewChild, inject } from \'@angular/core\';\nimport { NgDocNotifyService } from \'@ng-doc/ui-kit\';\nimport { Connection, Edge, EdgeChange, Node, NodeChange, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./handling-changes-demo.component.html\',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule],\n})\nexport class HandlingChangesDemoComponent {\n private notifyService = inject(NgDocNotifyService)\n\n @ViewChild(\'toast\')\n public toastTemplate!: TemplateRef<{}>;\n\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public toastData: any = {}\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n\n public handleNodeChanges(changes: NodeChange[]) {\n this.toastData = {\n title: \'(onNodesChange)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleEdgeChanges(changes: EdgeChange[]) {\n this.toastData = {\n title: \'(onEdgesChange)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n}\n
'},{title:"HTML",code:'<vflow\n [nodes]="nodes"\n [edges]="edges"\n (onConnect)="createEdge($event)"\n (onNodesChange)="handleNodeChanges($event)"\n (onEdgesChange)="handleEdgeChanges($event)"\n/>\n\n<ng-template #toast>\n <h3>{{ toastData?.title }}</h3>\n\n <pre>{{ toastData?.json }}</pre>\n</ng-template>\n
'}],HandlingChangesFilteredDemoComponent:[{title:"TypeScript",code:'import { ChangeDetectionStrategy, Component, TemplateRef, ViewChild, inject } from \'@angular/core\';\nimport { NgDocNotifyService } from \'@ng-doc/ui-kit\';\nimport { Connection, Edge, EdgeChange, Node, NodeAddChange, NodeChange, NodePositionChange, NodeSelectedChange, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./handling-changes-filtered-demo.component.html\',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule],\n})\nexport class HandlingChangesFilteredDemoComponent {\n private notifyService = inject(NgDocNotifyService)\n\n @ViewChild(\'toast\')\n public toastTemplate!: TemplateRef<{}>;\n\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public toastData: any = {}\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n\n public handleNodePositionChange(change: NodePositionChange) {\n this.toastData = {\n title: \'(onNodesChange.position.single)\',\n json: JSON.stringify(change, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleNodeSelectChange(change: NodeSelectedChange) {\n this.toastData = {\n title: \'(onNodesChange.select.single)\',\n json: JSON.stringify(change, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleNodesAddChange(changes: NodeAddChange[]) {\n this.toastData = {\n title: \'(onNodesChange.add.many)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleEdgesAddChange(changes: EdgeChange[]) {\n this.toastData = {\n title: \'(onEdgesChange.add)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public addNodes() {\n this.nodes = [...this.nodes,\n {\n id: crypto.randomUUID(),\n point: { x: 0, y: 0 },\n type: \'default\',\n text: `random`,\n },\n {\n id: crypto.randomUUID(),\n point: { x: 300, y: 300 },\n type: \'default\',\n text: `random`\n },\n ]\n }\n}\n
'},{title:"HTML",code:'<button (click)="addNodes()">Add nodes</button>\n\n<vflow\n [nodes]="nodes"\n [edges]="edges"\n (onConnect)="createEdge($event)"\n (onNodesChange.position.single)="handleNodePositionChange($event)"\n (onNodesChange.select.single)="handleNodeSelectChange($event)"\n (onNodesChange.add.many)="handleNodesAddChange($event)"\n (onEdgesChange.add)="handleEdgesAddChange($event)"\n/>\n\n<ng-template #toast let-ctx>\n <h3>{{ toastData?.title }}</h3>\n\n <pre>{{ toastData?.json }}</pre>\n</ng-template>\n
'}]};let j=(()=>{class e extends i.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='You can observe changes in the toasts.
You can observe various changes in nodes and edges.
Types of NodeChange
s:
position
- new node position after drag and dropadd
- when node was createdremove
- when node was removedselect
- when node was selected (also triggers for unselected nodes)Types of EdgeChange
s:
add
- when edge was createdremove
- when edge was removedselect
- when edge was selected (also triggers for unselected edges)detached
- when edge became invisible due to the absence of the source or target node. Use this to delete such edges from the edges listThere are a several ways to receive these changes:
This is a way to get every possible change. Changes came as non empty arrays:
(onNodesChange)
emits NodeChange[]
(onEdgesChange)
emits EdgeChange[]
For your convenience, here is the filtering scheme for changes based on the (onNodesChange)
and (onEdgesChange)
events:
(onNodesChange.[NodeChangeType])
- a list of node changes of a certain type(onNodesChange.[EdgeChangeType])
- a list of edge changes of a certain type(onNodesChange.[NodeChangeType].[Count])
- a list (many
) or single (single
) node change of a certain type(onEdgesChange.[EdgeChangeType].[Count])
- a list (many
) or single (single
) edge change of a certain typeWhere:
type NodeChangeType = \'position\' | \'add\' | \'remove\' | \'select\'\n\ntype EdgeChangeType = \'detached\' | \'add\' | \'remove\' | \'select\'\n\n// single - when there is only one change of this type (for example if you drag and drop some node, it\'s consireder as single change)\n// many - when there is more than one change of this type (for example if you deleted several nodes at the same time)\ntype Count = \'single\' | \'many\'\n
List of all possible filter outputs:
\'onNodesChange.position\',\n\'onNodesChange.position.single\',\n\'onNodesChange.position.many\',\n\'onNodesChange.add\',\n\'onNodesChange.add.single\',\n\'onNodesChange.add.many\',\n\'onNodesChange.remove\',\n\'onNodesChange.remove.single\',\n\'onNodesChange.remove.many\',\n\'onNodesChange.select\',\n\'onNodesChange.select.single\',\n\'onNodesChange.select.many\',\n\n\'onEdgesChange.detached\',\n\'onEdgesChange.detached.single\',\n\'onEdgesChange.detached.many\',\n\'onEdgesChange.add\',\n\'onEdgesChange.add.single\',\n\'onEdgesChange.add.many\',\n\'onEdgesChange.remove\',\n\'onEdgesChange.remove.single\',\n\'onEdgesChange.remove.many\',\n\'onEdgesChange.select\',\n\'onEdgesChange.select.single\',\n\'onEdgesChange.select.many\',\n
{\n ...\n @ViewChild(VflowComponent)\n vflow: VflowComponent\n\n ngAfterViewInit() {\n this.vflow.nodesChange$.subscribe((changes) => {\n // handle node changes\n })\n\n this.vflow.edgesChange$.subscribe((changes) => {\n // handle edges changes\n })\n }\n ...\n}\n
',this.editSourceFileUrl="https://github.com/artem-mangilev/ngx-vflow/edit/main/projects/ngx-vflow-demo/src/app/categories/features/pages/handling-changes/index.md?message=docs(handling-changes): describe your changes here...",this.page=t,this.demoAssets=k}static#s=this.\u0275fac=function(a){return new(a||e)};static#n=this.\u0275cmp=s.Xpm({type:e,selectors:[["ng-doc-page-features-handling-changes"]],standalone:!0,features:[s._Bn([{provide:i.a,useExisting:e},x,t.providers??[]]),s.qOj,s.jDz],decls:1,vars:0,template:function(a,l){1&a&&s._UZ(0,"ng-doc-page")},dependencies:[f.z],encapsulation:2,changeDetection:0})}return e})();const D=[{...(0,m.isRoute)(t.route)?t.route:{},path:"",component:j,title:"Handling changes"}]}}]);
\ No newline at end of file
+"use strict";(self.webpackChunkngx_vflow_demo=self.webpackChunkngx_vflow_demo||[]).push([[3842],{3842:(E,c,g)=>{g.r(c),g.d(c,{DynamicComponent:()=>j,default:()=>D});var i=g(6286),f=g(7134),m=g(9143),u=g(2936),s=g(5879),o=g(8413),h=g(2898),r=g(8944);const y=["toast"];function C(e,T){if(1&e&&(s.TgZ(0,"h3"),s._uU(1),s.qZA(),s.TgZ(2,"pre"),s._uU(3),s.qZA()),2&e){const n=s.oxw();s.xp6(1),s.Oqu(null==n.toastData?null:n.toastData.title),s.xp6(2),s.Oqu(null==n.toastData?null:n.toastData.json)}}const v=["toast"];function w(e,T){if(1&e&&(s.TgZ(0,"h3"),s._uU(1),s.qZA(),s.TgZ(2,"pre"),s._uU(3),s.qZA()),2&e){const n=s.oxw();s.xp6(1),s.Oqu(null==n.toastData?null:n.toastData.title),s.xp6(2),s.Oqu(null==n.toastData?null:n.toastData.json)}}const t={title:"Handling changes",mdFile:"./index.md",category:u.Z,demos:{HandlingChangesDemoComponent:(()=>{class e{constructor(){this.notifyService=(0,s.f3M)(o.Z),this.nodes=[{id:"1",point:{x:100,y:100},type:"default",text:"1"},{id:"2",point:{x:200,y:200},type:"default",text:"2"}],this.edges=[],this.toastData={}}createEdge({source:n,target:a}){this.edges=[...this.edges,{id:`${n} -> ${a}`,source:n,target:a}]}handleNodeChanges(n){this.toastData={title:"(onNodesChange)",json:JSON.stringify(n,null,2)},this.notifyService.notify(this.toastTemplate)}handleEdgeChanges(n){this.toastData={title:"(onEdgesChange)",json:JSON.stringify(n,null,2)},this.notifyService.notify(this.toastTemplate)}static#s=this.\u0275fac=function(a){return new(a||e)};static#n=this.\u0275cmp=s.Xpm({type:e,selectors:[["ng-component"]],viewQuery:function(a,l){if(1&a&&s.Gf(y,5),2&a){let d;s.iGM(d=s.CRH())&&(l.toastTemplate=d.first)}},standalone:!0,features:[s.jDz],decls:3,vars:2,consts:[[3,"nodes","edges","onConnect","onNodesChange","onEdgesChange"],["toast",""]],template:function(a,l){1&a&&(s.TgZ(0,"vflow",0),s.NdJ("onConnect",function(p){return l.createEdge(p)})("onNodesChange",function(p){return l.handleNodeChanges(p)})("onEdgesChange",function(p){return l.handleEdgeChanges(p)}),s.qZA(),s.YNc(1,C,4,2,"ng-template",null,1,s.W1O)),2&a&&s.Q6J("nodes",l.nodes)("edges",l.edges)},dependencies:[h.p,r.t],encapsulation:2,changeDetection:0})}return e})(),HandlingChangesFilteredDemoComponent:(()=>{class e{constructor(){this.notifyService=(0,s.f3M)(o.Z),this.nodes=[{id:"1",point:{x:100,y:100},type:"default",text:"1"},{id:"2",point:{x:200,y:200},type:"default",text:"2"}],this.edges=[],this.toastData={}}createEdge({source:n,target:a}){this.edges=[...this.edges,{id:`${n} -> ${a}`,source:n,target:a}]}handleNodePositionChange(n){this.toastData={title:"(onNodesChange.position.single)",json:JSON.stringify(n,null,2)},this.notifyService.notify(this.toastTemplate)}handleNodeSelectChange(n){this.toastData={title:"(onNodesChange.select.single)",json:JSON.stringify(n,null,2)},this.notifyService.notify(this.toastTemplate)}handleNodesAddChange(n){this.toastData={title:"(onNodesChange.add.many)",json:JSON.stringify(n,null,2)},this.notifyService.notify(this.toastTemplate)}handleEdgesAddChange(n){this.toastData={title:"(onEdgesChange.add)",json:JSON.stringify(n,null,2)},this.notifyService.notify(this.toastTemplate)}addNodes(){this.nodes=[...this.nodes,{id:crypto.randomUUID(),point:{x:0,y:0},type:"default",text:"random"},{id:crypto.randomUUID(),point:{x:300,y:300},type:"default",text:"random"}]}static#s=this.\u0275fac=function(a){return new(a||e)};static#n=this.\u0275cmp=s.Xpm({type:e,selectors:[["ng-component"]],viewQuery:function(a,l){if(1&a&&s.Gf(v,5),2&a){let d;s.iGM(d=s.CRH())&&(l.toastTemplate=d.first)}},standalone:!0,features:[s.jDz],decls:5,vars:2,consts:[[3,"click"],[3,"nodes","edges","onConnect","onNodesChange.position.single","onNodesChange.select.single","onNodesChange.add.many","onEdgesChange.add"],["toast",""]],template:function(a,l){1&a&&(s.TgZ(0,"button",0),s.NdJ("click",function(){return l.addNodes()}),s._uU(1,"Add nodes"),s.qZA(),s.TgZ(2,"vflow",1),s.NdJ("onConnect",function(p){return l.createEdge(p)})("onNodesChange.position.single",function(p){return l.handleNodePositionChange(p)})("onNodesChange.select.single",function(p){return l.handleNodeSelectChange(p)})("onNodesChange.add.many",function(p){return l.handleNodesAddChange(p)})("onEdgesChange.add",function(p){return l.handleEdgesAddChange(p)}),s.qZA(),s.YNc(3,w,4,2,"ng-template",null,2,s.W1O)),2&a&&(s.xp6(2),s.Q6J("nodes",l.nodes)("edges",l.edges))},dependencies:[h.p,r.t],encapsulation:2,changeDetection:0})}return e})()},order:3},x=[],k={HandlingChangesDemoComponent:[{title:"TypeScript",code:'import { ChangeDetectionStrategy, Component, TemplateRef, ViewChild, inject } from \'@angular/core\';\nimport { NgDocNotifyService } from \'@ng-doc/ui-kit\';\nimport { Connection, Edge, EdgeChange, Node, NodeChange, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./handling-changes-demo.component.html\',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule],\n})\nexport class HandlingChangesDemoComponent {\n private notifyService = inject(NgDocNotifyService)\n\n @ViewChild(\'toast\')\n public toastTemplate!: TemplateRef<{}>;\n\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public toastData: any = {}\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n\n public handleNodeChanges(changes: NodeChange[]) {\n this.toastData = {\n title: \'(onNodesChange)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleEdgeChanges(changes: EdgeChange[]) {\n this.toastData = {\n title: \'(onEdgesChange)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n}\n
'},{title:"HTML",code:'<vflow\n [nodes]="nodes"\n [edges]="edges"\n (onConnect)="createEdge($event)"\n (onNodesChange)="handleNodeChanges($event)"\n (onEdgesChange)="handleEdgeChanges($event)"\n/>\n\n<ng-template #toast>\n <h3>{{ toastData?.title }}</h3>\n\n <pre>{{ toastData?.json }}</pre>\n</ng-template>\n
'}],HandlingChangesFilteredDemoComponent:[{title:"TypeScript",code:'import { ChangeDetectionStrategy, Component, TemplateRef, ViewChild, inject } from \'@angular/core\';\nimport { NgDocNotifyService } from \'@ng-doc/ui-kit\';\nimport { Connection, Edge, EdgeChange, Node, NodeAddChange, NodeChange, NodePositionChange, NodeSelectedChange, VflowModule } from \'projects/ngx-vflow-lib/src/public-api\';\n\n@Component({\n templateUrl: \'./handling-changes-filtered-demo.component.html\',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [VflowModule],\n})\nexport class HandlingChangesFilteredDemoComponent {\n private notifyService = inject(NgDocNotifyService)\n\n @ViewChild(\'toast\')\n public toastTemplate!: TemplateRef<{}>;\n\n public nodes: Node[] = [\n {\n id: \'1\',\n point: { x: 100, y: 100 },\n type: \'default\',\n text: `1`,\n },\n {\n id: \'2\',\n point: { x: 200, y: 200 },\n type: \'default\',\n text: `2`\n },\n ]\n\n public edges: Edge[] = []\n\n public toastData: any = {}\n\n public createEdge({ source, target }: Connection) {\n this.edges = [...this.edges, {\n id: `${source} -> ${target}`,\n source,\n target\n }]\n }\n\n public handleNodePositionChange(change: NodePositionChange) {\n this.toastData = {\n title: \'(onNodesChange.position.single)\',\n json: JSON.stringify(change, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleNodeSelectChange(change: NodeSelectedChange) {\n this.toastData = {\n title: \'(onNodesChange.select.single)\',\n json: JSON.stringify(change, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleNodesAddChange(changes: NodeAddChange[]) {\n this.toastData = {\n title: \'(onNodesChange.add.many)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public handleEdgesAddChange(changes: EdgeChange[]) {\n this.toastData = {\n title: \'(onEdgesChange.add)\',\n json: JSON.stringify(changes, null, 2)\n }\n\n this.notifyService.notify(this.toastTemplate)\n }\n\n public addNodes() {\n this.nodes = [...this.nodes,\n {\n id: crypto.randomUUID(),\n point: { x: 0, y: 0 },\n type: \'default\',\n text: `random`,\n },\n {\n id: crypto.randomUUID(),\n point: { x: 300, y: 300 },\n type: \'default\',\n text: `random`\n },\n ]\n }\n}\n
'},{title:"HTML",code:'<button (click)="addNodes()">Add nodes</button>\n\n<vflow\n [nodes]="nodes"\n [edges]="edges"\n (onConnect)="createEdge($event)"\n (onNodesChange.position.single)="handleNodePositionChange($event)"\n (onNodesChange.select.single)="handleNodeSelectChange($event)"\n (onNodesChange.add.many)="handleNodesAddChange($event)"\n (onEdgesChange.add)="handleEdgesAddChange($event)"\n/>\n\n<ng-template #toast let-ctx>\n <h3>{{ toastData?.title }}</h3>\n\n <pre>{{ toastData?.json }}</pre>\n</ng-template>\n
'}]};let j=(()=>{class e extends i.a{constructor(){super(),this.routePrefix="",this.pageType="guide",this.pageContent='You can observe changes in the toasts.
You can observe various changes in nodes and edges.
Types of NodeChange
s:
position
- new node position after drag and dropadd
- when node was createdremove
- when node was removedselect
- when node was selected (also triggers for unselected nodes)Types of EdgeChange
s:
add
- when edge was createdremove
- when edge was removedselect
- when edge was selected (also triggers for unselected edges)detached
- when edge became invisible due to the absence of the source or target node. Use this to delete such edges from the edges listThere are a several ways to receive these changes:
This is a way to get every possible change. Changes came as non empty arrays:
(onNodesChange)
emits NodeChange[]
(onEdgesChange)
emits EdgeChange[]
For your convenience, here is the filtering scheme for changes based on the (onNodesChange)
and (onEdgesChange)
events:
(onNodesChange.[NodeChangeType])
- a list of node changes of a certain type(onNodesChange.[EdgeChangeType])
- a list of edge changes of a certain type(onNodesChange.[NodeChangeType].[Count])
- a list (many
) or single (single
) node change of a certain type(onEdgesChange.[EdgeChangeType].[Count])
- a list (many
) or single (single
) edge change of a certain typeWhere:
type NodeChangeType = \'position\' | \'add\' | \'remove\' | \'select\'\n\ntype EdgeChangeType = \'detached\' | \'add\' | \'remove\' | \'select\'\n\n// single - when there is only one change of this type (for example if you drag and drop some node, it\'s consireder as single change)\n// many - when there is more than one change of this type (for example if you deleted several nodes at the same time)\ntype Count = \'single\' | \'many\'\n
List of all possible filter outputs:
\'onNodesChange.position\',\n\'onNodesChange.position.single\',\n\'onNodesChange.position.many\',\n\'onNodesChange.add\',\n\'onNodesChange.add.single\',\n\'onNodesChange.add.many\',\n\'onNodesChange.remove\',\n\'onNodesChange.remove.single\',\n\'onNodesChange.remove.many\',\n\'onNodesChange.select\',\n\'onNodesChange.select.single\',\n\'onNodesChange.select.many\',\n\n\'onEdgesChange.detached\',\n\'onEdgesChange.detached.single\',\n\'onEdgesChange.detached.many\',\n\'onEdgesChange.add\',\n\'onEdgesChange.add.single\',\n\'onEdgesChange.add.many\',\n\'onEdgesChange.remove\',\n\'onEdgesChange.remove.single\',\n\'onEdgesChange.remove.many\',\n\'onEdgesChange.select\',\n\'onEdgesChange.select.single\',\n\'onEdgesChange.select.many\',\n
{\n ...\n @ViewChild(VflowComponent)\n vflow: VflowComponent\n\n ngAfterViewInit() {\n this.vflow.nodesChange$.subscribe((changes) => {\n // handle node changes\n })\n\n this.vflow.edgesChange$.subscribe((changes) => {\n // handle edges changes\n })\n }\n ...\n}\n
',this.editSourceFileUrl="https://github.com/artem-mangilev/ngx-vflow/edit/main/projects/ngx-vflow-demo/src/app/categories/features/pages/handling-changes/index.md?message=docs(handling-changes): describe your changes here...",this.page=t,this.demoAssets=k}static#s=this.\u0275fac=function(a){return new(a||e)};static#n=this.\u0275cmp=s.Xpm({type:e,selectors:[["ng-doc-page-features-handling-changes"]],standalone:!0,features:[s._Bn([{provide:i.a,useExisting:e},x,t.providers??[]]),s.qOj,s.jDz],decls:1,vars:0,template:function(a,l){1&a&&s._UZ(0,"ng-doc-page")},dependencies:[f.z],encapsulation:2,changeDetection:0})}return e})();const D=[{...(0,m.isRoute)(t.route)?t.route:{},path:"",component:j,title:"Handling changes"}]}}]);
\ No newline at end of file
diff --git a/3856.5e7189d0bad70f20.js b/3856.f976724b5ca1f2d1.js
similarity index 95%
rename from 3856.5e7189d0bad70f20.js
rename to 3856.f976724b5ca1f2d1.js
index 9805a75d..aea05d67 100644
--- a/3856.5e7189d0bad70f20.js
+++ b/3856.f976724b5ca1f2d1.js
@@ -1 +1 @@
-"use strict";(self.webpackChunkngx_vflow_demo=self.webpackChunkngx_vflow_demo||[]).push([[3856],{3856:(g,t,e)=>{e.r(t),e.d(t,{DynamicComponent:()=>c,default:()=>d});var o=e(6286),l=e(7134),n=e(5879);let c=(()=>{class s extends o.a{constructor(){super(),this.routePrefix=void 0,this.pageType="api",this.editSourceFileUrl="https://github.com/artem-mangilev/ngx-vflow/edit/main/projects/ngx-vflow-lib/src/lib/vflow/interfaces/component-node-event.interface.ts?message=docs(ngx-vflow): describe your changes here...#L16",this.viewSourceFileUrl="https://github.com/artem-mangilev/ngx-vflow/blob/main/projects/ngx-vflow-lib/src/lib/vflow/interfaces/component-node-event.interface.ts#L16",this.pageContent='Event of custom component node
Generic accepts array of custom components and merge their event emitters for type-safe event handling
type ComponentNodeEvent = { nodeId: string } & {\n [I in keyof T]: EventsFromComponent<T[I]>;\n}[number];\n
Event of custom component node
Generic accepts array of custom components and merge their event emitters for type-safe event handling
type ComponentNodeEvent = { nodeId: string } & {\n [I in keyof T]: EventsFromComponent<T[I]>;\n}[number];\n
function isTemplateDynamicNode(node: DynamicNode<unknown>): boolean;\n
Name | Type | Description |
---|---|---|
node | DynamicNode<unknown> |