Add scrollbar and search box in the selection dropdown

image

I have this selection dropdown and i want to add a scroll bar in it so that it display only 5 records , how can we do that.
And can we add a search functionality also in the selection dropdown where i can type and it will filter the data and display me the list.

  this.diagram.nodeTemplate =
      new go.Node("Auto")
        .add(
          new go.Shape({ fill: "white" }),
          new go.Panel("Vertical")
            .add(
              new go.TextBlock({ margin: 4, font: "bold 12pt sans-serif" })
                .bind("text", "title"),
              new go.Panel("Horizontal", {
                click: (e:any, pnl:any) => {  // show or hide the ChoicesAdornment
                  // e.handled = true;
                  this.showChoices((pnl.findObject("SHP").figure === "LineDown") ? pnl.part : null);
                },
                // for mouse-over highlighting
                background: "transparent",
                mouseEnter: (e, pnl) => pnl.background = "lightgray",
                mouseLeave: (e, pnl) => pnl.background = "transparent"
              })
                .add(
                  new go.TextBlock("(choose)", {
                    name: "TB",
                    width: 100,
                    margin: new go.Margin(4, 4, 2, 4),
                    font: "italic 10pt sans-serif",
                    stroke: "blue"
                  })
                    .bindTwoWay("text", "value")
                    .bind("font", "value", v => v ? "bold 10pt sans-serif" : "italic 10pt sans-serif")
                    .bind("stroke", "value", v => v ? "black" : "blue"),
                  new go.Shape("LineDown", {
                    name: "SHP",
                    width: 14, height: 12,
                    margin: 2,
                    strokeWidth: 2,
                  })
                )
            )
        );

    this.diagram.model = new go.GraphLinksModel(
      [
        // { key: 1, title: "Alpha", choices: ["Segment, Tray and Motor", "Helix", "Helix Timing" , "Helix Accessory" , "Accessories" , "Single Crowder"], value: "one" },
        { key: 2, title: "Beta", choices: ["A", "B","C","D","E","F","J","K","L"] },
        { key: 3, title: "Gamma", choices: ["only"] },
        { key: 4, title: "Delta" },  // use a default choices Array
      ]);
private  ChoicesAdornment =
    new go.Adornment("Spot")
      .add(
        new go.Placeholder(),  // placeholder for the TextBlock in the Node
        new go.Panel("Auto", { alignment: go.Spot.BottomLeft, alignmentFocus: go.Spot.TopLeft})
          .add(
            new go.Shape("Rectangle", { fill: "white", stroke: "gray", strokeWidth: 1 , margin : new go.Margin(2,0,0,0) }),
            new go.Panel("Vertical", {
              margin: 7,
              defaultStretch: go.Stretch.Horizontal,
              itemTemplate:
                new go.Panel({
                  isActionable: true,  // to allow a click event in an Adornment
                  click: (e: any, item: any) => {
                    const tb = item.part.adornedPart.findObject("TB");
                    if (!tb) return;
                    e.diagram.commit((diag: any) => {
                      tb.text = item.data;
                      this.showChoices(null);
                    });
                  },
                  // for mouse-over highlighting
                  background: "transparent",
                  mouseEnter: (e:any, item:any) => item.background = "lightGray",
                  mouseLeave: (e:any, item:any) => item.background = "transparent"
                },
              
              )
              .add(
                    new go.Shape("Rectangle",{fill:"transparent",stroke:"gray",strokeWidth:0.5, width:155, height :35}),
                    new go.TextBlock({ height:35, width:150,verticalAlignment:go.Spot.Center , margin: new go.Margin(0,0,0,5)})
                      .bind("text", "")  // TextBlock.text gets the whole Array item value
                  )
            })
              .bind("itemArray", "choices")
          )
      );

  private showChoices(node:any) {
    this.diagram!.commit(() => {
      if (!node) {
        const ad = this.ChoicesAdornment;
        const oldnode = ad.adornedPart;
        if (oldnode) {
          const oldshp:any = oldnode.findObject("SHP");
          if (oldshp) oldshp.figure = "LineDown";
          oldnode.removeAdornment("Choices");
        }
      } else {
        const tb = node.findObject("TB");
        const shp = node.findObject("SHP");
        if (!tb || !shp) return;
        this.showChoices(null);  // remove from any other node
        shp.figure = "LineUp";
        const ad = this.ChoicesAdornment;
        ad.adornedObject = tb;
        node.addAdornment("Choices", ad);
        if (!Array.isArray(node.data.choices) || node.data.choices.length === 0) {
          ad.data = { choices: ["Alpha", "Beta", "Gamma", "Delta"] };  // default choices Array
        }
      }
    }, null);  // skipsUndoManager
  }

this selection dropdown node template is Selection dropdown menu - #6 by walter

Are you sure you want to reimplement all that in GoJS, when you could use existing (non-GoJS) code to implement it in HTML?

That other topic said in a later post that you implemented in HTML.

if i am using html one then i have to click two times to open the dropdown so it’s not a good user experience.
I have to create one dropdown like this
image
and one without the search functionality.

This sample demonstrates adding a scrollbar implemented in GoJS. You’ll need to wrap the “Table” Panel that you want to scroll with a “ScrollingTable” Panel that holds that panel plus the implementation of the scrollbar. Scrolling Table | GoJS

The complete implementation of the “ScrollingTable” is at extensionsJSM/ScrollingTable.ts or .js (ES module), or at extensions/ScrollingTable.js (for script or UMD).

To implement filtering, you’ll need to implement a custom text editor where each time the user types a character you filter the items shown, changing their visible property.

I am not able to add the scrollbar in the dropdown adornment using the ScrollingTable.js, can you help with it.

<!DOCTYPE html>
<html>
<head>
  <title>Simple Choices Selector Adornment</title>
  <!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
  <textarea id="mySavedModel" style="width:100%;height:150px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <script src="https://unpkg.com/create-gojs-kit/dist/extensions/ScrollingTable.js"></script>
  <script id="code">
const myDiagram =
  new go.Diagram("myDiagramDiv", {
      "BackgroundSingleClicked": e => showChoices(null),
      "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();
        }
      }
    });

