Closed alexeybahterwork closed 3 years ago
I face the same problem. When you using CustomNodeModel basen on demo example it dosent provide link method on PortModel as it have place for DefaultNodeModel:
I tried extending CustomNodeModel with DefaultNodeModel instead of NodeModel or couple of different things but I always end up with some errors.
How can we achieve linking custom nodes/portes ?
Ok, I managed to achieve this by copiying DefaultNode to my project and adjust to personal needs
@dr-azbest that would be the right way to do it since the idea is that the library can be extended with custom logic and components. @alexeybahterwork take a look at the source code of DefaultNode to see how this is done, the example demos also show this with the custom diamond node
Here's how I managed to do it
DiamondNodeModel.ts
based on NodeModel.ts
from the source code:
import {
DefaultNodeModelOptions,
NodeModel,
NodeModelGenerics,
PortModelAlignment,
} from "@projectstorm/react-diagrams";
import { DiamondPortModel } from "./DiamondPortModel";
interface IDiamondNodeModel {
title?: string;
}
export interface DiamondNodeModelGenerics extends NodeModelGenerics {
PORT: DiamondPortModel;
OPTIONS: DefaultNodeModelOptions;
}
export class DiamondNodeModel extends NodeModel<DiamondNodeModelGenerics> {
protected ports: { [s: string]: DiamondPortModel };
title: string;
constructor(params?: IDiamondNodeModel) {
super({
type: "diamond",
});
this.title = params?.title || "";
this.ports = {};
this.addPort(new DiamondPortModel(PortModelAlignment.TOP));
this.addPort(new DiamondPortModel(PortModelAlignment.LEFT));
this.addPort(new DiamondPortModel(PortModelAlignment.BOTTOM));
this.addPort(new DiamondPortModel(PortModelAlignment.RIGHT));
}
getPort(name: string): DiamondPortModel | null {
return this.ports[name];
}
addPort(port: DiamondPortModel): DiamondPortModel {
port.setParent(this);
this.ports[port.getName()] = port;
return port;
}
}
DiamondPortModel.ts
import {
LinkModel,
PortModel,
DefaultLinkModel,
PortModelAlignment,
} from "@projectstorm/react-diagrams";
import { AbstractModelFactory } from "@projectstorm/react-canvas-core";
export class DiamondPortModel extends PortModel {
constructor(alignment: PortModelAlignment) {
super({
type: "diamond",
name: alignment,
alignment,
});
}
link<T extends LinkModel>(
port: PortModel,
factory?: AbstractModelFactory<T>,
): T {
let link = this.createLinkModel(factory);
link.setSourcePort(this);
link.setTargetPort(port);
return link as T;
}
createLinkModel(factory?: AbstractModelFactory<LinkModel>): LinkModel {
let link = super.createLinkModel();
if (!link && factory) {
return factory.generateModel({});
}
return link || new DefaultLinkModel();
}
}
And a little tweak to the DiamondNodeFactory.tsx
import { DiamondNodeWidget } from "./DiamondNodeWidget";
import { DiamondNodeModel } from "./DiamondNodeModel";
import * as React from "react";
import { AbstractReactFactory } from "@projectstorm/react-canvas-core";
import { DiagramEngine } from "@projectstorm/react-diagrams-core";
interface IDiamondNodeFactory {
size?: number;
}
export class DiamondNodeFactory extends AbstractReactFactory<
DiamondNodeModel,
DiagramEngine
> {
size: number = 50;
constructor(params?: IDiamondNodeFactory) {
super("diamond");
this.size = params?.size ?? this.size;
}
generateReactWidget(event): JSX.Element {
return (
<DiamondNodeWidget
engine={this.engine}
size={this.size}
node={event.model}
/>
);
}
generateModel() {
return new DiamondNodeModel();
}
}
In my React component:
import { CanvasWidget } from "@projectstorm/react-canvas-core";
import createEngine, {
DefaultNodeModel,
DiagramEngine,
DiagramModel,
PortModelAlignment,
} from "@projectstorm/react-diagrams";
import * as React from "react";
import { useEffect, useState } from "react";
import { DiamondNodeFactory } from "./DiamondNodeFactory";
import { DiamondNodeModel } from "./DiamondNodeModel";
import { DiamondPortModel } from "./DiamondPortModel";
import { SimplePortFactory } from "./SimplePortFactory";
const Diagram= () => {
const [engine, setEngine] = useState<DiagramEngine>();
useEffect(() => {
const engine = createEngine();
// * Register custom factories
engine
.getPortFactories()
.registerFactory(
new SimplePortFactory(
"diamond",
() => new DiamondPortModel(PortModelAlignment.LEFT),
),
);
engine.getNodeFactories().registerFactory(new DiamondNodeFactory());
//2) setup the diagram model
const model = new DiagramModel();
const X = 650;
// * Nodes
const customNode = new DiamondNodeModel({ title: "AYYE" });
mainNode.setPosition(X, 100);
const customNode2 = new DiamondNodeModel({ title: "AYYE22" });
mainNode2.setPosition(X - 150, 500);
// * Ports
const leftMainNodePort = mainNode.getPort(PortModelAlignment.LEFT);
const rightMainNodePort = mainNode.getPort(PortModelAlignment.RIGHT);
// * Links
const leftLinkCustomNode2 = leftMainNodePort.link(
mainNode2.getPort(PortModelAlignment.LEFT),
);
const leftLinkCustomNode2 = rightMainNodePort.link(
mainNode2.getPort(PortModelAlignment.LEFT),
);
// * Add models to the root graph
model.addAll(
customNode ,
customNode2 ,
leftMainNodePort ,
rightMainNodePort ,
leftLinkCustomNode2 ,
leftLinkCustomNode2
);
// * Lock dragging the nodes
model.setLocked(true);
//5) load model into engine
engine.setModel(model);
setEngine(engine);
}, []);
if (!engine) return null;
//6) render the diagram!
return <CanvasWidget className="w-full h-full bg-gray-600" engine={engine} />;
};
export default Diagram;
Authors, thank you for a wonderful library! How can I connect custom nodes? I created a custom node based on the example (https://github.com/projectstorm/react-diagrams/tree/master/packages/diagrams-demo-gallery/demos/demo-custom-node1). But in this example, the custom node is connected to the default node. Maybe someone has examples of connecting a custom node with a custom node? I'm just beginning to understand this library. Thanks!