How to create custom adornment with the help of dynamic data from an API

How to create Adornments depending on the category and name of the Node. If categories is an array of objects which has another set of array of objects this array has the name of the node along with and an array called adornments. Now using the adornments Array I want to create an adornment for each array value.

palleteNodes = [{'type': 'trigger', nodes: [ { name: 'Activity One', adornments: ['Y','N'], }, { name: 'Activity Two', adornments: ['A','B','C','D'], }, ] }, {'type': 'actions', nodes: [ { name: 'Activity 3', adornments: ['A','S','O'], }, { name: 'Activity 4', adornments: ['S , 'D', 'N, ], } ] },];

I am using the above variable to create palette nodes which are without adornment. After drag and drop on canvas, I want to see the node with adornments (see the key adornments from the above array variable). How can I achieve this?

For example please find this picture

when the Node is not selected

withoutHover

And when the Node is selected
onhover

I do not understand what you want. Could you please sketch or provide a small screenshot showing what you want?

Actually, please show two sketches/screenshots: one when the Node is not selected, and one where the Node is selected.

@walter I have updated the question with images, I want to achieve the selection state as shared in the above image on Node hover state. Also It should get converted into link with the adjacent adornment as link label (For eg. If I try to draw a link from adornment Y of activity one to activity two, It should make Link between these two node with the link label as Y).

So A, B, C, D, and E are not Nodes? And the dashed lines from “Activity Two” to those items are not Links? Hmmm, that would be so easy to implement if they actually were Nodes and Links…

I assume you don’t have any problem defining the “Activity” nodes, and the “trash can” button in the Adornment. However it still isn’t clear to me whether that is a “Selection” Adornment or if it is just an Adornment that you programmatically show and hide based on mouse events. Either way can work, but they really are different. If the user executes the SelectAll command (Ctrl-A), do you expect all “Activity” nodes to get those Adornments? Or is there at most one Adornment visible at a time? Or some other behavior?

For selection adornments, please read https://gojs.net/latest/intro/selection.html and see this sample: https://gojs.net/latest/samples/grafcet.html.

For hover adornments, please see this sample: https://gojs.net/latest/samples/hoverButtons.html.

Although in both cases you might not want to use a button-like object – that interaction requirement isn’t clear to me from your description.

@walter
A, B, C, D, E are adornments. Yes, they are not links. They are all Adornments, that can be shown or hidden programmatically on mouse events only. So on mouse enter event, I want all the adornments to be visible related to that particular node.

I checked this example, But I want to attach adornment dynamically depending on the Json. according to the example these are few predefined adornments depending on their type. for example, I have a function called makeNodesAdornments(‘pass adornments related to a particular node’)

makeNodesAdornments(adornments) { const adornmentArrayList = adornments.map(adornment => { const adornmentNode = GJS(go.Panel, 'Auto' , GJS(go.Shape , 'RoundedRectangle', {strokeWidth: 1 , fill : '#fff'}, new go.Binding('stroke', 'stroke')), GJS(go.TextBlock , {margin : 5, width : 100, height: 25, font: 'bold 16pt', editable: false, text: 'verticalAlignment: Center', verticalAlignment: go.Spot.Center}, new go.Binding('text', adornment)) ); return adornmentNode; }); return adornmentArrayList; }

Basically I am trying to achieve this with the adornments and Node please view the Image

Thanks

You haven’t stated whether you want a selection Adornment template (that depends on the value of the data.adornments Array) or a single hover Adornment (that also depends on the data.adornments Array).

Wait – I missed in your most recent screenshot that it appears that those “A” and “D” are supposed to be permanently visible. You have a link going from “Activity One” “D” to “Activity Three”.

I had assumed from your description that the Adornment would be temporary and would go away after the user had (somehow) drawn a link to another node, or not. But that seems not to be the case.

And with the “Activity One” (argh – there are two of them) on the upper left corner, there are two “A” appendages.

Oh, I see – those extras are actually part of the Links. I bet they are deleted when the link is deleted.

So there is only one Adornment visible at a time, customized to the Node that the mouse is over? If so, that means you are talking about a hover adornment, not a selection adornment.