// the Adornment holding the list of choices
const ChoicesAdornment =
  new go.Adornment("Spot")
    .add(
      new go.Placeholder(),  // placeholder for the TextBlock in the Node
      new go.Panel("Auto", {
          alignment: go.Spot.BottomLeft,
          alignmentFocus: go.Spot.TopLeft,
          stretch: go.Stretch.Horizontal
        })
        .add(
          new go.Shape("RoundedRectangle", { fill: "white", stroke: "gray", strokeWidth: 2 }),
          go.GraphObject.build("ScrollingTable", {
              maxSize: new go.Size(NaN, 65),
              stretch: go.Stretch.Fill,
              "TABLE.rowSizing": go.Sizing.None,
              "TABLE.itemTemplate":
                new go.Panel("TableRow", {
                    isActionable: true,  // to allow a click event in an Adornment
                    click: (e, item) => {
                      const tb = item.part.adornedPart.findObject("TB");
                      if (!tb) return;
                      e.diagram.commit(diag => {
                        tb.text = item.data;
                        showChoices(null);
                      });
                    },
                    // for mouse-over highlighting
                    background: "transparent",
                    mouseEnter: (e, item) => item.background = "cyan",
                    mouseLeave: (e, item) => item.background = "transparent"
                  })
                  .add(
                    new go.TextBlock({ margin: 1 })
                      .bind("text", "")  // TextBlock.text gets the whole Array item value
                  )
            })
            .bind("TABLE.itemArray", "choices")
            .bind("SCROLLBAR.visible", "choices", arr => arr.length > 6)
          )
      );

function showChoices(node) {
  myDiagram.commit(diag => {
    if (!node) {
      const ad = ChoicesAdornment;
      const oldnode = ad.adornedPart;
      if (oldnode) {
        const oldshp = oldnode.findObject("SHP");
        if (oldshp) oldshp.figure = "LineDown";
        oldnode.removeAdornment("Choices");
      }
    } else {
      const tb = node.findObject("TB");
      const shp = node.findObject("SHP");
      if (!tb || !shp) return;
      showChoices(null);  // remove from any other node
      shp.figure = "LineUp";
      const ad = ChoicesAdornment;
      ad.adornedObject = tb;
      node.addAdornment("Choices", ad);
      if (!Array.isArray(node.data.choices) || node.data.choices.length === 0) {
        ad.data = { choices: ["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta"] };  // default choices Array
      }
    }
  }, null);  // skipsUndoManager
}

myDiagram.nodeTemplate =
  new go.Node("Auto")
    .add(
      new go.Shape({ fill: "white" }),
      new go.Panel("Vertical")
        .add(
          new go.TextBlock({ margin: 4, font: "bold 12pt sans-serif" })
            .bind("text", "title"),
          new go.Panel("Horizontal", {
              click: (e, pnl) => {  // show or hide the ChoicesAdornment
                e.handled = true;
                showChoices((pnl.findObject("SHP").figure === "LineDown") ? pnl.part : null);
              },
              // for mouse-over highlighting
              background: "transparent",
              mouseEnter: (e, pnl) => pnl.background = "lightgray",
              mouseLeave: (e, pnl) => pnl.background = "transparent"
            })
            .add(
              new go.TextBlock("(choose)", {
                  name: "TB",
                  width: 100,
                  margin: new go.Margin(4, 4, 2, 4),
                  font: "italic 10pt sans-serif",
                  stroke: "blue"
                })
                .bindTwoWay("text", "value")
                .bind("font", "value", v => v ? "bold 10pt sans-serif" : "italic 10pt sans-serif")
                .bind("stroke", "value", v => v ? "black" : "blue"),
              new go.Shape("LineDown", {
                name: "SHP",
                width: 14, height: 12,
                margin: 2,
                strokeWidth: 2,
              })
            )
        )
    );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, title: "Alpha", choices: ["one", "two", "three", "four", "five", "six", "seven"], value: "one" },
  { key: 2, title: "Beta", choices: ["hello", "goodbye"] },
  { key: 3, title: "Gamma", choices: ["only one"] },
  { key: 4, title: "Delta" },  // use a default choices Array
]);
  </script>
</body>
</html>

How do we use any goJs extension in the angular component, I have copy pasted the extension.ts code in a file how to use that code in a component?

In this case the extension is entirely implemented as GoJS GraphObjects, so there’s no need to try to create your own Angular component for that. It’s all just code that is incorporated into your diagram component.

Do we have any sample available for the custom text editor as input element
something like this, all samples available are of selection dropdown.
image

This is not what you are asking for now, but it is an example of a custom text editor that does what you are asking for while also showing a filtered list of choices in HTML.

