-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: throttle node drag with rAF * feat: add width and height props for default node type * feat: add component node type * feat: add onComponentNodeEvent api * feat: add helper type for merging events * docs: extend doc with component nodes description * docs: some minor fixes * feat: add demo page with all features * docs: improve English * fix: fix manual zoom * fix: fix pan * fix: fix pan * up version
- Loading branch information
1 parent
78c94e6
commit 9b829d1
Showing
36 changed files
with
780 additions
and
89 deletions.
There are no files selected for viewing
4 changes: 2 additions & 2 deletions
4
...ngx-vflow-demo/src/app/categories/examples/pages/connection-validation/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
# {{ NgDocPage.title }} | ||
|
||
`ngx-vflow` supports realtime sync validation of connections. Validation performs on user attempt to create new edge. By default, every connection is valid, but you can provide `ConnectionSettings` with `validatior` callback where you specify validation logic. | ||
`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 passes only connection from 1 to 2 node. If `validator` returns `false`, `(onConnect)` even won't be called because there is no valid connection. | ||
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. | ||
|
||
{{ NgDocActions.demo("ConnectionValidationDemoComponent", { expanded: true }) }} |
2 changes: 1 addition & 1 deletion
2
projects/ngx-vflow-demo/src/app/categories/examples/pages/curves/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# {{ NgDocPage.title }} | ||
|
||
It's possible to set curve for both edges and connection. | ||
It's possible to set curve for both the edges and connection. | ||
|
||
{{ NgDocActions.demo("CurvesDemoComponent", { expanded: true }) }} |
2 changes: 1 addition & 1 deletion
2
...cts/ngx-vflow-demo/src/app/categories/examples/pages/custom-background/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# {{ NgDocPage.title }} | ||
|
||
You're able to select background color for your flow. Now it limits to only color, but later it will be possible to set more complex backgrounds. To select color just pass it in `[background]` input. | ||
You're able to select background color for your flow. Currently, it is limited to selecting a color, but in the future, it will be possible to set more complex backgrounds. To select a color, simply pass it to the `[background]` input. | ||
|
||
{{ NgDocActions.demo("CustomBackgroundDemoComponent", { expanded: true }) }} |
10 changes: 5 additions & 5 deletions
10
projects/ngx-vflow-demo/src/app/categories/examples/pages/custom-edges/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
.../app/categories/examples/pages/custom-nodes/demo/custom-component-nodes-demo.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import { NgIf } from '@angular/common'; | ||
import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output, inject } from '@angular/core'; | ||
import { HotToastService } from '@ngneat/hot-toast'; | ||
import { ComponentNodeEvent, CustomNodeComponent, Edge, Node, VflowModule } from 'projects/ngx-vflow-lib/src/public-api'; | ||
|
||
@Component({ | ||
template: `<vflow [nodes]="nodes" [edges]="edges" (onComponentNodeEvent)="handleComponentEvent($event)" />`, | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
standalone: true, | ||
imports: [VflowModule] | ||
}) | ||
export class CustomComponentNodesDemoComponent { | ||
private toast = inject(HotToastService); | ||
|
||
public nodes: Node[] = [ | ||
{ | ||
id: '1', | ||
point: { x: 100, y: 100 }, | ||
type: RedSquareNodeComponent, | ||
data: { | ||
redSquareText: 'Red', | ||
} satisfies RedSquareData | ||
}, | ||
{ | ||
id: '2', | ||
point: { x: 250, y: 250 }, | ||
type: BlueSquareNodeComponent, | ||
data: { | ||
blueSquareText: 'Blue', | ||
} satisfies BlueSquareData | ||
}, | ||
] | ||
|
||
public edges: Edge[] = [ | ||
{ | ||
id: '1 -> 2', | ||
source: '1', | ||
target: '2' | ||
} | ||
] | ||
|
||
// Type-safe! | ||
handleComponentEvent(event: ComponentNodeEvent<[RedSquareNodeComponent, BlueSquareNodeComponent]>) { | ||
if (event.eventName === 'redSquareEvent') { | ||
this.toast.info(event.eventPayload) | ||
} | ||
|
||
if (event.eventName === 'blueSquareEvent') { | ||
this.toast.info(`${event.eventPayload.x + event.eventPayload.y}`) | ||
} | ||
} | ||
} | ||
|
||
// --- Description of red square component node | ||
|
||
interface RedSquareData { | ||
redSquareText: string | ||
} | ||
|
||
@Component({ | ||
template: ` | ||
<div class="red-square" (click)="onClick()"> | ||
{{ node.data?.redSquareText }} | ||
<handle type="source" position="right"/> | ||
</div> | ||
`, | ||
styles: [` | ||
.red-square { | ||
width: 100px; | ||
height: 100px; | ||
background-color: #DE3163; | ||
border-radius: 5px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
padding-left: 5px; | ||
padding-right: 5px; | ||
} | ||
`], | ||
standalone: true, | ||
imports: [VflowModule] | ||
}) | ||
export class RedSquareNodeComponent extends CustomNodeComponent<RedSquareData> { | ||
@Output() | ||
redSquareEvent = new EventEmitter<string>() | ||
|
||
onClick() { | ||
this.redSquareEvent.emit('Click from red square') | ||
} | ||
} | ||
|
||
// --- Description of blue square component node | ||
|
||
interface BlueSquareData { | ||
blueSquareText: string | ||
} | ||
|
||
@Component({ | ||
template: ` | ||
<div class="blue-square" (click)="onClick()"> | ||
{{ node.data?.blueSquareText }} | ||
<handle type="target" position="left"/> | ||
</div> | ||
`, | ||
styles: [` | ||
.blue-square { | ||
width: 100px; | ||
height: 100px; | ||
background-color: #0096FF; | ||
border-radius: 5px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
padding-left: 5px; | ||
padding-right: 5px; | ||
} | ||
`], | ||
standalone: true, | ||
imports: [VflowModule] | ||
}) | ||
export class BlueSquareNodeComponent extends CustomNodeComponent<BlueSquareData> { | ||
@Output() | ||
blueSquareEvent = new EventEmitter<{ x: number, y: number }>() | ||
|
||
onClick() { | ||
this.blueSquareEvent.emit({ x: 5, y: 5 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 67 additions & 3 deletions
70
projects/ngx-vflow-demo/src/app/categories/examples/pages/custom-nodes/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,79 @@ | ||
# {{ NgDocPage.title }} | ||
|
||
> **Warning** | ||
> Be careful with CSS rules applied to node content; custom nodes are implemented with SVG's `foreignObject` element, and Safari has issues with some CSS rules inside `foreignObject`. Therefore, please check this browser when applying complex styling. | ||
> Be careful with CSS rules applied to node content. Custom nodes are implemented with SVG's `foreignObject` element, and Safari has issues with some CSS rules inside `foreignObject`. Therefore, please check this browser when applying complex styling. | ||
This is where things became a lot more interesting. You can create custom nodes with HTML and CSS. | ||
This is where things become a lot more interesting. You can create custom nodes with HTML and CSS. | ||
|
||
Do the following steps to archieve this: | ||
## Template nodes | ||
|
||
You can create custom nodes with `ng-template` | ||
|
||
Follow these steps to achieve this: | ||
|
||
1. Set `type` of node to `html-template` | ||
2. Provide `ng-template` with `nodeHtml` selector inside `vflow` | ||
3. Write your HTML inside this template | ||
4. You can also pass any data with `data` field on node, and then get it inside `ng-template` | ||
|
||
{{ NgDocActions.demo("CustomNodesDemoComponent", { expanded: true }) }} | ||
|
||
## Component nodes | ||
|
||
Another approach is to render nodes from components. | ||
|
||
Its benefits: | ||
|
||
- type-safe node data access | ||
- good for complex flows with many different node types | ||
|
||
Its limitations | ||
|
||
- it's harder to manage events because such nodes are rendered dynamically | ||
|
||
How to create component node: | ||
|
||
1. Create a regular angular standalone component | ||
2. Extend with `CustomNodeComponent` (please see the reference of this base component to get an idea of what fields you could use in your custom component node), otherwise it won't work! | ||
3. Pass your data interface to generic of `CustomNodeComponent` to use in component. This `data` comes from `Node` definition | ||
4. Use your new component in `type` field of `Node`. Library will render your node for you | ||
|
||
{{ NgDocActions.demo("CustomComponentNodesDemoComponent", { expanded: true }) }} | ||
|
||
### Handling events | ||
|
||
> **Warning** | ||
> This is an experimental API | ||
There is a `(onComponentNodeEvent)` event on `VflowComponent`. Here is how it works: | ||
|
||
1. It accumulates every `EventEmitter` of every component node of your flow | ||
2. It emits on every emit of those emitters | ||
|
||
The shape of this accumulator-event contains following useful info: | ||
|
||
```ts | ||
export type AnyComponentNodeEvent = { | ||
nodeId: string // Id of node where event occurs | ||
eventName: string | ||
eventPayload: unknown | ||
} | ||
``` | ||
The Library also includes `ComponentNodeEvent` helper type to get type-safe event, where you just need to pass an array of your custom components in generic, and this type will infer proper types for `eventName` and `eventPayload`: | ||
```ts | ||
... | ||
|
||
handleComponentEvent(event: ComponentNodeEvent<[RedSquareNodeComponent, BlueSquareNodeComponent]>) { | ||
if (event.eventName === 'redSquareEvent') { | ||
console.log(event.eventPayload) | ||
} | ||
|
||
if (event.eventName === 'blueSquareEvent') { | ||
console.log(event.eventPayload.x + event.eventPayload.y) | ||
} | ||
} | ||
|
||
.. | ||
``` |
3 changes: 2 additions & 1 deletion
3
projects/ngx-vflow-demo/src/app/categories/examples/pages/custom-nodes/ng-doc.page.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 4 additions & 4 deletions
8
...ts/ngx-vflow-demo/src/app/categories/examples/pages/default-connection/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
# {{ NgDocPage.title }} | ||
|
||
Edges are not creating automatically. Basically, these steps needs to be performed to create new edge: | ||
Edges are not creating automatically. To create a new edge, follow these steps: | ||
|
||
1. Create handler to `(onConnect)` event | ||
2. This handler accepts `Connection` argument. `Connection` is like `Edge`, but it doesn't exists in flow, you need to "convert" it to new `Edge` | ||
3. `Edge[]` list updated with new edge that was created from `Connection` | ||
1. Create handler to the `(onConnect)` event | ||
2. This handler accepts a `Connection` argument. `Connection` is similar to an `Edge`, but it doesn't exists in the flow, you need to "convert" it into a new `Edge` | ||
3. Update the `Edge[]` list with the new edge that was created from the `Connection`. | ||
|
||
{{ NgDocActions.demo("DefaultConnectionDemoComponent", { expanded: true }) }} |
2 changes: 1 addition & 1 deletion
2
projects/ngx-vflow-demo/src/app/categories/examples/pages/default-edges/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# {{ NgDocPage.title }} | ||
|
||
You can link nodes by edges. All you need to do is to create another `Edge[]` array and pass to `vflow` component. Each edge contains id of `source` and `target` nodes, also edge must have it's own `id`. | ||
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`. | ||
|
||
{{ NgDocActions.demo("DefaultEdgesDemoComponent", { expanded: true }) }} |
2 changes: 1 addition & 1 deletion
2
projects/ngx-vflow-demo/src/app/categories/examples/pages/draggables/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# {{ NgDocPage.title }} | ||
|
||
On some nodes you can disable `draggable` behavior. | ||
You can disable `draggable` behavior on certain nodes. | ||
|
||
{{ NgDocActions.demo("DraggablesDemoComponent", { expanded: true }) }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
projects/ngx-vflow-demo/src/app/categories/examples/pages/labels/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.