Wants to improve Semantic zoom level functionality in regroupingTreeView

Hi So i am using this library Regrouping Demo with Tree View

But i want to semantic zoom level functionality which is present in this library Regrouping Demo . You can we have slider in this library and using this slider graph is expanding and collapsing.

So i tried to implement same functionality in this library regroupingTreeView . And almost i achieved.
so i have added logic like this to achieve Semantic zoom functionality
reexpand(e) {
this.$options.mySimpleDiagram.commit((diag) => {
var level = parseInt(document.getElementById(“levelSlider”).value);
//diag.findTopLevelGroups().each(g => this.expandGroups(g, 50, level))
setTimeout(
() =>
diag
.findTopLevelGroups()
.each((g) => this.expandGroups(g, 50, level)),
250
);
}, “reexpand”);
},
expandGroups(g, i, level) {
if (!(g instanceof go.Group)) return;
g.isSubGraphExpanded = i < level;
g.memberParts.each((m) => this.expandGroups(m, i + 1, level));
//setTimeout(()=> {g.memberParts.each(m => this.expandGroups(m, i + 1, level))},100)
},

But if you check slider here Regrouping Demo
You can see in the below attached screenshot. Slider value is not maximum but still whole is expanded.

What i mean to say even if the slider is 50% whole graph is expanded and if you increase slider to maximum value graph will not impact because whole graph is already expanded
Same case for collapse , when we add slider to maximum value and slowly if you try to reduce the slider value or drag graph is not collapse it will collapse only when you reduce value less than 50%.
I just giving you example

What i want to implement i want to divide the graph in level wise and so suppose when i increase slider in that case parent should expand and then sub parent should expand and when we reach to maximum value then final all the child should expand and vice versa for collapse also

Can you guide me how i can achieve

It sounds as if the only issue is that the maximum value for the slider does not accurately reflect the depth of nesting in the diagram. I think that could be computed when group membership changes.

I can demonstrate this later today when I have some more free time.

Sure i will wait for your reply

This adds a function (updateTotalGroupDepth) for updating the “max” property of the slider, based on calling another new function (groupDepth) for determining the maximum nested depth of groups in the diagram.

That updateTotalGroupDepth function is called both when the diagram is initialiized (“InitialLayoutCompleted” DiagramEvent) and when the user drag-and-drops a node (in finishDrop).

That function is not called when the app changes the group structure programmatically and not in finishDrop. I don’t know if your app will do that, but if it does, you can either call it yourself after making the model changes, or else you can implement a Model Changed listener that detects group membership changes and then schedules a call to updateTotalGroupDepth. If you do that, you won’t need to add that call in finishDrop.