<!DOCTYPE html>
<html>
<head>
  <title>Text Editor using a datalist</title>
  <!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
  <meta name="description" content="Custom text editor that uses an <input> with a <datalist>.">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:300px"></div>
  When you edit the text, an &lt;input&gt; is shown that uses a &lt;datalist&gt;
  to show choices that match what the user has typed so far.
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <datalist id="CountryDatalist">
    <option>Afghanistan</option>
    <option>Åland Islands</option>
    <option>Albania</option>
    <option>Algeria</option>
    <option>American Samoa</option>
    <option>Andorra</option>
    <option>Angola</option>
    <option>Anguilla</option>
    <option>Antarctica</option>
    <option>Antigua and Barbuda</option>
    <option>Argentina</option>
    <option>Armenia</option>
    <option>Aruba</option>
    <option>Australia</option>
    <option>Austria</option>
    <option>Azerbaijan</option>
    <option>Bahamas</option>
    <option>Bahrain</option>
    <option>Bangladesh</option>
    <option>Barbados</option>
    <option>Belarus</option>
    <option>Belgium</option>
    <option>Belize</option>
    <option>Benin</option>
    <option>Bermuda</option>
    <option>Bhutan</option>
    <option>Bolivia (Plurinational State of)</option>
    <option>Bonaire, Sint Eustatius and Saba</option>
    <option>Bosnia and Herzegovina</option>
    <option>Botswana</option>
    <option>Bouvet Island</option>
    <option>Brazil</option>
    <option>British Indian Ocean Territory</option>
    <option>Brunei Darussalam</option>
    <option>Bulgaria</option>
    <option>Burkina Faso</option>
    <option>Burundi</option>
    <option>Cabo Verde</option>
    <option>Cambodia</option>
    <option>Cameroon</option>
    <option>Canada</option>
    <option>Cayman Islands</option>
    <option>Central African Republic</option>
    <option>Chad</option>
    <option>Chile</option>
    <option>China</option>
    <option>Christmas Island</option>
    <option>Cocos (Keeling) Islands</option>
    <option>Colombia</option>
    <option>Comoros</option>
    <option>Congo</option>
    <option>Congo, Democratic Republic of the</option>
    <option>Cook Islands</option>
    <option>Costa Rica</option>
    <option>Côte d'Ivoire</option>
    <option>Croatia</option>
    <option>Cuba</option>
    <option>Curaçao</option>
    <option>Cyprus</option>
    <option>Czechia</option>
    <option>Denmark</option>
    <option>Djibouti</option>
    <option>Dominica</option>
    <option>Dominican Republic</option>
    <option>Ecuador</option>
    <option>Egypt</option>
    <option>El Salvador</option>
    <option>Equatorial Guinea</option>
    <option>Eritrea</option>
    <option>Estonia</option>
    <option>Eswatini</option>
    <option>Ethiopia</option>
    <option>Falkland Islands (Malvinas)</option>
    <option>Faroe Islands</option>
    <option>Fiji</option>
    <option>Finland</option>
    <option>France</option>
    <option>French Guiana</option>
    <option>French Polynesia</option>
    <option>French Southern Territories</option>
    <option>Gabon</option>
    <option>Gambia</option>
    <option>Georgia</option>
    <option>Germany</option>
    <option>Ghana</option>
    <option>Gibraltar</option>
    <option>Greece</option>
    <option>Greenland</option>
    <option>Grenada</option>
    <option>Guadeloupe</option>
    <option>Guam</option>
    <option>Guatemala</option>
    <option>Guernsey</option>
    <option>Guinea</option>
    <option>Guinea-Bissau</option>
    <option>Guyana</option>
    <option>Haiti</option>
    <option>Heard Island and McDonald Islands</option>
    <option>Holy See</option>
    <option>Honduras</option>
    <option>Hong Kong</option>
    <option>Hungary</option>
    <option>Iceland</option>
    <option>India</option>
    <option>Indonesia</option>
    <option>Iran (Islamic Republic of)</option>
    <option>Iraq</option>
    <option>Ireland</option>
    <option>Isle of Man</option>
    <option>Israel</option>
    <option>Italy</option>
    <option>Jamaica</option>
    <option>Japan</option>
    <option>Jersey</option>
    <option>Jordan</option>
    <option>Kazakhstan</option>
    <option>Kenya</option>
    <option>Kiribati</option>
    <option>Korea (Democratic People's Republic of)</option>
    <option>Korea, Republic of</option>
    <option>Kuwait</option>
    <option>Kyrgyzstan</option>
    <option>Lao People's Democratic Republic</option>
    <option>Latvia</option>
    <option>Lebanon</option>
    <option>Lesotho</option>
    <option>Liberia</option>
    <option>Libya</option>
    <option>Liechtenstein</option>
    <option>Lithuania</option>
    <option>Luxembourg</option>
    <option>Macao</option>
    <option>Madagascar</option>
    <option>Malawi</option>
    <option>Malaysia</option>
    <option>Maldives</option>
    <option>Mali</option>
    <option>Malta</option>
    <option>Marshall Islands</option>
    <option>Martinique</option>
    <option>Mauritania</option>
    <option>Mauritius</option>
    <option>Mayotte</option>
    <option>Mexico</option>
    <option>Micronesia (Federated States of)</option>
    <option>Moldova, Republic of</option>
    <option>Monaco</option>
    <option>Mongolia</option>
    <option>Montenegro</option>
    <option>Montserrat</option>
    <option>Morocco</option>
    <option>Mozambique</option>
    <option>Myanmar</option>
    <option>Namibia</option>
    <option>Nauru</option>
    <option>Nepal</option>
    <option>Netherlands</option>
    <option>New Caledonia</option>
    <option>New Zealand</option>
    <option>Nicaragua</option>
    <option>Niger</option>
    <option>Nigeria</option>
    <option>Niue</option>
    <option>Norfolk Island</option>
    <option>North Macedonia</option>
    <option>Northern Mariana Islands</option>
    <option>Norway</option>
    <option>Oman</option>
    <option>Pakistan</option>
    <option>Palau</option>
    <option>Palestine, State of</option>
    <option>Panama</option>
    <option>Papua New Guinea</option>
    <option>Paraguay</option>
    <option>Peru</option>
    <option>Philippines</option>
    <option>Pitcairn</option>
    <option>Poland</option>
    <option>Portugal</option>
    <option>Puerto Rico</option>
    <option>Qatar</option>
    <option>Réunion</option>
    <option>Romania</option>
    <option>Russian Federation</option>
    <option>Rwanda</option>
    <option>Saint Barthélemy</option>
    <option>Saint Helena, Ascension and Tristan da Cunha</option>
    <option>Saint Kitts and Nevis</option>
    <option>Saint Lucia</option>
    <option>Saint Martin (French part)</option>
    <option>Saint Pierre and Miquelon</option>
    <option>Saint Vincent and the Grenadines</option>
    <option>Samoa</option>
    <option>San Marino</option>
    <option>Sao Tome and Principe</option>
    <option>Saudi Arabia</option>
    <option>Senegal</option>
    <option>Serbia</option>
    <option>Seychelles</option>
    <option>Sierra Leone</option>
    <option>Singapore</option>
    <option>Sint Maarten (Dutch part)</option>
    <option>Slovakia</option>
    <option>Slovenia</option>
    <option>Solomon Islands</option>
    <option>Somalia</option>
    <option>South Africa</option>
    <option>South Georgia and the South Sandwich Islands</option>
    <option>South Sudan</option>
    <option>Spain</option>
    <option>Sri Lanka</option>
    <option>Sudan</option>
    <option>Suriname</option>
    <option>Svalbard and Jan Mayen</option>
    <option>Sweden</option>
    <option>Switzerland</option>
    <option>Syrian Arab Republic</option>
    <option>Taiwan, Province of China</option>
    <option>Tajikistan</option>
    <option>Tanzania, United Republic of</option>
    <option>Thailand</option>
    <option>Timor-Leste</option>
    <option>Togo</option>
    <option>Tokelau</option>
    <option>Tonga</option>
    <option>Trinidad and Tobago</option>
    <option>Tunisia</option>
    <option>Turkey</option>
    <option>Turkmenistan</option>
    <option>Turks and Caicos Islands</option>
    <option>Tuvalu</option>
    <option>Uganda</option>
    <option>Ukraine</option>
    <option>United Arab Emirates</option>
    <option>United Kingdom of Great Britain and Northern Ireland</option>
    <option>United States Minor Outlying Islands</option>
    <option>United States of America</option>
    <option>Uruguay</option>
    <option>Uzbekistan</option>
    <option>Vanuatu</option>
    <option>Venezuela (Bolivarian Republic of)</option>
    <option>Viet Nam</option>
    <option>Virgin Islands (British)</option>
    <option>Virgin Islands (U.S.)</option>
    <option>Wallis and Futuna</option>
    <option>Western Sahara</option>
    <option>Yemen</option>
    <option>Zambia</option>
    <option>Zimbabwe</option>
  </datalist>

  <script src="../latest/release/go.js"></script>
  <script id="code">

// Define a custom text editor, an instance of go.HTMLInfo,
// that uses an <input> with a <datalist>.

const TextEditor = new go.HTMLInfo();
const _editor = document.createElement('div');
const _input = document.createElement('input');
_editor.appendChild(_input);
_input.setAttribute("list", "CountryDatalist");
_input.setAttribute("autocomplete", "off");
_input.style["width"] = "200px";
_input.addEventListener('keydown', (e) => {
    if (e.isComposing)
        return;
    const tool = TextEditor.tool;
    if (tool.textBlock === null)
        return;
    const key = e.key;
    if (key === 'Enter') {
        // Enter
        if (tool.textBlock.isMultiline === false)
            e.preventDefault();
        tool.acceptText(go.TextEditingAccept.Enter);
    }
    else if (key === 'Tab') {
        // Tab
        tool.acceptText(go.TextEditingAccept.Tab);
        e.preventDefault();
    }
    else if (key === 'Escape') {
        // Esc
        tool.doCancel();
        if (tool.diagram !== null)
            tool.diagram.doFocus();
    }
}, false);
// handle focus:
_input.addEventListener('focus', (e) => {
    const tool = TextEditor.tool;
    if (!tool ||
        tool.currentTextEditor === null ||
        tool.state === go.TextEditingState.None)
        return;
    if (tool.state === go.TextEditingState.Active) {
        tool.state = go.TextEditingState.Editing;
    }
}, false);
// Disallow blur.
// If the textEditingTool blurs and the text is not valid,
// we do not want focus taken off the element just because a user clicked elsewhere.
_input.addEventListener('blur', (e) => {
    const tool = TextEditor.tool;
    if (!tool ||
        tool.currentTextEditor === null ||
        tool.state === go.TextEditingState.None)
        return;
    _input.focus();
}, false);
TextEditor.valueFunction = () => _input.value;
TextEditor.mainElement = _editor; // to reference it more easily
TextEditor.tool = null; // Initialize
// used to be in doActivate
TextEditor.show = (textBlock, diagram, tool) => {
    if (!diagram || !diagram.div) return;
    if (!(textBlock instanceof go.TextBlock)) return;
    if (TextEditor.tool !== null) return; // Only one at a time.
    TextEditor.tool = tool; // remember the TextEditingTool for use by listeners
    // This is called during validation, if validation failed:
    if (tool.state === go.TextEditingState.Invalid) {
        _input.style.border = '3px solid red';
        _input.focus();
        return;
    }
    // This part is called during initalization:
    const loc = textBlock.getDocumentPoint(go.Spot.TopLeft);
    const pos = diagram.position;
    const sc = diagram.scale;
    let textscale = textBlock.getDocumentScale() * sc;
    if (textscale < tool.minimumEditorScale)
        textscale = tool.minimumEditorScale;
    // Add slightly more width/height to stop scrollbars and line wrapping on some browsers
    // +6 is firefox minimum, otherwise lines will be wrapped improperly
    const left = (loc.x - pos.x) * sc;
    const top = (loc.y - pos.y) * sc;
    _input.value = textBlock.text;
    // the only way you can mix font and fontSize is if the font inherits and the fontSize overrides
    // in the future maybe have input contained in its own div
    diagram.div.style['font'] = textBlock.font;
    const paddingsize = 1;
    _editor.style['position'] = 'absolute';
    _editor.style['zIndex'] = '100';
    _input.style['font'] = 'inherit';
    _input.style['fontSize'] = textscale * 100 + '%';
    _editor.style['left'] = (left | 0) - paddingsize + 'px';
    _editor.style['top'] = (top | 0) - paddingsize + 'px';
    // Show:
    diagram.div.appendChild(_editor);
    // After adding, focus:
    _input.focus();
};
TextEditor.hide = (diagram, tool) => {
    TextEditor.tool = null; // forget reference to TextEditingTool
    if (diagram.div) diagram.div.removeChild(_editor);
};


// Set up the example Diagram and model

const myDiagram =
  new go.Diagram("myDiagramDiv",
    {
      "undoManager.isEnabled": true,
      "ModelChanged": e => {     // just for demonstration purposes,
        if (e.isTransactionFinished) {  // show the model data in the page's Input
          document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      }
    });

myDiagram.nodeTemplate =
  new go.Node("Auto")
    .add(
      new go.Shape({ fill: "white" })
        .bind("fill", "color"),
      new go.TextBlock({ margin: 8, editable: true, textEditor: TextEditor, isMultiline: false })
        .bindTwoWay("text")
    );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "Austria", color: "lightblue" },
  { key: 2, text: "Belgium", color: "orange" },
  { key: 3, text: "Germany", color: "lightgreen" },
  { key: 4, text: "Denmark", color: "pink" }
],
[
  { from: 1, to: 2 },
  { from: 1, to: 3 },
  { from: 2, to: 2 },
  { from: 3, to: 4 },
  { from: 4, to: 1 }
]);
  </script>
