react-diagrams
react-diagrams copied to clipboard
type error when registering default layer factories
Problem
Trying to build the at the bottom provided basic example on a clean new react-scripts project leads to following type error:
argument of type 'NodeLayerFactory' is not assignable to parameter of type 'AbstractReactFactory<LayerModel<LayerModelGenerics>, CanvasEngine<CanvasEngineListener, CanvasModel<CanvasModelGenerics>>>'.
Types of property 'generateReactWidget' are incompatible.
Type '(event: GenerateWidgetEvent<NodeLayerModel<NodeLayerModelGenerics>>) => Element' is not assignable to type '(event: GenerateWidgetEvent<LayerModel<LayerModelGenerics>>) => Element'.
Types of parameters 'event' and 'event' are incompatible.
Type 'GenerateWidgetEvent<LayerModel<LayerModelGenerics>>' is not assignable to type 'GenerateWidgetEvent<NodeLayerModel<NodeLayerModelGenerics>>'.
Property 'getNodes' is missing in type 'LayerModel<LayerModelGenerics>' but required in type 'NodeLayerModel<NodeLayerModelGenerics>'.
91 engine.getLayerFactories().registerFactory(new NodeLayerFactory());
~~~~~~~~~~~~~~~~~~~~~~
../../node_modules/@projectstorm/react-diagrams-core/dist/@types/entities/node-layer/NodeLayerModel.d.ts:12:5
12 getNodes(): {
~~~~~~~~
'getNodes' is declared here.
Basic Example
import './App.css';
import createEngine, {
DefaultDiagramState,
DefaultLabelFactory,
DefaultLinkFactory,
DefaultLinkModel,
DefaultNodeFactory,
DefaultNodeModel,
DefaultPortFactory,
DiagramEngine,
DiagramModel,
LinkLayerFactory,
NodeLayerFactory,
PathFindingLinkFactory} from '@projectstorm/react-diagrams';
import {
CanvasWidget, SelectionBoxLayerFactory} from '@projectstorm/react-canvas-core';
export default function App() {
// create engine
const engine = new DiagramEngine();
// register model factories
engine.getLayerFactories().registerFactory(new NodeLayerFactory());
engine.getLayerFactories().registerFactory(new LinkLayerFactory());
engine.getLayerFactories().registerFactory(new SelectionBoxLayerFactory());
engine.getLabelFactories().registerFactory(new DefaultLabelFactory());
engine.getNodeFactories().registerFactory(new DefaultNodeFactory()); // i cant figure out why
engine.getLinkFactories().registerFactory(new DefaultLinkFactory());
engine.getLinkFactories().registerFactory(new PathFindingLinkFactory());
engine.getPortFactories().registerFactory(new DefaultPortFactory());
// register the default interaction behaviours
engine.getStateMachine().pushState(new DefaultDiagramState());
// end create engine
// node 1
const node1 = new DefaultNodeModel({
name: 'Node 1',
color: 'rgb(0,192,255)',
});
node1.setPosition(100, 100);
let port1 = node1.addOutPort('Out');
// node 2
const node2 = new DefaultNodeModel({
name: 'Node 1',
color: 'rgb(0,192,255)',
});
node2.setPosition(100, 100);
let port2 = node2.addOutPort('Out');
// link them and add a label to the link
const link = port1.link<DefaultLinkModel>(port2);
link.addLabel('Hello World!');
const model = new DiagramModel();
model.addAll(node1, node2, link);
engine.setModel(model);
return (
<CanvasWidget className="srd-demo-canvas" engine={engine} />
);
}
The "create engine part" is just copy paste from this:
https://github.com/projectstorm/react-diagrams/blob/master/packages/react-diagrams/src/index.ts
There seems to be a typing issue in your particular case. I'd just use:
engine.getLayerFactories().registerFactory(new NodeLayerFactory() as any);
engine.getLayerFactories().registerFactory(new LinkLayerFactory() as any);
The types do work when creating custom factories.
class CustomNodeModel extends NodeModel { ... }
class CustomNodeFactory extends AbstractReactFactory<CustomNodeModel, DiagramEngine> { ... }
engine.getLayerFactories().registerFactory(new CustomNodeFactory); // works
Here's a rewrite of the demo project, avoiding the dependencies dagre
, pathfinding
and others.
You'll also need to include the css.
import { useEffect, useState } from "react";
import { AbstractReactFactory, GenerateWidgetEvent, CanvasEngineOptions, SelectionBoxLayerFactory, CanvasWidget } from '@projectstorm/react-canvas-core';
import { DiagramModel, DiagramEngine, NodeModel, PortWidget, NodeLayerFactory, LinkLayerFactory, DefaultDiagramState } from '@projectstorm/react-diagrams-core';
import { DefaultPortModel, DefaultLabelFactory, DefaultLinkFactory, DefaultNodeFactory, DefaultLinkModel, DefaultPortFactory } from '@projectstorm/react-diagrams-defaults';
export default function ReactDiagram() {
const [engine, setEngine] = useState<DiagramEngine>();
useEffect(() => {
const engine = createEngine();
engine.getNodeFactories().registerFactory(new CustomNodeFactory);
const node1 = new CustomNodeModel({ color: 'rgb(192,255,0)' });
node1.setPosition(50, 50);
const node2 = new CustomNodeModel({ color: 'rgb(0,192,255)' });
node2.setPosition(200, 50);
const link1 = new DefaultLinkModel;
link1.setSourcePort(node1.getPort('out')!)
link1.setTargetPort(node2.getPort('in')!)
const model = new DiagramModel;
model.addAll(node1, node2, link1);
engine.setModel(model);
setEngine(engine);
}, []);
return engine
? <CanvasWidget className="diagram-container" engine={engine} />
: null;
}
function createEngine(options: CanvasEngineOptions = {}): DiagramEngine {
const engine = new DiagramEngine(options);
// register model factories
engine.getLayerFactories().registerFactory(new NodeLayerFactory() as any);
engine.getLayerFactories().registerFactory(new LinkLayerFactory() as any);
engine.getLayerFactories().registerFactory(new SelectionBoxLayerFactory());
engine.getLabelFactories().registerFactory(new DefaultLabelFactory());
engine.getNodeFactories().registerFactory(new DefaultNodeFactory()); // i cant figure out why
engine.getLinkFactories().registerFactory(new DefaultLinkFactory());
// engine.getLinkFactories().registerFactory(new PathFindingLinkFactory());
engine.getPortFactories().registerFactory(new DefaultPortFactory());
// register the default interaction behaviours
engine.getStateMachine().pushState(new DefaultDiagramState());
return engine;
};
class CustomNodeFactory extends AbstractReactFactory<CustomNodeModel, DiagramEngine> {
constructor() {
super('custom-node');
}
generateModel() {
return new CustomNodeModel({});
}
generateReactWidget(event: GenerateWidgetEvent<CustomNodeModel>) {
return <CustomNodeWidget engine={this.engine} node={event.model} />;
}
}
class CustomNodeModel extends NodeModel {
color: string;
constructor(opts: { color?: string }) {
super({ ...opts, type: 'custom-node' });
this.color = opts.color || 'red';
this.addPort(new DefaultPortModel({ in: true, name: 'in' }));
this.addPort(new DefaultPortModel({ in: false, name: 'out' }));
}
serialize() {
return { ...super.serialize(), color: this.color };
}
deserialize(event: any): void {
super.deserialize(event);
this.color = event.data.color;
}
}
function CustomNodeWidget(props: {
engine: DiagramEngine;
node: CustomNodeModel;
}) {
return (
<div className="custom-node">
<PortWidget engine={props.engine} port={props.node.getPort('in')!}>
<div className="circle-port" />
</PortWidget>
<PortWidget engine={props.engine} port={props.node.getPort('out')!}>
<div className="circle-port" />
</PortWidget>
<div className="custom-node-color" style={{ backgroundColor: props.node.color }} />
</div>
);
}