projectstorm / react-diagrams

a super simple, no-nonsense diagramming library written in react that just works
https://projectstorm.cloud/react-diagrams
MIT License
8.72k stars 1.18k forks source link

Want to add a port to the full node #535

Open M2Costa opened 4 years ago

M2Costa commented 4 years ago

Is it possible to make the full node a port? If you make a port in the same shape and size of the node it turns impossible to drag, so we thought in a solution that is when the user drops a link in a node we search for the closer port and make the link goes to it, but I don't know if its the best solution or if there are a better way to implement what we need. Also, we have some doubts about the node files organization, once them don't follow the default react organization with just a index and a style in a folder, do you think that this organization is fine or separating the 3 files in different folders will be better?

Thanks!

renato-bohler commented 4 years ago

There's no 'default' way of implementing a 'full node port' kind of thing.

The solution you described seems to be the best way to achieve this at the moment, but I never tested this. Theoretically, you could have your own implementation of CreateLinkState and DragDiagramItemsState, so that when the user release any links on top of nodes, it connects the link to the node port.

Something like this, maybe:

export class CustomCreateLinkState extends State<DiagramEngine> {
  sourcePort: PortModel;
  link: LinkModel;

  constructor() {
    super({ name: 'create-new-link' });

    this.registerAction(
      new Action({
        type: InputType.MOUSE_UP,
        fire: (actionEvent: ActionEvent<MouseEvent>) => {
          const element = this.engine.getActionEventBus().getModelForEvent(actionEvent);
          // (...)

          if (element instanceof PortModel && !this.sourcePort) {
            // (...)
          } else if (element instanceof PortModel && this.sourcePort && element != this.sourcePort) {
            // (...)
          /**
           * 
           * This `else if` block is what I've changed
           * 
           */
          } else if (element instanceof NodeModel) {
            const nodePort = element.getPorts()[0]; // assuming nodes have only one port

            if (this.sourcePort.canLinkToPort(nodePort)) {
              this.link.setTargetPort(nodePort);
              nodePort.reportPosition();
              this.clearState();
              this.eject();
            }
          } else if (element === this.link.getLastPoint()) {
            // (...)
          }

            // (...)
        }
      })
    );

    // (...)
}

Also, this is duplicate of #249.

M2Costa commented 4 years ago

Thanks for the answer, I guess that this can solve the problem, we will just need to adapt it to a n-port solution. And sorry, I didn't saw the previous issue. About the files organization, what do you think?

renato-bohler commented 4 years ago

Adapting it is easy, because every port on the node is accessible by calling element.getPorts() and port position is accessible by calling port.getPosition(). So you'll just need to calculate which port is closest to mouse release event and choose it.

File organization will depend on your use case. Personally, I'm structuring it like this:

root
|_ Node
    |_ NodeModel.js
    |_ NodeWidget.jsx
    |_ NodeFactory.js
|_ AnotherNode
    |_ AnotherNodeModel.js
    |_ AnotherNodeWidget.jsx
    |_ AnotherNodeFactory.js
|_ Link
    |_ LinkModel.js
    |_ LinkWidget.jsx
    |_ LinkFactory.js
index.js

You can check out how this looks on the project I'm developing.