Combine 2 textBlock in 1 panel

Hi there!

image

This pic is from regular html. we have here a link (number) and text which goes after link but when the text line breaks into next line it takes the free the space from the beginning of container.
In html its pretty simple to implement since you have a parent container that keeps this logic of breaking the line and text will be drawn inline from the beginning.

I dont know how to make same thing work in gojs.
I believe there is a way how to combine 2 textBlock inside a Panel to make it work same way, but I just cant come up with one.

Any ideas?

The reason the class is called TextBlock is because it is supposed to be the way to render text in a “block” – one font, one color, one background, in one rectangular area.

There is also the possibility of parsing the HTML and rendering a bunch of TextBlocks, one per word (or character?) or span of same attributes, whichever is smaller. We have some code to do that, but it’s not ready for general use.

But here’s some code that renders some HTML into an image that you could display:

function convertHtmlToImg(html, pic) {
  if (!pic.element) pic.element = new Image();

  let width = pic.width;
  let height = pic.height;
  if (isNaN(width) || !isFinite(width)) width = 100;
  if (isNaN(height) || !isFinite(height)) height = 100;

  const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='${width}' height='${height}'><foreignObject x='0' y='0' width='${width}' height='${height}'><div xmlns='http://www.w3.org/1999/xhtml'>${html}</div></foreignObject></svg>`;
  pic.element.src = `data:image/svg+xml;base64,${btoa(svg)}`;
  return pic.element;
}

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

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        { "undoManager.isEnabled": true });

    myDiagram.nodeTemplate =
      $(go.Node, "Vertical",
        $(go.TextBlock,
          { font: "bold 10pt sans-serif" },
          new go.Binding("text")),
        $(go.Panel, "Auto",
          $(go.Shape, { fill: "whitesmoke", strokeWidth: 0 }),
          $(go.Picture,  // Picture must have width and height (or desiredSize) set to real numbers
            { width: 100, height: 80, margin: 2 },
            new go.Binding("element", "html", convertHtmlToImg))
        )
      );

    myDiagram.model = new go.GraphLinksModel([
      { text: "Alpha", html: `Hello, how are <i>you</i> today?` },
      { text: "Beta", html: `<div style="color:green"><b>Bold</b> <i>italic</i> string</div>` },
      { text: "Gamma", html: `<style>em{color:red;}</style><em>Another</em> string <em>emphasized</em>` },
      { text: "Delta", html: `<u>underlined</u> <s>struckthrough</s>` }
    ]);
  }

The results:
image

Hi Walter!

I’ve checked what you suggested, and in the end of the day it is a picture which make this text impossible to interact with, especially if you need to have combination of link + text.

You can handle a click event, if that’s what you need.

On a whole picture, of course i can. I need to draw a link + regular text where the link acts like a link, and regular text acts like a regular text. Is it possible? Am i missing something?

No, you aren’t missing anything. You could detect exactly where in the picture the user clicked and then act accordingly. But that isn’t easy to generalize.

Are you always going to have two parts? An <a> followed by some text? Maybe it would be best to implement as a “HyperlinkText” superimposed on a TextBlock that includes initial spacing where the “HyperlinkText” is.

That might work out. Let me figure out how to use HyperLinkText.
Meanwhile how can I add spacing to the TextBlock only in first line?

Prepend your text with “\u200B” and as many spaces as you want.

    { key: 1, text: "\u200B Alpha Alpha Alpha Alpha Alpha Alpha Alpha", color: "lightblue" },
    { key: 2, text: "\u200B    Beta Beta Beta Beta Beta Beta Beta", color: "orange" },

produces:
image

No go, link text is dynamic value, i cant just pass spaces to the text that will be displayed after the link.
Other options?

??? why can’t you prepend "\u200B " to whatever text you want to show? It changes what is rendered, not what is in your model.

The hard part is figuring out how many spaces to use.

Yes, there is not a problem with adding "\u200B " to the displaying text. The problem is that i need to add spaces based on the length of the link text length. Of course I can calculate the length and prepend another “empty” line to the text, but I think that this is a very low quality solution with a lot of side effects.
So I’m wondering if there any other option to shift the first line of the text box.

Not that I can think of at this time.

I’ll check this option if i can make it stable enough. Thanks walter.

Here’s a completely different solution using the PanelLayoutFlow extension and no images/Pictures:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>

  <script src="go.js"></script>
  <script src="../extensions/HyperlinkText.js"></script>
  <script src="../extensions/PanelLayoutFlow.js"></script>
  <script id="code">
function init() {
  const $ = go.GraphObject.make;

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

  const itmap = new go.Map();
  itmap.add("",
      $(go.Panel,
        $(go.TextBlock, { margin: new go.Margin(0, 1.5) },
          new go.Binding("text", ""))
      ));
  itmap.add("Hyperlink",
      $(go.Panel,
        $("HyperlinkText",
          function(obj) { return "https://gojs.net/" + obj.part.data.version; },
          $(go.TextBlock, { stroke: "blue", font: "bold 10pt sans-serif" },
            new go.Binding("text", "", (v, obj) => "Visit GoJS " + obj.part.data.version))
        )
      ));

  function convText(t) {
    return [{ category: "Hyperlink" }].concat(t.split(" "));
  }

  myDiagram.nodeTemplate =
    $(go.Node, "Auto",
      { maxSize: new go.Size(200, NaN) },
      $(go.Shape,
        { fill: "white", portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" },
        new go.Binding("fill", "color")),
      $(go.Panel, "Flow",
        { margin: 8, stretch: go.GraphObject.Fill },
        new go.Binding("itemArray", "text", convText),
        { itemTemplateMap: itmap }
      )
    );

  myDiagram.model = new go.GraphLinksModel(
  [
    { key: 1, text: "Alpha Alpha Alpha Alpha Alpha Alpha Alpha", color: "lightblue", version: "latest" },
    { key: 2, text: "Beta Beta Beta Beta Beta", color: "orange", version: "beta" },
  ],
  [
    { from: 1, to: 2 },
  ]);
}
window.addEventListener('DOMContentLoaded', init);
  </script>
</body>
</html>

Hi Walter, can’t find this extension. I’m still on 2.1.44 version, does it include PanelLayoutFlow?
UPD.
It looks like it was added 2.1.49, can I add this extension to my project manually without updating gojs version?

I haven’t tried it with older versions, so I don’t know. Try it.

here is the code pen for your example.

Its seems like I cant limit lines number of regular textBlock and set overflow to ellipsis also doesnt take any effect.

UPD. I guess I understan now how it works, and it looks like this way i will not gonna be able to limit text within second line and add ellipsis effect.
Any suggestions?

Customize the PanelLayoutFlow to limit the TextBlocks to a total of two lines?

Yeah, right :) Looking into it