react-diagrams icon indicating copy to clipboard operation
react-diagrams copied to clipboard

type error when registering default layer factories

Open m8765432 opened this issue 3 years ago • 2 comments

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

m8765432 avatar Apr 15 '21 12:04 m8765432

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

rob-myers avatar May 27 '21 15:05 rob-myers

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>
  );
}

rob-myers avatar May 27 '21 17:05 rob-myers