I want to make adornments(‘A’,‘B’,‘C’,‘D’) visible on mousehover event of the Node(eg ‘Activity One’). And I want to copy the data of adornment into link label after the selection a particular adornment (eg ‘A’) in the above figure.

  1. You can see the mouse hover state on activity One
  2. The process of drawing from activity one to activity three using the adornment A which will get converted into link label.
  3. On activity three you can see the adornment converted into link label Y for node three.

Please don’t be confused by this sample: https://gojs.net/latest/samples/adornmentButtons.html
I know you want hover adornments, not selection adornment (template). But once the Adornment is visible, how do you want it to behave?

Did you want linking behavior like the third button (i.e. second from the right) of the selection adornment buttons? In that case the user can either click the button or start dragging from the button to start the LinkingTool from that node.

OK, I’ve made some assumptions about what you want. Here’s all of the code – you can adapt it for your own requirements. I didn’t bother doing much in the way of styling, since that’s something you can do easily.

  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
          {
            "undoManager.isEnabled": true,
            "linkingTool.isEnabled": false,  // invoked explicitly by drawLink function, below
            "linkingTool.direction": go.LinkingTool.ForwardsOnly  // only draw "from" towards "to"
          });

    myDiagram.nodeTemplate =
      $(go.Node, "Spot",
        {
          // show the Adornment when a mouseHover event occurs
          mouseHover: function(e, obj) {
            var node = obj.part;
            theHoverAdornment.adornedObject = node;
            node.addAdornment("MouseHover", theHoverAdornment);
          }
        },
        $(go.Panel, "Auto",
          $(go.Shape,
            { fill: "white", portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" },
            new go.Binding("fill", "color")),
          $(go.TextBlock,
            { margin: 8, editable: true },
            new go.Binding("text").makeTwoWay())
        ),
        $(go.Shape, "Circle",
          { alignment: go.Spot.Right, width: 5, height: 5, name: "AD" })
      );

    var ItemHeight = 20;
    var ItemSpacing = 10;
    var PathWidth = 30;

    function computePathPosition(i, shape) {
      var numads = shape.part.data.adornments.length;
      var totalheight = ItemHeight * numads + ItemSpacing * (numads-1);
      var fy = totalheight/2;
      var ty = i * (ItemHeight+ItemSpacing) + ItemHeight/2;

      var sy = (ty > fy) ? 0 : (fy-ty);
      var ey = (ty > fy) ? (ty-fy) : 0;
      var fig = new go.PathFigure(0, sy);
      fig.add(new go.PathSegment(go.PathSegment.Line, PathWidth/2, sy));
      fig.add(new go.PathSegment(go.PathSegment.Line, PathWidth/2, ey));
      fig.add(new go.PathSegment(go.PathSegment.Line, PathWidth, ey));
      shape.geometry = new go.Geometry().add(fig);

      return new go.Point(0, Math.min(fy, ty));
    }

    function computeBodyPosition(i) {
      return new go.Point(PathWidth, i * (ItemHeight+ItemSpacing));
    }

    var theHoverAdornment =
      $(go.Adornment, "Spot",
        {
          background: "transparent",
          // hide the Adornment when the mouse leaves it
          mouseLeave: function(e, obj) {
            obj.part.adornedPart.removeAdornment("MouseHover");
          }
        },
        $(go.Placeholder),
        $(go.Panel, "Position",
          new go.Binding("itemArray", "adornments"),
          {
            name: "ITEMS",
            alignment: go.Spot.Right,
            alignmentFocus: go.Spot.Left,
            itemTemplate:
              $(go.Panel, "Position",
                // the pretend link to one of the "adornments"
                $(go.Shape,
                  { name: "PATH", fill: null, strokeDashArray: [4, 3] },
                  new go.Binding("position", "itemIndex", computePathPosition).ofObject()),
                // the pretend node for one of the "adornments"
                $(go.Panel, "Spot",
                  new go.Binding("position", "itemIndex", computeBodyPosition).ofObject(),
                  { // drawLink is defined below, to support interactively drawing new links
                    isActionable: true,
                    click: drawLink,  // click on Button and then click on target node
                    actionMove: drawLink  // drag from Button to the target node
                  },
                  $(go.Panel, "Auto",
                    { width: 70, height: ItemHeight },
                    $(go.Shape, "RoundedRectangle",
                      { fill: "white", stroke: "purple" }),
                    $(go.TextBlock,
                      new go.Binding("text", ""))
                  ),
                  $(go.Shape, "Circle",
                    { alignment: go.Spot.Left, width: 5, height: 5, name: "AD" })
                )
              )
          }
        )
      );

    function drawLink(e, button) {
      var node = button.part.adornedPart;
      var tool = e.diagram.toolManager.linkingTool;
      tool.archetypeLinkData.text = button.panel.data;
      tool.startObject = node.port;
      e.diagram.currentTool = tool;
      tool.doActivate();
    }

    myDiagram.linkTemplate =
      $(go.Link,
        { routing: go.Link.Orthogonal, corner: 10 },
        $(go.Shape),
        $(go.Shape, { toArrow: "OpenTriangle" }),
        $(go.Panel, "Auto",
          { width: 70, height: ItemHeight },
          $(go.Shape, "RoundedRectangle",
            { fill: "white", stroke: "purple" }),
          $(go.TextBlock,
            new go.Binding("text"))
        ),
      );

    myDiagram.model = new go.GraphLinksModel(
    [
      { key: 1, text: "Alpha", color: "lightblue", adornments: ["D"] },
      { key: 2, text: "Beta", color: "orange", adornments: ["A", "B", "C"] },
      { key: 3, text: "Gamma", color: "lightgreen", adornments: ["A", "B"] },
      { key: 4, text: "Delta", color: "pink", adornments: ["A", "B", "C", "D", "E"] }
    ],
    [
      { from: 1, to: 2, text: "A" },
      { from: 1, to: 3, text: "A" },
      { from: 3, to: 4, text: "C" }
    ]);
  }

