Show/Hide ports while creating link


#1

We are developing highly interactive canvas using GoJS. Application has number of different types of nodes that the user can place on the canvas from a pallet. Each node can have one to many input or output (or both) port types depending on the node, therefore we are placing visual ports on either right or left side of nodes. These diagrams can get very complicated and we like to be able to hide the ports until user is ready to connect the nodes. Using simple MouseEnter and MouseLeave events over a node can show/hide the ports. However, when user starts drawing a link we want to show the ports on node which they want to connect to. The two events don’t fire when user is drawing a link. Is there a way to find out which node the mouse is getting close to or is over while drawing a link, so we be able to show that node’s ports?

Capture


#2

First, I hope you normally have the ports hidden by setting their opacity to zero, rather than setting their visible to false. If they are still visible but not seen due to transparency, they can still participate in linking operations by the LinkingTool or the RelinkingTool.

Second, you can customize each of the linking tools by implementing a LinkingBaseTool.portTargeted function. I’ll assume your ports normally have opacity == 0.0. Something like:

        $(go.Shape, "Circle", . . .,
          { opacity: 0.0, portId: "...", toLinkable: true, . . . }),

Declare a global variable to hold the previously targeted port; I’ll call it lastPort. Then you can define the portTargeted function when you define the diagram, such as:

var lastPort = null;
$(go.Diagram, . . .,
        { . . .,
          "linkingTool.portTargeted": function(node, port, tempnode, tempport, toend) {
            if (lastPort) lastPort.opacity = 0.0;
            lastPort = port;
            if (port) port.opacity = 1.0;
          }
        });

#3

Thanks Walter, that worked. One more issue I have. As you can see in the image, the Node label and ports are external to the node image. The mouseEnter and mouseLeave only fire when the mouse is on any of these parts, however when the mouse is over an empty area between label or the ports and the node shape they don’t fire. This is how the node template is defined. Is there a way to fix the template to fire the event on the entire node and it’s parts including white spaces?

  $(go.Node, 'Vertical',
    new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), {
      isShadowed: false,
      layerName: 'Nodes',
      selectionObjectName: 'nodeRectangle',
      selectionAdornmentTemplate:
        $(go.Adornment, 'Auto',
          $(go.Shape, 'Circle',
            {fill: null, stroke: 'dodgerblue', strokeWidth: 3, width: 40, height: 40}),
          $(go.Placeholder)
        ),
      selectionChanged: (part) => {
        part.layerName = part.isSelected ? 'Foreground' : 'Nodes';
        if (part instanceof go.Node) {
          this.showPorts(part, part.isSelected ? true : false);
        }
      },
      linkConnected: (node, link, port) => {
        this.showPorts(node, true);
      },
      linkDisconnected: (node, link, port) => {
        this.showPorts(node, false);
      },
      mouseEnter: (e: go.InputEvent, node: go.GraphObject) => {
        if (node instanceof go.Node) {
          this.showPorts(node, true);
        }
      },
      mouseLeave: (e: go.InputEvent, node: go.GraphObject) => {
        if (node instanceof go.Node && !node.isSelected) {
          this.showPorts(node, false);
        }
      },
    },
    $(go.Panel, 'Horizontal',
      // input ports
      $(go.Panel, 'Vertical', {
          alignment: go.Spot.Left,
          alignmentFocus: new go.Spot(0, 0.5, 8, 0)
        },
        new go.Binding('itemArray', 'name', (n) => this.getInputPorts(items, n)), {
          itemTemplate:
            $(go.Panel, 'Spot',
              $(go.Shape, 'Ellipse', {
                  toSpot: go.Spot.LeftSide,
                  toLinkable: true,
                  fromLinkable: false,
                  alignment: go.Spot.TopLeft,
                  stroke: '#000000',
                  desiredSize: this.portSize,
                  margin: new go.Margin(2),
                  cursor: 'pointer',
                  toolTip: this.sharedToolTipPort,
                  opacity: 0.0
                },
                new go.Binding('portId', 'portId'),
                new go.Binding('fill', 'portColor'),
                new go.Binding('toMaxLinks', 'maxLinks'),
              )
            )
        }
      ),  // end of input ports
      $(go.Panel, 'Auto',
        $(go.Shape, 'Circle', new go.Binding('fill', 'name', (n) => this.getColor(items, n)),
          new go.Binding('stroke', 'name', (n) => this.getColor(items, n)), {
            name: 'nodeRectangle',
            width: 40,
            height: 40
          }),
        $(go.Picture, new go.Binding('source', 'name', (n) => this.getIcon(items, n).setHeight(24).setWidth(24).toDataURI())),
      ),
      // output ports
      $(go.Panel, 'Vertical', {
          alignment: go.Spot.RightCenter,
          alignmentFocus: new go.Spot(0, 0.5, 8, 0)
        },
        new go.Binding('itemArray', 'name', (n) => this.getOutputPorts(items, n)), {
          itemTemplate:
            $(go.Panel, 'Spot',
              $(go.Shape, 'Ellipse', {
                  fromSpot: go.Spot.RightSide,
                  fromLinkable: true,
                  toLinkable: false,
                  alignment: go.Spot.TopRight,
                  desiredSize: this.portSize,
                  margin: new go.Margin(2),
                  stroke: '#000000',
                  strokeWidth: 1,
                  cursor: 'pointer',
                  toolTip: this.sharedToolTipPort,
                  opacity: 0.0
                },
                new go.Binding('portId', 'portId'),
                new go.Binding('fill', 'portColor'),
                new go.Binding('fromMaxLinks', 'maxLinks')
              )
            )
        }
      ),  // end output ports
    ),
    $(go.TextBlock, {
        name: 'stageLabel',
        margin: new go.Margin(8, 0, 0, 0),
        width: 100,
        wrap: go.TextBlock.WrapFit,
        shadowVisible: false,
        textAlign: 'center'
      },
      new go.Binding('text', 'label')
    )
  )

#4

I’m not sure exactly what you want, but I would first try setting Node.background to “transparent”.


#5

Worked. Thanks.