Add text in a SubGraphExpander and change styles

Hello, everyone. I need to add text in a SubGraphExpander instead of the default icon. I tried the _subGraphExpandedFigure and _button properties of the SubGraphExpanderButton, but it failed to remove the icon and leave the text. I was trying with properties of this documentation, GoJS Buttons -- Northwoods Software, but I didn’t find something according to what I need. Thanks in advance.

I have that for the moment.

Screenshot_19

With this code:

$("SubGraphExpanderButton",
              {
                "_subGraphExpandedFigure": null,
                "_subGraphCollapsedFigure": null,
              },
              { alignment: new go.Spot(0.5, 1), margin: 20 },
                $(go.TextBlock, "collapse")
            ),

The design I need to approach is something like:

Screenshot_20

I copied the definition from extensions/Buttons.js and modified it to bind the Shape.geometryString rather than the Shape.figure. I also added the TextBlock and bound its text string based on the Group.isSubGraphExpanded property.

<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<script id="code">
  go.GraphObject.defineBuilder('SubGraphExpanderButton2', function (args) {
    var button = /** @type {Panel} */ (
      go.GraphObject.make('Button',
        go.GraphObject.make(go.Panel, "Horizontal",
          go.GraphObject.make(go.Shape,  // the icon
            {
              name: 'ButtonIcon',
              stroke: '#424242',
              strokeWidth: 1,
              desiredSize: new go.Size(10, 10)
            },
            // bind the Shape.geometryString to the Group.isSubGraphExpanded value using this converter:
            new go.Binding('geometryString', 'isSubGraphExpanded',
              function (exp) {
                if (exp) {
                  return "M0 0 L 4 4 M0 4 L4 4 4 0  M 6 10 L6 6 10 6 M 10 10 L 6 6";
                } else {
                  return "M0 0 L 4 4 M0 4 L0 0 4 0  M 6 10 L10 10 10 6 M 10 10 L 6 6";
                }
              }
            ).ofObject()
          ),
          go.GraphObject.make(go.TextBlock,
            new go.Binding("text", "isSubGraphExpanded",
              function(exp) { return exp ? "collapse" : "expand"; }
            ).ofObject())
        )
      )
    );

    // subgraph expand/collapse behavior
    button.click = function (e, btn) {
      var group = btn.part;
      if (group instanceof go.Adornment) group = group.adornedPart;
      if (!(group instanceof go.Group)) return;
      var diagram = group.diagram;
      if (diagram === null) return;
      var cmd = diagram.commandHandler;
      if (group.isSubGraphExpanded) {
        if (!cmd.canCollapseSubGraph(group)) return;
      } else {
        if (!cmd.canExpandSubGraph(group)) return;
      }
      e.handled = true;
      if (group.isSubGraphExpanded) {
        cmd.collapseSubGraph(group);
      } else {
        cmd.expandSubGraph(group);
      }
    };

    return button;
  });

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

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          "undoManager.isEnabled": true,
          "ModelChanged": function(e) {     // just for demonstration purposes,
            if (e.isTransactionFinished) {  // show the model data in the page's TextArea
              document.getElementById("mySavedModel").textContent = e.model.toJson();
            }
          }
        });

    myDiagram.nodeTemplate =
      $(go.Node, "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())
      );

    myDiagram.groupTemplate =
      $(go.Group, "Vertical",
        $(go.Panel, "Horizontal",
          $("SubGraphExpanderButton2"),
          $(go.TextBlock, new go.Binding("text")),
        ),
        $(go.Placeholder, { padding: 10, background: "beige" })
      );

    myDiagram.model = new go.GraphLinksModel(
    [
      { key: 3, text: "Gamma", color: "lightgreen", group: 5 },
      { key: 4, text: "Delta", color: "pink", group: 5 },
      { key: 5, text: "GROUP", isGroup: true }
    ],
    [
      { from: 3, to: 4 }
    ]);
  }