Here’s what it looks like after I hovered over the “Delta” node, clicked on one of the letter “E”, and then moved the mouse up and to the right:
image

After drawing the link from “Delta” to “Beta”:
image

You will want to read:
https://gojs.net/latest/intro/linkLabels.html
https://gojs.net/latest/intro/tools.html#LinkingToolAndRelinkingTool

1 Like

Thank you so much that was a great help! But I will be asking more question in the future.

Hi, @walter
I need your help, you can consider the above example, How can I add box-shadow on a node on its mousehover event. Is it possible?

You could use https://gojs.net/latest/api/symbols/GraphObject.html#mouseHover, but you probably want to use https://gojs.net/latest/api/symbols/GraphObject.html#mouseEnter and https://gojs.net/latest/api/symbols/GraphObject.html#mouseLeave instead.

Also: https://gojs.net/latest/api/symbols/Part.html#isShadowed, and the various “shadow…” properties.

Please search the forum and the web for more information and examples. https://gojs.net/latest/intro/highlighting.html

1 Like

Thank you!

Is it possible to give zOrder to different panels of a particular node? I am trying to do that but whatever styles I have applied by GoJs they are not visible.

The relative z-ordering of GraphObjects in a Panel is determined entirely by their order in the Panel.elements collection. https://gojs.net/latest/api/symbols/Panel.html#elements

1 Like

@walter Thanks for the Solution. I am still working on it.

@walter Thanks for the great support!
I have a doubt. Considering the above example, All the nodes and its respective adornments (adornments should inherit the colour of its node only) should be different in colours. I am able to maintain the different colour for node but not for adornments. So, for example - Suppose node A is blue in colour then all the adornments for that node should be in blue colour. But in my example, the adornments are taking the colour of the latest node irrespective of whether it belongs to it or not.

Please help

Have you added code to change the colors of the objects that you are showing?

Yes I am binding it with node’s stroke like this
GJS(go.Panel, 'Auto', { width: 100, height: itemHeight }, GJS(go.Shape, 'RoundedRectangle', { fill: 'white'}, new go.Binding('stroke', '')), GJS(go.TextBlock, { font: '7pt sans-serif', textAlign: 'center', }, new go.Binding('text', '')) ),