Default vs custom adornment

Hi,

Previously I had a common selection adornment for all my nodes in my diagram. It was as given below:

let selad = new go.Adornment();
    selad.type = go.Panel.Auto;
    let seladhandle = new go.Shape();
    seladhandle.figure = 'RoundedRectangle';
    seladhandle.fill = null;
    seladhandle.stroke = '#00DFCC';
    seladhandle.strokeWidth = 3;
    selad.add(seladhandle);
    let selplace = new go.Placeholder();
    selad.add(selplace);
    diagram.nodeSelectionAdornmentTemplate = selad;
    diagram.groupSelectionAdornmentTemplate = selad;

On my nodes, I have a menu for Delete and Duplicate buttons. Duplicate functionality is achieved by copySelection() and pasteSelection().

Now, I changed the adornment to be specific to the different types of nodes in my application.

Now the adornment is a part of the node structure as follows:

$(go.Node, 'Table',

            { linkConnected: this.updatePort, linkDisconnected: this.updatePort,
                selectionObjectName: 'SHAPE',
                selectionAdornmentTemplate:
                $(go.Adornment, "Auto",
                    $(go.Shape,
                    {   fill: null, 
                        geometryString: 'F M30.36.5H196c9.113,0,17.621-2.612,16.5,16.5V78.042c0,.276,3.145,26.137-12.838,26.458H30.36C-6.553,93.241-8.643,41.97,6.227,17,13.681,4.482,30.36.5,30.36.5Z',                            
                        stroke: "#162789", 
                        strokeWidth: 4 },
                    ),
                    $(go.Placeholder)
                )  // end Adornment
            },

My duplicate functionality is currently not working as the node is not getting “selected” when i click on the button. Due to which copySelection is undefined.

Is this because of the difference in handling the selection adornment? How can this be resolved?

I do not understand your situation. Are you talking about a context menu and calling CommandHandler.copySelection? If you have a reference to your Diagram, getting its Diagram.commandHandler will certainly not be undefined, so you can definitely call that command method.

But maybe you are asking about having the Node selected when the user performs a context-click on the Node. Normally the Node does become selected just before the context menu is shown. Changing the Selection Adornment will have no effect on that.

For some examples, see the Basic sample, https://gojs.net/latest/samples/basic.html, and please read https://gojs.net/latest/intro/contextmenus.html.

It is basically a context menu, but triggered on a left click. Here’s the implementation.

$("ContextMenuButton", 
                        {
                            row: 0, column: 2,
                            width: 25,
                            height: 25,
                            'ButtonBorder.fill': 'white', 
                            'ButtonBorder.stroke': 'white',
                            '_buttonStrokeNormal': 'white',
                            '_buttonFillOver': '#d1cff2',
                            '_buttonStrokeOver': '#d1cff2',
                            contextMenu:     // define a context menu for each node
                                $('ContextMenu',  // that has one button
                                    $('ContextMenuButton',
                                        { 'ButtonBorder.fill': 'white', '_buttonFillOver': 'lightgray' },
                                        $(go.TextBlock, 'Delete',
                                        { margin: 12 }
                                        ),
                                        { click: (e, obj) => { this.deleteComponent(obj); } }),
                                    $('ContextMenuButton',
                                        { 'ButtonBorder.fill': 'white', '_buttonFillOver': 'lightgray' },
                                        $(go.TextBlock, 'Duplicate',
                                        { margin: 12 }
                                        ),
                                        { click: (e, pt, compInstance) => this.duplicateComponent(compInstance, pt, e) })
                                ),
                            click: (e, node) => {e.diagram.commandHandler.showContextMenu(node), e.handled = true;},
                            contextClick: function(e, node) {
                                e.handled = true;
                            }
                        },
                        go.GraphObject.make(go.Picture,
                            { name: 'ButtonIcon', 
                                width: 15, 
                                height: 15,
                            },
                            new go.Binding('source', 'hamburgerMenu')
                        )
                    )

We have used e.handled = true after showing the menu as we did not want to trigger the click event at a higher level which would open up the configurations in our case.

But currently the node is not getting selected even just before the context menu is shown either. Which is why im getting undefined.

Oh, so you are not using the normal context click mechanisms. That probably explains the different behavior.

I hope it is clear that the first arguments to deleteComponent and duplicateComponent are their respective “ContextMenuButtons”. So those functions can find the Node that they should be operating on by evaluating firstArg.part.adornedPart. Because button.part will evaluate to the “ContextMenu”, and because Adornment.adornedPart will return the adorned Node.

So you can just call node.diagram.select(node) to make sure that Node is the only selected part before you call any command.

By the way, for other people reading this topic, I should say that the use of the outer “ContextMenuButton” is rather misleading. In this context it’s in a Node (or Link), not in an Adornment, so it’s just a regular button with some properties that have been preset.

Yes, but the adornment is NOT apprearing on that node when i click on the context menu button. The Adornment only appears when i select the node specifically by clicking on it.

It wouldnt be able to evaluate firstArg.part.adornedPart since there is no adornment that appears on the button click to begin with.

I just tried the following, and everything worked as I expected:

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape,
      { fill: "white", portId: "" },
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8 },
      new go.Binding("text")),
    $("Button", // not "ContextMenuButton", because it's not in a "ContextMenu"
      {
        alignment: go.Spot.TopRight,
        contextMenu:
          $("ContextMenu",
            $("ContextMenuButton",
              $(go.TextBlock, "Click Me"),
              {
                click: function(e, cmbutton) {
                  const node = cmbutton.part.adornedPart;
                  // select the node either with:
                  e.diagram.select(node);
                  // or with:
                  //e.diagram.commit(d => d.select(node));
                  console.log(node.key + " " + node.isSelected);
                }
              }
            )
          ),
        click: function(e, nbutton) {
          e.diagram.commandHandler.showContextMenu(nbutton);
        }
        // this is unneeded -- context clicks aren't supposed to work on Buttons:
        //contextClick: function(e, nbutton) { e.handled = true; }
        // this is optional:
        //toolTip: $("ToolTip", $(go.TextBlock, "click for some commands"))
      },
      $(go.Shape, "Triangle", { width: 8, height: 8 })
    )
  );