</body>
</html>

Is it possible to modify the looks of the scrollbar which is implemented using scrolling table.js?
By editing or adding some code

Yes, you have the complete source code there, so you can modify it to suit your specific needs.

By the way, I found a bug in the “ScrollingTable” implementation that causes it not to work well in an Adornment. It has been fixed for the next release, 3.0.11, which will come out soon.

I am not able to add the custom editor in the selection dropdown,
this is my code,

 private ChoicesAdornment =
    new go.Adornment("Spot")
      .add(
        new go.Placeholder(),  // placeholder for the TextBlock in the Node
        new go.Panel("Auto", { alignment: go.Spot.BottomLeft, alignmentFocus: go.Spot.TopLeft,})
          .add(
            $(go.Shape, { fill: 'white', stroke: '#E0E0E0' }),
            $(go.Panel, 'Vertical', { name: 'somePanel', margin: 5, background: 'white' },
              this.searchPanel(150),
              $("ScrollingTable",
                {
                  name: 'SCROLLER',
                  stretch: go.GraphObject.Fill,
                  desiredSize: new go.Size(165, 200),
                },
                new go.Binding("TABLE.itemArray", "choices"),
                new go.Binding("SCROLLBAR.visible", "choices", arr => arr.length > 4),
                {
                  "TABLE.itemTemplate":
                    $(go.Panel, 'Auto',
                      {
                        cursor: 'pointer',
                        isActionable: true,  // to allow a click event in an Adornment
                        click: (e: any, item: any) => {
                          const tb = item.part.adornedPart.findObject("TB");
                          if (!tb) return;
                          e.diagram.commit((diag: any) => {
                            tb.text = item.data;
                            this.showChoices(null);
                          });
                        },
                        // for mouse-over highlighting
                        background: "white",
                        mouseEnter: (e: any, item: any) => item.background = "#DBDBDB",
                        mouseLeave: (e: any, item: any) => item.background = "white"
                      },
                      new go.Shape("Rectangle", { fill: "transparent", stroke: "gray", strokeWidth: 0.5, width: 155, height: 35 }),
                      new go.TextBlock({ height: 35, width: 150, verticalAlignment: go.Spot.Center, margin: new go.Margin(0, 0, 0, 0) })
                        .bind("text", "")  // TextBlock.text gets the whole Array item value
                    )
                }
              )
            )
          ),
      );

 private searchPanel(searchTextBoxWidth: number = 250, showSelectAll: boolean = false): go.Panel {
    return $(go.Panel, 'Auto', { name: "abc" },
      $(go.Panel, { name: 'searchPanel' }, 'Auto',
        $(go.Panel, 'Auto',
          $(go.Shape, { fill: 'white', stroke: '#E0E0E0', strokeWidth: 2 }),
          $(go.TextBlock,
            {
              name: 'searchTechnologyText',
              margin: new go.Margin(0, 0, 0, 4),
              editable: true,
              maxLines: 1,
              font: 'Italic 1.1rem verdana',
              width: searchTextBoxWidth,
              background: 'white',
              stretch: go.GraphObject.Fill,
              stroke: '#DBDBDB',
              height: 40,
              textEditor: this.searchCustomEditor(searchTextBoxWidth),
              verticalAlignment: go.Spot.Center,
              text: "Search...",
            },
          ),
        )
      )
    );
  }

