Based on the Toggle I would need to separate Parent-child relationship and Single Selection of nodes

I am using the Org Hierarchy in the modal. I have a Cascade Toggle button in the screen and based on the toggle the selection should happen. If the toggle is true I should be select only parent-child nodes relationship and if false then only single node selection. Below is the design and code I am using.

diagram.toolManager.clickSelectingTool.standardMouseSelect = function () {
            const diagram = this.diagram;
            let selectedItems = [];
            if (diagram === null || !diagram.allowSelect) return;
            var e = diagram.lastInput;
            var curobj = diagram.findPartAt(e.documentPoint, false);
            if (curobj !== null) {
                if (e.shift) {  // add the part to the selection
                    if (!curobj.isSelected) {
                        diagram.raiseDiagramEvent('ChangingSelection', diagram.selection);
                        let part = curobj;
                        while (part !== null && !part.canSelect()) part = part.containingGroup as go.Part;
                        if (part !== null) {
                            part.isSelected = true;
                            console.log(part.text);
                            console.log(part);
                            selectedItems.push(part.text);
                            if (part instanceof go.Node) {
                                part.findTreeParts().each(n => {
                                    console.log(n.text);
                                    selectedItems.push(n.text);
                                    n.isSelected = true;
                                });
                            }
                        }
                        const result = selectedItems.filter((element: string) => {
                            return element !== '';
                        });
                        props.updateSelectedModels(result);
                        diagram.raiseDiagramEvent('ChangedSelection', diagram.selection);
                    }
                } else {  // otherwise, toggle the selection
                    diagram.raiseDiagramEvent('ChangingSelection', diagram.selection);
                    let part = curobj;
                    while (part !== null && !part.canSelect()) part = part.containingGroup as go.Part;
                    if (part !== null) {
                        part.isSelected = !part.isSelected;
                        console.log(part.text);
                        console.log(part);
                        selectedItems.push(part.text);
                        if (part instanceof go.Node) {
                            part.findTreeParts().each(n => {
                                console.log(n.text);
                                selectedItems.push(n.text);
                                n.isSelected = part.isSelected;
                            });
                        }
                        const result = selectedItems.filter((element: string) => {
                            return element !== '';
                        });
                        props.updateSelectedModels(result);
                    }
                    diagram.raiseDiagramEvent('ChangedSelection', diagram.selection);
                }
            }
            // don't clear selection on background click
        }

Did you have a question?

Thanks for posting some code, but I cannot read it. Please indent it properly. Surround lines of code with lines of triple backquotes.

I have indent the code. Please check and let me know. @jhardy added the above code and sample in codesandbox gojs-react-selection-demo - CodeSandbox

You keep adding new requirements. Does the state of the toggle persist within app state, or does it default to some value each time the modal opens?

Here’s my solution:

  $(go.Diagram, . . .,
    {
      . . .,
      "clickSelectingTool.standardMouseSelect": function() {
        const diagram = this.diagram;
        if (!diagram.allowSelect) return;
        const e = diagram.lastInput;
        const node = diagram.findPartAt(e.documentPoint);
        if (node instanceof go.Node && node.canSelect()) {
          const includeDescendants = document.getElementById("myTestButton").checked;
          if (e.control || e.meta) {  // toggle the part's selection
            diagram.raiseDiagramEvent('ChangingSelection', diagram.selection);
            node.isSelected = !node.isSelected;
            if (includeDescendants) {
              node.findTreeParts().each(part => {
                if (part instanceof go.Node) part.isSelected = node.isSelected;
              });
            }
            diagram.raiseDiagramEvent('ChangedSelection', diagram.selection);
          } else if (e.shift) {  // add the part to the selection
            if (!node.isSelected) {
              diagram.raiseDiagramEvent('ChangingSelection', diagram.selection);
              node.isSelected = true;
              if (includeDescendants) {
                node.findTreeParts().each(part => {
                  if (part instanceof go.Node) part.isSelected = true;
                });
              }
              diagram.raiseDiagramEvent('ChangedSelection', diagram.selection);
            }
          } else {
            if (!node.isSelected) {
              diagram.raiseDiagramEvent('ChangingSelection', diagram.selection);
              node.isSelected = true;
              if (includeDescendants) {
                node.findTreeParts().each(part => {
                  if (part instanceof go.Node) part.isSelected = true;
                });
              }
              diagram.raiseDiagramEvent('ChangedSelection', diagram.selection);
            }
          }
        } else if (e.left && !(e.control || e.meta) && !e.shift) {
          // left click on background with no modifier: clear selection
          diagram.clearSelection();  // also raises ChangingSelection/Finished
        }
      }
    })