<!DOCTYPE html>
<html><body>
<script src="https://unpkg.com/gojs"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lora&display=swap" rel="stylesheet">
<script id="code">
  function init() {

    myDiagram = new go.Diagram("myDiagramDiv",
      {
        "InitialLayoutCompleted": e => updateTotalGroupDepth(),
        // when a drag-drop occurs in the Diagram's background, make it a top-level node
        mouseDrop: e => finishDrop(e, null),
        layout:  // Diagram has simple horizontal layout
          new go.GridLayout(
            { wrappingWidth: Infinity, alignment: go.GridAlignment.Position, cellSize: new go.Size(1, 1) }),
        "commandHandler.archetypeGroupData": { isGroup: true, text: "Group", horiz: false },
        "undoManager.isEnabled": true
      });

    const colors = {
      white: '#fffcf6',
      blue: '#dfebe5',
      darkblue: '#cae0d8',
      yellow: '#fcdba2',
      gray: '#59524c',
      black: '#000000'
    }

    // The one template for Groups can be configured to either layout out its members
    // horizontally or vertically, each with a different default color.

    function makeLayout(horiz) {  // a Binding conversion function
      return new go.GridLayout(
        {
          wrappingColumn: horiz ? Infinity : 1,
          alignment: go.GridAlignment.Position,
          cellSize: new go.Size(1, 1),
          spacing: horiz ? new go.Size(8, 8) : new go.Size(5, 5)
        });
    }

    function defaultColor(horiz) {  // a Binding conversion function
      return horiz ? colors.white : colors.darkblue;
    }

    // this function is used to highlight a Group that the selection may be dropped into
    function highlightGroup(e, grp, show) {
      if (!grp) return;
      e.handled = true;
      if (show) {
        // cannot depend on the grp.diagram.selection in the case of external drag-and-drops;
        // instead depend on the DraggingTool.draggedParts or .copiedParts
        var tool = grp.diagram.toolManager.draggingTool;
        var map = tool.draggedParts || tool.copiedParts;  // this is a Map
        // now we can check to see if the Group will accept membership of the dragged Parts
        if (grp.canAddMembers(map.toKeySet())) {
          grp.isHighlighted = true;
          return;
        }
      }
      grp.isHighlighted = false;
    }

    // Upon a drop onto a Group, we try to add the selection as members of the Group.
    // Upon a drop onto the background, or onto a top-level Node, make selection top-level.
    // If this is OK, we're done; otherwise we cancel the operation to rollback everything.
    function finishDrop(e, grp) {
      var ok = (grp !== null
        ? grp.addMembers(grp.diagram.selection, true)
        : e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true));
      if (!ok) e.diagram.currentTool.doCancel();
      updateTotalGroupDepth();
    }

    myDiagram.groupTemplate =
      new go.Group("Auto",
        {
          ungroupable: true,
          // highlight when dragging into the Group
          mouseDragEnter: (e, grp, prev) => highlightGroup(e, grp, true),
          mouseDragLeave: (e, grp, next) => highlightGroup(e, grp, false),
          computesBoundsAfterDrag: true,
          computesBoundsIncludingLocation: true,
          // when the selection is dropped into a Group, add the selected Parts into that Group;
          // if it fails, cancel the tool, rolling back any changes
          mouseDrop: finishDrop,
          handlesDragDropForMembers: true,  // don't need to define handlers on member Nodes and Links
          // Groups containing Groups lay out their members horizontally
          layout: makeLayout(false),
          background: defaultColor(false) // default value if not specified in data
        })
        .bind("background", "horiz", defaultColor)
        .bind("layout", "horiz", makeLayout)
        .add(
          new go.Shape("Rectangle",
            { stroke: colors.gray, strokeWidth: 1, hasShadow: true })
            .bindObject("fill", "isHighlighted", h => h ? 'rgba(0,255,0,.3)' : 'transparent'),
          new go.Panel("Vertical")  // title above Placeholder
            .add(
              new go.Panel("Horizontal",  // button next to TextBlock
                { stretch: go.Stretch.Horizontal })
                .add(
                  go.GraphObject.build("SubGraphExpanderButton", { alignment: go.Spot.Right, margin: 5 }),
                  new go.TextBlock(
                    {
                      alignment: go.Spot.Left,
                      editable: true,
                      margin: new go.Margin(6, 10, 6, 1),
                      font: "bold 16px Lora, serif",
                      opacity: 0.95,  // allow some color to show through
                      stroke: colors.black
                    })
                    .bind("font", "horiz", (horiz) => horiz ? "bold 20px Lora, serif" : "bold 16px Lora, serif")
                    .bindTwoWay("text")
                ),  // end Horizontal Panel
              new go.Placeholder({ padding: 8, margin: 4, alignment: go.Spot.TopLeft })
            )  // end Vertical Panel
        )  // end Auto Panel


    myDiagram.nodeTemplate =
      new go.Node("Auto",
        { // dropping on a Node is the same as dropping on its containing Group, even if it's top-level
          mouseDrop: (e, node) => finishDrop(e, node.containingGroup),
          isShadowed: true,
          shadowBlur: 0,
          shadowColor: colors.gray,
          shadowOffset: new go.Point(4.5, 3.5)
        })
        .add(new go.Shape("RoundedRectangle", { fill: colors.yellow, stroke: null, strokeWidth: 0 }))
        .add(new go.TextBlock(
          {
            margin: 8,
            editable: true,
            font: "13px Lora, serif",
          })
          .bindTwoWay("text"));

    // initialize the Palette and its contents
    myPalette =
      new go.Palette("myPaletteDiv",
        {
          nodeTemplateMap: myDiagram.nodeTemplateMap,
          groupTemplateMap: myDiagram.groupTemplateMap
        });

    myPalette.model = new go.GraphLinksModel([
      { text: "New Node", color: "#ACE600" },
      { isGroup: true, text: "H Group", horiz: true },
      { isGroup: true, text: "V Group", horiz: false }
    ]);

    var slider = document.getElementById("levelSlider");
    slider.addEventListener('change', reexpand);
    slider.addEventListener('input', reexpand);

    load();
  }

  function reexpand(e) {
    myDiagram.commit(diag => {
      var level = parseInt(document.getElementById("levelSlider").value);
      diag.findTopLevelGroups().each(g => expandGroups(g, 0, level))
    }, "reexpand");
  }
  function expandGroups(g, i, level) {
    if (!(g instanceof go.Group)) return;
    g.isSubGraphExpanded = i < level;
    g.memberParts.each(m => expandGroups(m, i + 1, level))
  }
  function updateTotalGroupDepth() {
    let d = 0;
    myDiagram.findTopLevelGroups().each(g => d = Math.max(d, groupDepth(g)));
    document.getElementById("levelSlider").max = Math.max(0, d);
  }
  function groupDepth(g) {
    if (!(g instanceof go.Group)) return 0;
    let d = 1;
    g.memberParts.each(m => d = Math.max(d, groupDepth(m)+1));
    return d;
  }

  // save a model to and load a model from JSON text, displayed below the Diagram
  function save() {
    document.getElementById("mySavedModel").value = myDiagram.model.toJson();
    myDiagram.isModified = false;
  }
  function load() {
    myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
  }
  window.addEventListener('DOMContentLoaded', init);