if i am adding searchPanel() any other place it’s working fine, but only in the adornmant part i am not able to edit the textblock

That should work. Here’s an example. Notice that when the choices are shown, it automatically starts editing the filter text.

However, you will need to implement a custom text editor (which I have not done in this example) so that as the user types each choice in the list is made visible or not.

I still think that implementing this in HTML, as I have demonstrated above, is an easier implementation approach than having to reimplement everything in GoJS.

<!DOCTYPE html>
<html>
<head>
  <title>Simple Choices Selector Adornment</title>
  <!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
  <textarea id="mySavedModel" style="width:100%;height:150px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <script src="https://unpkg.com/create-gojs-kit/dist/extensions/ScrollingTable.js"></script>
  <script id="code">
const myDiagram =
  new go.Diagram("myDiagramDiv", {
      "BackgroundSingleClicked": e => showChoices(null),
      "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();
        }
      }
    });

// the Adornment holding the list of choices
const ChoicesAdornment =
  new go.Adornment("Spot")
    .add(
      new go.Placeholder(),  // placeholder for the TextBlock in the Node
      new go.Panel("Vertical", {
          alignment: go.Spot.BottomLeft,
          alignmentFocus: go.Spot.TopLeft,
          defaultStretch: go.Stretch.Horizontal
        })
        .add(
          new go.Panel("Auto")
            .add(
              new go.Shape({ fill: "lightyellow" }),
              new go.TextBlock("filter", {
                name: "FILTER",
                stretch: go.Stretch.Horizontal,
                margin: new go.Margin(2, 2, 1, 2),
                font: "italic 10pt sans-serif",
                editable: true
              })
            ),
          new go.Panel("Auto")
            .add(
              new go.Shape({ fill: "white", stroke: "gray", strokeWidth: 2 }),
              go.GraphObject.build("ScrollingTable", {
                  maxSize: new go.Size(NaN, 65),
                  stretch: go.Stretch.Fill,
                  "TABLE.rowSizing": go.Sizing.None,
                  "TABLE.itemTemplate":
                    new go.Panel("TableRow", {
                        isActionable: true,  // to allow a click event in an Adornment
                        click: (e, item) => {
                          const tb = item.part.adornedPart.findObject("TB");
                          if (!tb) return;
                          e.diagram.commit(diag => {
                            tb.text = item.data;
                            showChoices(null);
                          });
                        },
                        // for mouse-over highlighting
                        background: "transparent",
                        mouseEnter: (e, item) => item.background = "cyan",
                        mouseLeave: (e, item) => item.background = "transparent"
                      })
                      .add(
                        new go.TextBlock({ margin: 1 })
                          .bind("text", "")  // TextBlock.text gets the whole Array item value
                      )
                })
                .bind("TABLE.itemArray", "choices")
                .bind("SCROLLBAR.visible", "choices", arr => arr.length > 6)
              )
        )
    );