</script>
</head>
<body onload="init()">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>
</body>
</html>

Great, that works. I made some small modifications, to add an icon when the group is contracted, but it generates a margin. Like this:

Screenshot_21

Here’s the code as I modified it:

go.GraphObject.defineBuilder('SubGraphExpanderButton2', function (args) {
      var button = /** @type {Panel} */ (
        go.GraphObject.make('Button',
          go.GraphObject.make(go.Panel, "Horizontal",
            go.GraphObject.make(go.Shape,  // the icon
              {
                name: 'ButtonIcon',
                stroke: '#424242',
                strokeWidth: 1,
              },
              // bind the Shape.geometryString to the Group.isSubGraphExpanded value using this converter:
              new go.Binding('geometryString', 'isSubGraphExpanded',
                function (exp) {
                  if (exp) {
                    return "M0 0 L 4 4 M0 4 L4 4 4 0  M 6 10 L6 6 10 6 M 10 10 L 6 6";
                  } else {
                    return "";
                  }
                }
              ).ofObject()
            ),
            go.GraphObject.make(go.TextBlock,
              new go.Binding("text", "isSubGraphExpanded",
                function(exp) { return exp ? "collapse" : ""; }
              ).ofObject(), {font: "normal 18px sans-serif"}),
            go.GraphObject.make(go.Picture,
              new go.Binding("source", "isSubGraphExpanded",
                function(exp) { return !exp ? "./../../assets/image/gojs/icon-arrow.svg" : ""; }
              ).ofObject())
          )
        )
      );

My other question is how do I dynamically change the color of the button. In the group where I added the SubGraphExpanderButton2, it varies in background. Would there be any way to pass the power to the button from there?

Screenshot_22

Depends on how the group template is defined. Did you specify a margin?

Yes, from the button you could modify objects outside of the button but within the group. However that makes it less likely that the button could be shared in other group templates.

Yes, I added a margin to better position him. But I was referring to the space when the group is collapsed (to the left of the icon)

Screenshot_23

Mmm to the button I mean send some background property from the SubGraphExpanderButton2 of the group to the definition and have dynamic background.

You don’t have to do everything in the button. Do what you want in a Group | GoJS API function.

Okay, I’ll check it out. Thanks.

I have another question related to this topic, how could I perform the expansion and collapse in a horizontal way?

What do you mean by “in a horizontal way”? Before-and-after images are always helpful to explain what you want.

An expansion like this:

Before:

Screenshot_24

After expansion:

Screenshot_25

That is a matter of changing the layout to do what you want.

That would make it a click event, right?

If that is how you want your users to invoke the change, yes. Whether you use a standard “Button” or make you own. Or in a context menu command or in some code invoked by other HTML.

Oh, ok ok. Yes, it should be invoked by the user. I think I’ll try making one of my own. Thank you, Walter.

I added a click event to a panel, and by using the e parameter, I am trying to update the diagram. Is there any way to issue those changes and take them to the general diagram?

This is the code.

  $(go.Panel, "Auto",
          { 
           click: (e: any) => {
              e.diagram.ac.Cc[0].width = 900;
              e.diagram.ac.Cc[0].stroke = "red";
              console.log(e.diagram.ac);
              console.log(e.diagram.ac.Cc);
            }
          },
          { alignment: new go.Spot(1, 0) },
          $(go.Shape, "Circle", 
            { width: 30, height: 30,  stroke: "#E2E2EA", strokeWidth: 1},
            new go.Binding("fill", "background")
          ),
          $(go.TextBlock, { name: "COUNT" }, new go.Binding("stroke", "stroke"))
        ),

Don’t use minified names. I can’t help you if you do.

What minified names do you mean, sorry?

This?

event.diagram.ac.Cc[0].width = 900;
event.diagram.ac.Cc[0].stroke = "red";

I’ve already found a better way to do what I need to do. Using the ContextMenu like you told me. Thanks, Walter!.