Like Jon’s solution, this was adapted from the implementation of Tool.standardMouseSelect. I simplified it to ignore unselectable Nodes and only select Nodes (not any Links).

@jhardy Toggle state we will be persist within the app state. So how can I split the above code based on the toggle.

I’ve modified the CodeSandbox. It now uses modelData to keep track of whether to cascade the selection. This is similar to what gojs-react-basic does with its “Allow Relinking?” checkbox.

I have implemented the given code. One issue I am facing when I toggle each time the nodes are getting doubled 13, 26, 39, etc. as attached screenshot. May I know what will be the issue?

Sorry, I don’t know. As you can see, the sandbox works. So you’ll have to debug your app.

Check the Diagram’s Model.nodeDataArray to make sure it has the number of node data objects that you are expecting.

If it has more nodes than you expected, you can try to track down what code is adding those extra copies, and when, and why.

I have debug the code and below is the method is getting loop and creating duplicate nodes. Actually I have 13 nodes but you can see in the screenshot that the key is increased from 14 -26 and keep increasing. I feel the “insertedNodeKeys” is adding more nodes. Please let me know. Also, I have not added “handleDiagramEvent” in my code. Please let me know if it require to add.

public handleModelChange(obj: go.IncrementalData) {
     const insertedNodeKeys = obj.insertedNodeKeys;
    const modifiedNodeData = obj.modifiedNodeData;
      const removedNodeKeys = obj.removedNodeKeys;

    // maintain maps of modified data so insertions don't need slow lookups
    const modifiedNodeMap = new Map<go.Key, go.ObjectData>();
    this.setState(
      produce((draft: AppState) => {
        let narr = draft.nodeDataArray;
        if (modifiedNodeData) {
          modifiedNodeData.forEach((nd: go.ObjectData) => {
            modifiedNodeMap.set(nd.key, nd);
            const idx = this.mapNodeKeyIdx.get(nd.key);
            console.log(idx);
            if (idx !== undefined && idx >= 0) {
              narr[idx] = nd;
            }
          });
        }
        if (insertedNodeKeys) {
          insertedNodeKeys.forEach((key: go.Key) => {
            const nd = modifiedNodeMap.get(key);
            const idx = this.mapNodeKeyIdx.get(key);
            if (nd && idx === undefined) {  // nodes won't be added if they already exist
              this.mapNodeKeyIdx.set(nd.key, narr.length);
              narr.push(nd);
              console.log(nd);
            }
          });
        }
        if (removedNodeKeys) {
          narr = narr.filter((nd: go.ObjectData) => {
            if (removedNodeKeys.includes(nd.key)) {
              return false;
            }
            return true;
          });
          draft.nodeDataArray = narr;
          this.refreshNodeIndex(narr);
        }
        draft.skipsDiagramUpdate = true;  // the GoJS model already knows about these updates
        console.log(draft.nodeDataArray);
      })
    ); 
  }

You will need handleDiagramEvent if you want the diagram selection to be saved in your state.

How does your toggle work? It sounds like something in your code is creating duplicate nodes. Take a close look at the sandbox and determine what you are doing differently.