function showChoices(node) {
  myDiagram.commit(diag => {
    if (!node) {
      const ad = ChoicesAdornment;
      const oldnode = ad.adornedPart;
      if (oldnode) {
        const oldshp = oldnode.findObject("SHP");
        if (oldshp) oldshp.figure = "LineDown";
        oldnode.removeAdornment("Choices");
      }
    } else {
      const tb = node.findObject("TB");
      const shp = node.findObject("SHP");
      if (!tb || !shp) return;
      showChoices(null);  // remove from any other node
      shp.figure = "LineUp";
      const ad = ChoicesAdornment;
      ad.adornedObject = tb;
      node.addAdornment("Choices", ad);
      if (!Array.isArray(node.data.choices) || node.data.choices.length === 0) {
        ad.data = { choices: ["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta"] };  // default choices Array
      }
      setTimeout(() => {
        const filter = ad.findObject("FILTER");
        if (filter) diag.commandHandler.editTextBlock(filter);
      }, 100);
    }
  }, null);  // skipsUndoManager
}

myDiagram.nodeTemplate =
  new go.Node("Auto")
    .add(
      new go.Shape({ fill: "white" }),
      new go.Panel("Vertical")
        .add(
          new go.TextBlock({ margin: 4, font: "bold 12pt sans-serif" })
            .bind("text", "title"),
          new go.Panel("Horizontal", {
              click: (e, pnl) => {  // show or hide the ChoicesAdornment
                e.handled = true;
                showChoices((pnl.findObject("SHP").figure === "LineDown") ? pnl.part : null);
              },
              // for mouse-over highlighting
              background: "transparent",
              mouseEnter: (e, pnl) => pnl.background = "lightgray",
              mouseLeave: (e, pnl) => pnl.background = "transparent"
            })
            .add(
              new go.TextBlock("(choose)", {
                  name: "TB",
                  width: 100,
                  margin: new go.Margin(4, 4, 2, 4),
                  font: "italic 10pt sans-serif",
                  stroke: "blue"
                })
                .bindTwoWay("text", "value")
                .bind("font", "value", v => v ? "bold 10pt sans-serif" : "italic 10pt sans-serif")
                .bind("stroke", "value", v => v ? "black" : "blue"),
              new go.Shape("LineDown", {
                name: "SHP",
                width: 14, height: 12,
                margin: 2,
                strokeWidth: 2,
              })
            )
        )
    );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, title: "Alpha", choices: ["one", "two", "three", "four", "five", "six", "seven"], value: "one" },
  { key: 2, title: "Beta", choices: ["hello", "goodbye"] },
  { key: 3, title: "Gamma", choices: ["only one"] },
  { key: 4, title: "Delta" },  // use a default choices Array
]);
  </script>
</body>
</html>

If I implement it using html then i have to click two times on the node to open the dropdown, is there any way to open the dropdown in the one click only?

Maybe with your own custom element. But as I mentioned in one of your prior threads, that can’t be done with a <select> element as they can’t be expanded programmatically.

See my example from this thread: Selection dropdown menu

Here’s the final sample that I’ll give you involving this issue. I’ve customized the text editor used for the editable “FILTER” TextBlock in the ChoicesAdornment so that as the user types, each of the list’s item panels are made visible or not depending on whether their TextBlock.text string includes the filter string.

Note that when that “FILTER” TextBlock is being edited, the user has to leave the text editor before the user can do anything else in the diagram, such as selecting a choice, scrolling the choices list, selecting another node, or even just hiding the ChoicesAdornment.

<!DOCTYPE html>
<html>
<head>
  <title>Simple Choices Selector Adornment</title>
  <!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
  <textarea id="mySavedModel" style="width:100%;height:150px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <!-- <script src="https://unpkg.com/create-gojs-kit/dist/extensions/ScrollingTable.js"></script> -->
  <script src="../latest/extensions/ScrollingTable.js"></script>
  <script id="code">
// This is copied and adapted from extensions/TextEditor.js
// in order to implement filtering in the ChoicesAdornment in this sample.
// See the additional code in the textarea's "input" listener.

/*
 *  Copyright (C) 1998-2024 by Northwoods Software Corporation. All Rights Reserved.
 */
// This is the definitions of the predefined text editor used by TextEditingTool
// when you set or bind TextBlock.editable to true.
// You do not need to load this file in order to use in-place text editing.