</script>

<div id="sample">
  <div style="width: 100%; display: flex; justify-content: space-between">
    <div id="myPaletteDiv" style="width: 130px; margin-right: 2px; background-color: #dfebe5; border: solid 1px black">
    </div>
    <div id="myDiagramDiv" style="flex-grow: 1; height: 500px; background-color: #dfebe5; border: solid 1px black">
    </div>
  </div>
  <p>
    This sample allows the user to drag nodes, including groups, into and out of groups,
    both from the Palette as well as from within the Diagram.
    See the <a href="../intro/groups.html">Groups Intro page</a> for an explanation of GoJS Groups.
  </p>
  <p>
    Highlighting to show feedback about potential addition to a group during a drag is implemented
    using <a>GraphObject.mouseDragEnter</a> and <a>GraphObject.mouseDragLeave</a> event handlers.
    Because <a>Group.computesBoundsAfterDrag</a> is true, the Group's <a>Placeholder</a>'s bounds are
    not computed until the drop happens, so the group does not continuously expand as the user drags
    a member of a group.
  </p>
  <p>
    When a drop occurs on a Group or a regular Node, the <a>GraphObject.mouseDrop</a> event handler
    adds the selection (the dragged Nodes) to the Group as new members.
    The <a>Diagram.mouseDrop</a> event handler changes the dragged selected Nodes to be top-level,
    rather than members of whatever Groups they had been in.
  </p>
  <p>
    The slider controls how many nested levels of Groups are expanded. </br>
    Semantic zoom level: <input id="levelSlider" type="range" min="0" max="5" />
  </p>
  <div id="buttons">
    <button id="saveModel" onclick="save()">Save</button>
    <button id="loadModel" onclick="load()">Load</button>
    Diagram Model saved in JSON format:
  </div>
  <textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
  "nodeDataArray": [
{"key":1, "isGroup":true, "text":"Main 1", "horiz":true},
{"key":2, "isGroup":true, "text":"Main 2", "horiz":true},
{"key":3, "isGroup":true, "text":"Group A", "group":1},
{"key":4, "isGroup":true, "text":"Group B", "group":1},
{"key":5, "isGroup":true, "text":"Group C", "group":2},
{"key":6, "isGroup":true, "text":"Group D", "group":2},
{"key":7, "isGroup":true, "text":"Group E", "group":6},
{"text":"First A", "group":3, "key":-7},
{"text":"Second A", "group":3, "key":-8},
{"text":"First B", "group":4, "key":-9},
{"text":"Second B", "group":4, "key":-10},
{"text":"Third B", "group":4, "key":-11},
{"text":"First C", "group":5, "key":-12},
{"text":"Second C", "group":5, "key":-13},
{"text":"First D", "group":6, "key":-14},
{"text":"First E", "group":7, "key":-15}
 ],
  "linkDataArray": [  ]}
  </textarea>
</div>
</body></html>