How to make "outline" in a mindmap chart?

I want a “outline” function in my mindmap chart like below:

1、Chose two nodes with cmd or shift key
2、Click “make outline” button from outside to add a new “outline node” with a new line, this new line point to the nodes I have chosen.
3、Add a new group which can drag up and down to select/deselect more nodes, when the group change nodes in it, the “outline” will point to the new node too like this:

Can you give a demo about doing this? I fount it a little hard to me, thank you very much!!!

Now my code is like this:

myDiagram = $(
    go.Diagram,
    "goeditor", // id of DIV
    {
      layout: $(DoubleTreeLayout, {
        //vertical: true,  // default directions are horizontal
        // choose whether this subtree is growing towards the right or towards the left:
        directionFunction: (n) => n.data && n.data.dir !== "left",
        // controlling the parameters of each TreeLayout:
        // bottomRightOptions: { nodeSpacing: 50, layerSpacing: 120 },
      }),
      "undoManager.isEnabled": true,
      "toolManager.hoverDelay": 0,

      allowMove: false,
      scrollMode: go.Diagram.InfiniteScroll,
    
    }
  );

What is this outline that you want the user to be able to add?

Is it this?
image

Or is it this?
image

Or is it both together, always?

What behaviors can the user use?

I want both together:
First, choose more than two nodes, and then this new node with the red line will appear.
image

Then, click the new node which generate above, the red rectangle appears, then I can drag the top or bottom of the rectangle to change the chosen nodes.
image

So the red resizable rectangle only appears when the user selects that blue rounded rectangle node (with the light bulb)? And the red left curly brace has a height that stretches to include all of the chosen nodes? What if the chosen nodes are spread out far away from each other, vertically or horizontally? It doesn’t matter if some of the nodes in the range are not included?

Can there be more than one “outline” or curly-brace object at a time?
If so, can there be any nodes included in more than one such “outline” at one time?
Can multiple "outline"s overlap each other?

@walter Thank you very much for your quick reply!
1、 [quote=“walter, post:4, topic:15441”]
So the red resizable rectangle only appears when the user selects that blue rounded rectangle node (with the light bulb)? And the red left curly brace has a height that stretches to include all of the chosen nodes?
[/quote]

Yes, that’s totally what I want.

2、 What if the chosen nodes are spread out far away from each other, vertically or horizontally? It doesn’t matter if some of the nodes in the range are not included?

It will make more than one “outline” if they are far from each other

3、Each node only can add one outline. And only one red rectangle will appear depanding on which “outline” be clicked.

4、Can multiple "outline"s overlap each other? No!

OK, I can create a sample for you when I get some free time.

The solution will probably require that the model be a GraphLinksModel, not a TreeModel, so you might want to start changing your code accordingly, if needed.

Thanks very much! But can GraphLinksModel make a mindmap tree?

Yes.

Model types are independent of layouts and node templates and link templates and tools and commands.

Here’s the first part of what you want:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
  Select the "Alpha" Node and then click this button:
  <button id="myTestButton">make Group for selected ungrouped Nodes</button>
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <script src="go.js"></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      layout: $(go.TreeLayout, { layerSpacing: 50, isRealtime: false }),
      "undoManager.isEnabled": true,
      "ModelChanged": 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, "Spot",
    {
      selectionObjectName: "DETAILS",
      selectionAdornmentTemplate:
        $(go.Adornment, "Auto",
          $(go.Shape, "RoundedRectangle", { fill: null, stroke: "dodgerblue", strokeWidth: 2 }),
          $(go.Placeholder, { margin: 4 })
        ),
      layout: null  // so that the Diagram.layout ignores groups 
    },
    $(go.Placeholder, { name: "PH" }),
    $(go.Shape,
      {
        alignment: go.Spot.Left, alignmentFocus: new go.Spot(1, 0.5, 4, 0),
        stretch: go.GraphObject.Vertical,
        geometryString: "M20 0 C0 0 20 10 0 10 20 10 0 20 20 20",
        stroke: "brown", strokeWidth: 1.5
      },
      new go.Binding("alignment", "right", r => r ? go.Spot.Right : go.Spot.Left),
      new go.Binding("alignmentFocus", "right", r => r ? new go.Spot(0, 0.5, -4, 0) : new go.Spot(1, 0.5, 4, 0)),
      new go.Binding("geometryString", "right", r => r ? "M0 0 C20 0 0 10 20 10 0 10 20 20 0 20" : "M20 0 C0 0 20 10 0 10 20 10 0 20 20 20")
    ),
    $(go.Panel, "Vertical",
      {
        name: "DETAILS",
        alignment: go.Spot.Left, alignmentFocus: new go.Spot(1, 0.5, 36, 0),
      },
      new go.Binding("alignment", "right", r => r ? go.Spot.Right : go.Spot.Left),
      new go.Binding("alignmentFocus", "right", r => r ? new go.Spot(0, 0.5, -36, 0) : new go.Spot(1, 0.5, 36, 0)),
      $(go.TextBlock,
        new go.Binding("text")),
      $(go.Shape, { stretch: go.GraphObject.Horizontal, height: 2, strokeWidth: 0 })
    )
  );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "Alpha", color: "lightblue" },
  { key: 2, text: "Beta", color: "orange" },
  { key: 3, text: "Gamma", color: "lightgreen" },
  { key: 4, text: "Delta", color: "pink", group: 5 },
  { key: 5, isGroup: true, right: true, text: "on right" },
],
[
  { from: 1, to: 2 },
  { from: 1, to: 3 },
  { from: 3, to: 4 },
]);

document.getElementById("myTestButton").addEventListener("click", e => {
    if (myDiagram.selection.any(p => p instanceof go.Node && p.isTopLevel && !(p instanceof go.Group))) {
      myDiagram.model.commit(m => {
        const newgrp = { isGroup: true, text: "Group" };
        // add the new group to the model
        m.addNodeData(newgrp);
        // add nodes as members of the new group
        myDiagram.selection.each(p => {
          if (p instanceof go.Node && p.isTopLevel && !(p instanceof go.Group)) {
            m.setGroupKeyForNodeData(p.data, m.getKeyForNodeData(newgrp));
          }
        });
      });
    }
  });
  </script>
</body>
</html>

After selecting the “Beta” and “Gamma” nodes and clicking the button:

image

Sorry, I don’t have time now to do more. Basically, you’ll want a custom Part.resizeAdornmentTemplate on the group which also sets Part.resizeObjectName to “PH”. Then you’ll need to customize the ResizingTool so that ResizingTool.resize instead of actually changing the GraphObject.desiredSize changes the group membership of the group based on which nodes are within the resize bounds. Or something like that…

Thank you very much, I will have a try.