// HTML + JavaScript text editor menu, made with HTMLInfo
// This is a re-implementation of the default text editor
// This file exposes one instance of HTMLInfo, window.TextEditor
// Typical usage is:
// ```js
//   new go.Diagram(...,
//      {
//        'textEditingTool.defaultTextEditor': window.TextEditor,
//        . . .
//      })
// ```
// or:
// ```js
//    myDiagram.toolManager.textEditingTool.defaultTextEditor = window.TextEditor;
// ```
// ```js
//   $(go.Node, . . .,
//     . . .
//       $(go.TextBlock, { textEditor: window.TextEditor, . . . })
//     . . .
//   )
// ```
// If you do use this code, copy it into your project and modify it there.
// See also TextEditor.html
((window) => {
    const TextEditor = new go.HTMLInfo();
    const textarea = document.createElement('textarea');
    textarea.id = 'myTextArea';
    textarea.addEventListener('input', (e) => {
        const tool = TextEditor.tool;
        if (tool.textBlock === null)
            return;
        const tempText = tool.measureTemporaryTextBlock(textarea.value);
        const scale = textarea.textScale;
        textarea.style.width =
            20 +
                Math.max(tool.textBlock.measuredBounds.width, tempText.measuredBounds.width) * scale +
                'px';
        textarea.rows = Math.max(tool.textBlock.lineCount, tempText.lineCount);

        // filter the choices list held in the "TABLE" panel of the Adornment
        const ad = tool.textBlock.part;
        const table = ad.findObject("TABLE");
        if (!table) return;
        const filter = textarea.value.toLowerCase();
        table.elements.each(itempanel => {
          const itemtext = itempanel.elt(0).text;
          itempanel.visible = itemtext.toLowerCase().includes(filter);
        });
        const scroller = ad.findObject("SCROLLER");
        if (scroller && scroller._updateScrollBar) scroller._updateScrollBar(table);
    }, false);
    textarea.addEventListener('keydown', (e) => {
        if (e.isComposing)
            return;
        const tool = TextEditor.tool;
        if (tool.textBlock === null)
            return;
        const code = e.code;
        if (code === 'Enter') {
            // Enter
            if (tool.textBlock.isMultiline === false)
                e.preventDefault();
            tool.acceptText(go.TextEditingAccept.Enter);
        }
        else if (code === 'Tab') {
            // Tab
            tool.acceptText(go.TextEditingAccept.Tab);
            e.preventDefault();
        }
        else if (code === 'Escape') {
            // Esc
            tool.doCancel();
            if (tool.diagram !== null)
                tool.diagram.doFocus();
        }
    }, false);
    // handle focus:
    textarea.addEventListener('focus', (e) => {
        const tool = TextEditor.tool;
        if (!tool || tool.currentTextEditor === null || tool.state === go.TextEditingState.None)
            return;
        if (tool.state === go.TextEditingState.Active) {
            tool.state = go.TextEditingState.Editing;
        }
        if (tool.selectsTextOnActivate) {
            textarea.select();
            textarea.setSelectionRange(0, 9999);
        }
    }, false);
    // Disallow blur.
    // If the textEditingTool blurs and the text is not valid,
    // we do not want focus taken off the element just because a user clicked elsewhere.
    textarea.addEventListener('blur', (e) => {
        const tool = TextEditor.tool;
        if (!tool || tool.currentTextEditor === null || tool.state === go.TextEditingState.None)
            return;
        textarea.focus();
        if (tool.selectsTextOnActivate) {
            textarea.select();
            textarea.setSelectionRange(0, 9999);
        }
    }, false);
    TextEditor.valueFunction = () => textarea.value;
    TextEditor.mainElement = textarea; // to reference it more easily
    TextEditor.tool = null; // Initialize
    // used to be in doActivate
    TextEditor.show = (textBlock, diagram, tool) => {
        if (!diagram || !diagram.div)
            return;
        if (!(textBlock instanceof go.TextBlock))
            return;
        if (TextEditor.tool !== null)
            return; // Only one at a time.
        TextEditor.tool = tool; // remember the TextEditingTool for use by listeners
        // This is called during validation, if validation failed:
        if (tool.state === go.TextEditingState.Invalid) {
            textarea.style.border = '3px solid red';
            textarea.focus();
            return;
        }
        // This part is called during initalization:
        const loc = textBlock.getDocumentPoint(go.Spot.Center);
        const pos = diagram.position;
        const sc = diagram.scale;
        let textscale = textBlock.getDocumentScale() * sc;
        if (textscale < tool.minimumEditorScale)
            textscale = tool.minimumEditorScale;
        // Add slightly more width/height to stop scrollbars and line wrapping on some browsers
        // +6 is firefox minimum, otherwise lines will be wrapped improperly
        const textwidth = textBlock.naturalBounds.width * textscale + 6;
        const textheight = textBlock.naturalBounds.height * textscale + 2;
        const left = (loc.x - pos.x) * sc;
        const yCenter = (loc.y - pos.y) * sc; // this is actually the center, used to set style.top
        const valign = textBlock.verticalAlignment;
        const oneLineHeight = textBlock.lineHeight + textBlock.spacingAbove + textBlock.spacingBelow;
        const allLinesHeight = oneLineHeight * textBlock.lineCount * textscale;
        const center = 0.5 * textheight - 0.5 * allLinesHeight;
        // add offset to yCenter to get the appropriate position:
        const yOffset = valign.y * textheight -
            valign.y * allLinesHeight +
            valign.offsetY -
            center -
            allLinesHeight / 2;
        textarea.value = textBlock.text;
        // the only way you can mix font and fontSize is if the font inherits and the fontSize overrides
        // in the future maybe have textarea contained in its own div
        diagram.div.style['font'] = textBlock.font;
        const paddingsize = 1;
        textarea.style['position'] = 'absolute';
        textarea.style['zIndex'] = '100';
        textarea.style['font'] = 'inherit';
        textarea.style['fontSize'] = textscale * 100 + '%';
        textarea.style['lineHeight'] = 'normal';
        textarea.style['width'] = textwidth + 'px';
        textarea.style['left'] = ((left - textwidth / 2) | 0) - paddingsize + 'px';
        textarea.style['top'] = ((yCenter + yOffset) | 0) - paddingsize + 'px';
        textarea.style['textAlign'] = textBlock.textAlign;
        textarea.style['margin'] = '0';
        textarea.style['padding'] = paddingsize + 'px';
        textarea.style['border'] = '0';
        textarea.style['outline'] = 'none';
        textarea.style['whiteSpace'] = 'pre-wrap';
        textarea.style['overflow'] = 'hidden'; // for proper IE wrap
        textarea.rows = textBlock.lineCount;
        textarea.textScale = textscale; // attach a value to the textarea, for convenience
        textarea.className = 'goTXarea';
        // Show:
        diagram.div.appendChild(textarea);
        // After adding, focus:
        textarea.focus();
        if (tool.selectsTextOnActivate) {
            textarea.select();
            textarea.setSelectionRange(0, 9999);
        }
    };
    TextEditor.hide = (diagram, tool) => {
        TextEditor.tool = null; // forget reference to TextEditingTool
        if (diagram.div)
            diagram.div.removeChild(textarea);
    };
    window.TextEditor = TextEditor;
})(window);


