Horizontal panel with optional image and text ellipsis

I am trying to make a node template which has
- horizontal panel containing image and text.
- Node is of fixed size of specified width and height.
- The image is optional - if present needs to be displayed to left of text.
- Text is placed next to image in single line and for longer text it should get trimmed with …
- When no image is present, text should occupy the space of image also. With this also if text is bigger it should get trimmed with …

Can you help with creating a template with above requirement

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

    myDiagram =
      $(go.Diagram, "myDiagramDiv");

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        { width: 80, height: 40 },
        $(go.Shape,
          { fill: "white" }),
        $(go.Panel, "Table",
          { margin: 4, stretch: go.GraphObject.Fill },
          $(go.RowColumnDefinition,
            { column: 0, sizing: go.RowColumnDefinition.None }),
          $(go.Picture,
            {
              column: 0,
              width: 32, height: 32,
              visible: false,
              successFunction: (pic, e) => pic.visible = true
            },
            new go.Binding("source")
          ),
          $(go.TextBlock,
            {
              column: 2,
              stretch: go.GraphObject.Horizontal,
              maxLines: 1,
              overflow: go.TextBlock.OverflowEllipsis
            },
            new go.Binding("text"))
        )
      );

    myDiagram.model = $(go.GraphLinksModel,
      {
        nodeDataArray: [
          { key: 1, text: "Alpha", source: "../samples/images/hs1.png" },
          { key: 2, text: "Beta Beta Beta", source: "../samples/images/hs2.png" },
          { key: 3, text: "Gamma" },
          { key: 4, text: "Delta Delta Delta" },
        ]
      });
  }

produces:
image

Will it be possible to have image and text both centered. When their is only text, it gets centered.
For first Alpha node it needs to be both text and image centered and align close to each other.

image

This is a bit tricky, because you want the panel layout to change behavior depending on the available width for the text. The problem is that you want the TextBlock’s available width to stretch to the width of the area that it is in, which depends on the visibility of the Picture. But you don’t want to just stretch the TextBlock’s width that far, because then it would always stretch the whole distance, and you want it to stop when it doesn’t need more width. Although it’s easy to set stretch to go.GraphObject.Horizontal in order to stretch the desiredSize, there’s no easy way to stretch the maxSize. So we need to do it programmatically.

Here’s one solution that depends on a binding on the node’s width and other properties to compute the available maximum width (maxSize).

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

    myDiagram =
      $(go.Diagram, "myDiagramDiv");

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        { width: 120, height: 40 },
        $(go.Shape,
          { fill: "white" }),
        $(go.Panel, "Table",
          { margin: 4 },
          $(go.Picture,
            {
              name: "PIC",
              column: 0,
              width: 32, height: 32,
              margin: new go.Margin(0, 2, 0, 0),
              visible: false,
              successFunction: (pic, e) => { pic.visible = true; pic.part.updateTargetBindings(); }
            },
            new go.Binding("source")
          ),
          $(go.TextBlock,
            {
              column: 1,
              maxLines: 1,
              overflow: go.TextBlock.OverflowEllipsis,
              maxSize: new go.Size(50, NaN)
            },
            new go.Binding("maxSize", "", maxTextSize).ofObject(),
            new go.Binding("text"))
        )
      );

    // compute the available width for the TextBlock -- this depends
    // on the structure of the node template
    function maxTextSize(node) {
      var pic = node.findObject("PIC");
      var table = pic.panel;
      var picw = pic.visible ? pic.margin.left + pic.width + pic.margin.right : 0;
      return new go.Size(node.width - table.margin.left - picw - table.margin.right, NaN);
    }

    myDiagram.model = $(go.GraphLinksModel,
      {
        nodeDataArray: [
          { key: 1, text: "Alpha", source: "../samples/images/hs1.png" },
          { key: 2, text: "Beta Beta Beta", source: "../samples/images/hs2.png" },
          { key: 3, text: "Gamma", source: "bogus" },
          { key: 4, text: "Delta Delta Delta Delta", source: "bogus" },
        ]
      });
  }

Thanks Walter.

I guess, one last help here … Your sample with codepen works as expected even when different nodes have same image.

But in my project, I set same model from sample code having same image for multiple nodes. In that case the Sucess function of picture object gets invoked uniquely for each image. For gamma node i set same image of Alpha. Setting another image loads for gamma node also.

Update - When i do hard refresh (Ctrl +F5) multiple times - then 2 times the sucess function executed for all nodes. May be something to do with request triggered timming has to do something here …

Please update to the very latest version of GoJS, 2.1.48. Maybe that will help.

Thanks. Seems working now … Will check it further.