const myDiagram =
  new go.Diagram("myDiagramDiv", {
      "BackgroundSingleClicked": e => {
        if (ChoicesAdornment.diagram &&
            ChoicesAdornment.actualBounds.containsPoint(e.diagram.lastInput.documentPoint)) return;
        showChoices(null)
      },
      "textEditingTool.starting": go.TextEditingStarting.SingleClick,
      "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();
        }
      }
    });

// the Adornment holding the list of choices
const ChoicesAdornment =
  new go.Adornment("Spot")
    .add(
      new go.Placeholder(),  // placeholder for the TextBlock in the Node
      new go.Panel("Auto", {
          alignment: go.Spot.BottomLeft,
          alignmentFocus: go.Spot.TopLeft,
          stretch: go.Stretch.Horizontal
        })
        .add(
          // border around whole filter + list
          new go.Shape({ fill: null, stroke: "lightgray", strokeWidth: 4 }),
          new go.Panel("Vertical", {
              stretch: go.Stretch.Horizontal,
              defaultStretch: go.Stretch.Horizontal
            })
            .add(
              new go.Panel("Auto")
                .add(
                  new go.Shape({ fill: "lightyellow" }),
                  new go.TextBlock("filter", {
                    name: "FILTER",
                    stretch: go.Stretch.Horizontal,
                    margin: new go.Margin(2, 2, 1, 2),
                    font: "italic 10pt sans-serif",
                    isMultiline: false,
                    editable: true,
                    textEditor: window.TextEditor
                  })
                ),
              new go.Panel("Auto")
                .add(
                  new go.Shape({ fill: "white", stroke: "gray", strokeWidth: 2 }),
                  go.GraphObject.build("ScrollingTable", {
                      name: "SCROLLER",
                      maxSize: new go.Size(NaN, 65),
                      stretch: go.Stretch.Fill,
                      "TABLE.rowSizing": go.Sizing.None,
                      "TABLE.itemTemplate":
                        new go.Panel("TableRow", {
                            isActionable: true,  // to allow a click event in an Adornment
                            click: (e, item) => {
                              if (e.diagram.currentTool instanceof go.TextEditingTool) {
                                e.diagram.currentTool.stopTool();
                              }
                              const tb = item.part.adornedPart.findObject("TB");
                              if (!tb) return;
                              e.diagram.commit(diag => {
                                tb.text = item.data;
                                showChoices(null);
                              });
                            },
                            // for mouse-over highlighting
                            background: "transparent",
                            mouseEnter: (e, item) => item.background = "cyan",
                            mouseLeave: (e, item) => item.background = "transparent"
                          })
                          .add(
                            new go.TextBlock({ margin: 1 })
                              .bind("text", "")  // TextBlock.text gets the whole Array item value
                          )
                    })
                    .bind("TABLE.itemArray", "choices")
                    .bind("SCROLLBAR.visible", "choices", arr => arr.length > 6)
                  )
            )
        )
    );

function showChoices(node) {
  const ad = ChoicesAdornment;
  const currentnode = ad.adornedPart;
  // if ChoicesAdornment is shown, when no node or when toggling visibility on currentnode,
  // remove the ChoicesAdornment
  if (currentnode && (!node || currentnode === node)) {
    const oldshp = currentnode.findObject("SHP");
    if (oldshp) oldshp.figure = "LineDown";
    currentnode.removeAdornment("Choices");
    ad.adornedObject = null;
    if (currentnode === node) return;
    ad._key = undefined;
  }
  if (!node) return;
  // maybe showing on a different node, so any filter no longer applies
  if (ad._key !== node.key) {
    const filter = ad.findObject("FILTER");
    if (filter) filter.text = "";
    const choices = ad.findObject("TABLE");
    if (choices) {
      choices.itemArray = [];
      choices.topIndex = 0;
    }
  }
  const tb = node.findObject("TB");
  const shp = node.findObject("SHP");
  if (!tb || !shp) return;
  shp.figure = "LineUp";
  ad.adornedObject = tb;
  node.addAdornment("Choices", ad);
  // if there's no "choices" data Array, use a default one
  if (!Array.isArray(node.data.choices) || node.data.choices.length === 0) {
    ad.data = { choices: ["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta"] };  // default choices Array
  }
  // remember which node it last showed for
  ad._key = node.key;
}

myDiagram.nodeTemplate =
  new go.Node("Auto")
    .add(
      new go.Shape({ fill: "white" }),
      new go.Panel("Vertical")
        .add(
          new go.TextBlock({ margin: 4, font: "bold 12pt sans-serif" })
            .bind("text", "title"),
          new go.Panel("Horizontal", {
              click: (e, pnl) => {  // show or hide the ChoicesAdornment
                e.handled = true;
                showChoices(pnl.part);
              },
              // for mouse-over highlighting
              background: "transparent",
              mouseEnter: (e, pnl) => pnl.background = "lightgray",
              mouseLeave: (e, pnl) => pnl.background = "transparent"
            })
            .add(
              new go.TextBlock("(choose)", {
                  name: "TB",
                  width: 100,
                  margin: new go.Margin(4, 4, 2, 4),
                  font: "italic 10pt sans-serif",
                  stroke: "blue"
                })
                .bindTwoWay("text", "value")
                .bind("font", "value", v => v ? "bold 10pt sans-serif" : "italic 10pt sans-serif")
                .bind("stroke", "value", v => v ? "black" : "blue"),
              new go.Shape("LineDown", {
                name: "SHP",
                width: 14, height: 12,
                margin: 2,
                strokeWidth: 2,
              })
            )
        )
    );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, title: "Alpha", choices: ["one", "two", "three", "four", "five", "six", "seven"], value: "one" },
  { key: 2, title: "Beta", choices: ["hello", "goodbye"] },
  { key: 3, title: "Gamma", choices: ["only one"] },
  { key: 4, title: "Delta" },  // use a default choices Array
]);
  </script>
</body>
</html>