create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
978 lines (918 loc) • 39.1 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="../assets/css/style.css">
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta itemprop="name" content="Buttons" />
<meta property="og:title" content="Buttons" />
<meta name="twitter:title" content="Buttons" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" />
<meta property="og:url" content="https://gojs.net/latest/intro/buttons.html" />
<meta property="twitter:url" content="https://gojs.net/latest/intro/buttons.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Buttons | GoJS
</title>
<link rel="stylesheet" href="../assets/css/prism.css"/>
</head>
<script>
window.diagrams = [];
window.goCode = function (pre, w, h, parentid, animation) {
window
.diagrams
.push([pre, w, h, parentid, animation]);
}
</script>
<body>
<nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200">
<div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4">
<a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo"
href="../">
GoJS
</a>
<div class="relative">
<button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div id="topnavList" class="hidden md:block">
<div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold
md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit">
<a href="../learn/">Learn</a>
<a href="../samples/">Samples</a>
<a href="../intro/">Intro</a>
<a href="../api/">API</a>
<a href="../download.html">Download</a>
<a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a>
<a id="tc" href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
<a id="tb" href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</div>
</div>
</div>
</div>
</nav>
<script>
window.addEventListener("DOMContentLoaded", function () {
// topnav
var topButton = document.getElementById("topnavButton");
var topnavList = document.getElementById("topnavList");
if (topButton && topnavList) {
topButton.addEventListener("click", function (e) {
topnavList
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) {
topButton.click();
}
});
// set active <a> element
var url = window
.location
.href
.toLowerCase();
var aTags = topnavList.getElementsByTagName('a');
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.endsWith('.html'))
lowerhref = lowerhref.slice(0, -5);
if (url.startsWith(lowerhref)) {
aTags[i]
.classList
.add('active');
break;
}
}
}
});
</script>
<div class="sticky top-0 left-0 z-10 px-2 w-full bg-white border-b border-b-gray-200 md:hidden">
<button id="sidenavButton" class="flex p-2 text-gray-900 bg-inherit shadow-none items-center text-sm font-semibold hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block mr-2" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Menu</span>
</button>
</div>
<script>
window.addEventListener("DOMContentLoaded", function () {
// sidenav
var sideButton = document.getElementById("sidenavButton");
var sidenav = document.getElementById("sidenav");
if (sideButton && sidenav) {
sideButton.addEventListener("click", function (e) {
sidenav
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!sidenav.classList.contains("hidden") && !e.target.closest("#sidenavList")) {
sideButton.click();
}
});
}
});
</script>
<div class="flex flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
<aside id="sidenav"
class="hidden fixed top-0 left-0 z-10 w-full bg-black/10 min-h-screen max-h-screen overflow-x-hidden overflow-y-auto shrink-0
md:block md:sticky md:w-52 md:min-h-0 md:bg-inherit md:border-r md:border-r-gray-200 md:overscroll-auto">
<nav id="sidenavList" class="flex flex-col bg-white w-52 min-h-screen pl-2 pt-4 pb-24 md:w-full md:min-h-0">
<a href="index.html">Basics</a>
<a href="buildingObjects.html">Building Parts</a>
<a href="usingModels.html">Using Models</a>
<a href="dataBinding.html">Data Binding</a>
<a href="react.html">GoJS with React</a>
<a href="svelte.html">GoJS with Svelte</a>
<a href="angular.html">GoJS with Angular</a>
<a href="textBlocks.html">TextBlocks</a>
<a href="shapes.html">Shapes</a>
<a href="pictures.html">Pictures</a>
<a href="panels.html">Panels</a>
<a href="tablePanels.html">Table Panels</a>
<a href="brush.html">Brushes</a>
<a href="sizing.html">Sizing Objects</a>
<a href="itemArrays.html">Item Arrays</a>
<a href="changedEvents.html">Changed Events</a>
<a href="transactions.html">Transactions</a>
<a href="viewport.html">Coordinates</a>
<a href="initialView.html">Initial View</a>
<a href="collections.html">Collections</a>
<a href="links.html">Links</a>
<a href="linkLabels.html">Link Labels</a>
<a href="connectionPoints.html">Link Points</a>
<a href="ports.html">Ports</a>
<a href="nodes.html">Nodes</a>
<a href="typings.html">Typings</a>
<a href="debugging.html">Debugging</a>
<a href="layouts.html">Layouts</a>
<a href="routers.html">Routers</a>
<a href="trees.html">Trees</a>
<a href="subtrees.html">SubTrees</a>
<a href="groups.html">Groups</a>
<a href="subgraphs.html">SubGraphs</a>
<a href="sizedGroups.html">Sized Groups</a>
<a href="selection.html">Selection</a>
<a href="highlighting.html">Highlighting</a>
<a href="theming.html">Theming</a>
<a href="tooltips.html">ToolTips</a>
<a href="contextMenus.html">Context Menus</a>
<a href="events.html">Diagram Events</a>
<a href="tools.html">Tools</a>
<a href="commands.html">Commands</a>
<a href="accessibility.html">Accessibility</a>
<a href="buttons.html">Buttons</a>
<a href="permissions.html">Permissions</a>
<a href="validation.html">Validation</a>
<a href="animation.html">Animation</a>
<a href="HTMLInteraction.html">HTML Interaction</a>
<a href="layers.html">Layers & Z-ordering</a>
<a href="palette.html">Palette</a>
<a href="overview.html">Overview</a>
<a href="replacingDeleting.html">Replacing and Deleting</a>
<a href="templateMaps.html">Template Maps</a>
<a href="legends.html">Legends and Titles</a>
<a href="extensions.html">Extensions</a>
<a href="geometry.html">Geometry Strings</a>
<a href="grids.html">Grid Patterns</a>
<a href="graduatedPanels.html">Graduated Panels</a>
<a href="SVGContext.html">Rendering to SVG</a>
<a href="makingSVG.html">Snapshot to SVG</a>
<a href="makingImages.html">Diagram Images</a>
<a href="printing.html">Printing</a>
<a href="serverSideImages.html">Server-side Images</a>
<a href="nodeScript.html">GoJS in Node.js</a>
<a href="testing.html">Testing</a>
<a href="performance.html">Performance</a>
<a href="platforms.html">Platforms</a>
<a href="deployment.html">Deployment</a>
</nav>
</aside>
<script>
var navList = document.getElementById('sidenavList');
if (navList !== null) {
var url = window.location.href;
var lindex = url.lastIndexOf('/');
url = url
.slice(lindex + 1)
.toLowerCase();
var aTags = navList.getElementsByTagName('a');
var currentindex = -1;
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.indexOf('/' + url) !== -1) {
currentindex = i;
aTags[i]
.classList
.add('active');
break;
}
}
}
</script>
<div class="px-4 pb-16 w-full overflow-hidden prose">
<h1>Buttons</h1>
<p>
For your convenience we have defined several <a>Panel</a>s for common uses.
These include "Button", "TreeExpanderButton", "SubGraphExpanderButton", "PanelExpanderButton", "ContextMenuButton",
"CheckBoxButton", "ToggleSwitch", and "AutoRepeatButton".
"ContextMenuButton"s are typically used inside of "ContextMenu" Panels;
"CheckBoxButton"s are used in the implementation of "CheckBox" Panels.
"ToggleSwitch"s are used in the implementation of "Toggle" Panels.
</p>
<p>
These predefined panels can be used as if they were <a>Panel</a>-derived classes
in calls to <a>GraphObject.build</a>.
They are implemented as simple visual trees of <a>GraphObject</a>s in <a>Panel</a>s,
with pre-set properties and event handlers.
</p>
<p>
You can see a copy of their definitions in this file:
<a href="../extensions/Buttons.js">Buttons.js</a>.
</p>
<p>
See samples that make use of buttons in the <a href="../samples/index.html#buttons">samples index</a>.
In addition, see the <a href="../samples/Checkboxes.html">Checkboxes</a> extension for an example of using
"CheckBoxButton".
</p>
<h2 id="GeneralButtons">General Buttons</h2>
<p>
The most general kind of predefined <a>Panel</a> is "Button".
</p>
<pre class="lang-js" id="button"><code>
diagram.nodeTemplate =
new go.Node("Auto", { locationSpot: go.Spot.Center })
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.Panel("Vertical", { margin: 3 })
.add(
go.GraphObject.build("Button", { margin: 2, click: incrementCounter })
.add(
new go.TextBlock("Click me!")
),
new go.TextBlock()
.bind("text", "clickCount", c => "Clicked " + c + " times.")
)
);
function incrementCounter(e, obj) {
const node = obj.part;
const data = node.data;
if (data && typeof(data.clickCount) === "number") {
node.diagram.model.commit(m => m.set(data, "clickCount", data.clickCount + 1), "clicked");
}
}
diagram.model = new go.GraphLinksModel([ { clickCount: 0 } ]);
</code></pre>
<script>goCode("button", 600, 150)</script>
<p>
Buttons are just Panels holding a Shape that will surround whatever content you give it.
The border Shape is named "ButtonBorder" so that you can easily set or bind its properties.
</p>
<p>
The event handlers defined by all "Button"s make use of additional properties,
not defined in the API, but that you can see in the definition of "Button":
<a href="../extensions/Buttons.js">Buttons.js</a>.
These properties parameterize the appearance of the button.
</p>
<pre class="lang-js" id="button2"><code>
diagram.nodeTemplate =
new go.Node("Auto", { locationSpot: go.Spot.Center })
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.Panel("Vertical", { margin: 3 })
.add(
go.GraphObject.build("Button", {
margin: 2,
click: (e, button) => alert(button.findObject("ButtonBorder").fill),
// set properties on the border Shape of the "Button"
"ButtonBorder.fill": "fuchsia",
// set properties on the "Button" itself used by its event handlers
"_buttonFillOver": "pink",
})
.add(
new go.TextBlock("fuchsia button\nwith pink highlight", { margin: 2, textAlign: "center" })
),
go.GraphObject.build("Button", {
margin: 2,
click: (e, button) => alert(button.findObject("ButtonBorder").stroke),
// set properties on the border Shape of the "Button"
"ButtonBorder.figure": "Circle",
"ButtonBorder.fill": "cyan",
"ButtonBorder.stroke": "darkcyan",
"ButtonBorder.strokeWidth": 3,
// set properties on the "Button" itself used by its event handlers
"_buttonFillOver": "white",
"_buttonStrokeOver": "cyan",
})
.add(
new go.TextBlock("Circular\nbutton", { margin: 2, textAlign: "center" })
),
go.GraphObject.build("Button", {
margin: 2,
click: (e, button) => alert(button.findObject("PIC").source)
})
.add(
// the button content can be anything -- it doesn't have to be a TextBlock
new go.Picture("images/50x40.png", { name: "PIC", width: 50, height: 40 })
),
go.GraphObject.build("Button", {
margin: 2,
// buttons can be disabled too, by either setting or data binding:
isEnabled: false,
click: (e, button) => alert("won't be alerted")
})
.add(
new go.TextBlock("disabled", { stroke: "gray" })
)
)
);
diagram.model = new go.GraphLinksModel([ { } ]);
</code></pre>
<script>goCode("button2", 600, 250)</script>
<h2 id="TreeExpanderButtons">TreeExpanderButtons</h2>
<p>
It is common to want to expand and collapse subtrees of nodes.
It is easy to let the user control this by adding an instance of the "TreeExpanderButton" to your Node template.
The button calls <a>CommandHandler.collapseTree</a> or <a>CommandHandler.expandTree</a>
depending on the value of <a>Node.isTreeExpanded</a>.
The button's icon's <a>Shape.figure</a> changes as the value of
<a>Node.isTreeExpanded</a> changes.
</p>
<pre class="lang-js" id="treeExpanderButton"><code>
diagram.nodeTemplate =
new go.Node("Spot")
.add(
new go.Panel("Auto")
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.TextBlock("Click small button\nto collapse/expand subtree",
{ margin: 5 })
),
go.GraphObject.build("TreeExpanderButton", {
alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Top,
visible: true
})
);
diagram.layout = new go.TreeLayout({ angle: 90 });
diagram.model = new go.GraphLinksModel(
[ { key: 1 },
{ key: 2 } ],
[ { from: 1, to: 2 } ] );
</code></pre>
<script>goCode("treeExpanderButton", 600, 200)</script>
<p>
A "TreeExpanderButton" is a "Button" that holds a Shape displaying either a "MinusLine" or a "PlusLine"
figure, depending on the value of the <a>Node.isTreeExpanded</a>.
That shape is named "ButtonIcon", so that you can easily set or bind its properties,
in addition to the properties of the "ButtonBorder" and of the "Button" itself.
</p>
<pre class="lang-js" id="treeExpanderButton2"><code>
diagram.nodeTemplate =
new go.Node("Spot")
.add(
new go.Panel("Auto")
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.TextBlock("Click small button\nto collapse/expand subtree",
{ margin: 5 })
),
go.GraphObject.build("TreeExpanderButton",
{
alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Top,
visible: true,
// set the two additional properties used by "TreeExpanderButton"
// that control the shape depending on the value of Node.isTreeExpanded
"_treeExpandedFigure": "TriangleUp",
"_treeCollapsedFigure": "TriangleDown",
// set properties on the icon within the border
"ButtonIcon.fill": "darkcyan",
"ButtonIcon.strokeWidth": 0,
// set general "Button" properties
"ButtonBorder.figure": "Circle",
"ButtonBorder.stroke": "darkcyan",
"_buttonStrokeOver": "darkcyan"
})
);
diagram.layout = new go.TreeLayout({ angle: 90 });
diagram.model = new go.GraphLinksModel(
[ { key: 1 },
{ key: 2 } ],
[ { from: 1, to: 2 } ] );
</code></pre>
<script>goCode("treeExpanderButton2", 600, 200)</script>
<h2 id="SubGraphExpanderButtons">SubGraphExpanderButtons</h2>
<p>
It is also common to want to expand and collapse groups containing subgraphs.
You can let the user control this by adding an instance of the "SubGraphExpanderButton" to your Group template.
The button calls <a>CommandHandler.collapseSubGraph</a> or <a>CommandHandler.expandSubGraph</a>
depending on the value of <a>Group.isSubGraphExpanded</a>.
The button's icon's <a>Shape.figure</a> changes as the value of
<a>Group.isSubGraphExpanded</a> changes.
</p>
<pre class="lang-js" id="subgraphExpanderButton"><code>
diagram.groupTemplate =
new go.Group("Auto")
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.Panel("Vertical", { margin: 5, defaultAlignment: go.Spot.Left })
.add(
new go.Panel("Horizontal")
.add(
go.GraphObject.build("SubGraphExpanderButton",
{ margin: new go.Margin(0, 3, 5, 0) }),
new go.TextBlock("Group")
),
new go.Placeholder()
)
);
diagram.model = new go.GraphLinksModel(
[ { key: 0, isGroup: true },
{ key: 1, group: 0 },
{ key: 2, group: 0 },
{ key: 3, group: 0 } ] );
</code></pre>
<script>goCode("subgraphExpanderButton", 600, 150)</script>
<p>
A "SubGraphExpanderButton" is like a "TreeExpanderButton" in its being a "Button"
with a border Shape surrounding an icon Shape.
That shape is named "ButtonIcon", so that you can easily set or bind its properties,
in addition to the properties of the "ButtonBorder" and of the "Button" itself.
</p>
<pre class="lang-js" id="subgraphExpanderButton2"><code>
diagram.groupTemplate =
new go.Group("Auto")
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.Panel("Vertical", { margin: 5, defaultAlignment: go.Spot.Left })
.add(
new go.Panel("Horizontal")
.add(
go.GraphObject.build("SubGraphExpanderButton", {
// set the two additional properties used by "SubGraphExpanderButton"
// that control the shape depending on the value of Group.isSubGraphExpanded
"_subGraphExpandedFigure": "TriangleUp",
"_subGraphCollapsedFigure": "TriangleDown",
// set other properties on the button icon
"ButtonIcon.scale": 1.2,
// and properties on the button border or the button itself
"ButtonBorder.opacity": 0.0
}),
new go.TextBlock("Group")
),
new go.Placeholder()
)
);
diagram.model = new go.GraphLinksModel(
[ { key: 0, isGroup: true },
{ key: 1, group: 0 },
{ key: 2, group: 0 },
{ key: 3, group: 0 } ] );
</code></pre>
<script>goCode("subgraphExpanderButton2", 600, 150)</script>
<h2 id="PanelExpanderButtons">PanelExpanderButtons</h2>
<p>
It is common to want to expand and collapse a piece of a node,
thereby showing or hiding details that are sometimes not needed.
It is easy to let the user control this by adding an instance of the "PanelExpanderButton" to your node template.
The third argument to <a>GraphObject.build</a>
should be a string that names the element in the node whose
<a>GraphObject.visible</a> property you want the button to toggle.
The default name is "COLLAPSIBLE".
</p>
<pre class="lang-js" id="panelExpanderButton"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.add(
new go.Shape({ fill: "gold" }),
new go.Panel("Table",
{ defaultAlignment: go.Spot.Top, defaultColumnSeparatorStroke: "black" })
.add(
new go.Panel("Table", { column: 0 })
.add(
new go.TextBlock("List 1", {
column: 0, margin: new go.Margin(3, 3, 0, 3),
font: "bold 12pt sans-serif"
}),
go.GraphObject.build("PanelExpanderButton", { column: 1 }, "LIST1"),
new go.Panel("Vertical",
{ name: "LIST1", row: 1, column: 0, columnSpan: 2 })
.bind("itemArray", "list1")
),
new go.Panel("Table", { column: 1 })
.add(
new go.TextBlock("List 2", {
column: 0, margin: new go.Margin(3, 3, 0, 3),
font: "bold 12pt sans-serif"
}),
go.GraphObject.build("PanelExpanderButton", { column: 1 }, "LIST2"),
new go.Panel("Vertical",
{ name: "LIST2", row: 1, column: 0, columnSpan: 2 })
.bind("itemArray", "list2")
)
)
);
diagram.model = new go.GraphLinksModel([
{
list1: [ "one", "two", "three", "four", "five" ],
list2: [ "first", "second", "third", "fourth" ]
}
]);
</code></pre>
<script>goCode("panelExpanderButton", 600, 200)</script>
<p>
A "PanelExpanderButton" is like a "TreeExpanderButton" or "SubGraphExpanderButton"
in its being a "Button" with a border Shape surrounding an icon Shape.
However, this panel binds the <a>Shape.geometryString</a> rather than the <a>Shape.figure</a>.
</p>
<pre class="lang-js" id="panelExpanderButton2"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.add(
new go.Shape({ fill: "gold" }),
new go.Panel("Table",
{ defaultAlignment: go.Spot.Top, defaultColumnSeparatorStroke: "black" })
.add(
new go.Panel("Table", { column: 0 })
.add(
new go.TextBlock("List 1", {
column: 0, margin: new go.Margin(3, 3, 0, 3),
font: "bold 12pt sans-serif"
}),
go.GraphObject.build("PanelExpanderButton", {
column: 1, height: 16,
// set the two additional properties used by "PanelExpanderButton"
// that control the shape depending on the value of GraphObject.visible
// of the object named "LIST1"
"_buttonExpandedFigure": "M0 0 L10 0",
"_buttonCollapsedFigure": "M0 5 L10 5 M5 0 L5 10",
"ButtonIcon.stroke": "blue"
},
"LIST1"),
new go.Panel("Vertical", {
name: "LIST1", row: 1, column: 0, columnSpan: 2
})
.bind("itemArray", "list1")
),
new go.Panel("Table", { column: 1 })
.add(
new go.TextBlock("List 2", {
column: 0, margin: new go.Margin(3, 3, 0, 3),
font: "bold 12pt sans-serif"
}),
go.GraphObject.build("PanelExpanderButton", {
column: 1,
// set the two additional properties used by "PanelExpanderButton"
// that control the shape depending on the value of GraphObject.visible
// of the object named "LIST1"
"_buttonExpandedFigure": "F M0 10 L5 0 10 10z",
"_buttonCollapsedFigure": "F M0 0 L10 0 5 10z",
"ButtonIcon.strokeWidth": 0,
"ButtonIcon.fill": "blue"
},
"LIST2"),
new go.Panel("Vertical", {
name: "LIST2", row: 1, column: 0, columnSpan: 2
})
.bind("itemArray", "list2")
)
)
);
diagram.model = new go.GraphLinksModel([
{
list1: [ "one", "two", "three", "four", "five" ],
list2: [ "first", "second", "third", "fourth" ]
}
]);
</code></pre>
<script>goCode("panelExpanderButton2", 600, 200)</script>
<p>
You normally will <i>not</i> want to have the "PanelExpanderButton" inside the panel that will be collapsed.
</p>
<h2 id="ContextMenuButtons">ContextMenuButtons and ContextMenus</h2>
<p>
Although you can implement context menus in any way you choose, it is common to use the predefined
"ContextMenuButton".
</p>
<pre class="lang-js" id="contextMenuButtons"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.TextBlock("Use ContextMenu!", { margin: 5 })
);
diagram.nodeTemplate.contextMenu =
go.GraphObject.build("ContextMenu")
.add(
go.GraphObject.build("ContextMenuButton",
{ click: (e, obj) => shiftNode(obj, -20) })
.add(
new go.TextBlock("Shift Left")
),
go.GraphObject.build("ContextMenuButton",
{ click: (e, obj) => shiftNode(obj, +20) })
.add(
new go.TextBlock("Shift Right")
)
);
function shiftNode(obj, dist) {
const adorn = obj.part;
const node = adorn.adornedPart;
node.diagram.commit(d => {
const pos = node.location.copy();
pos.x += dist;
node.location = pos;
}, "Shift");
}
diagram.model = new go.GraphLinksModel([ { key: 1 } ] );
</code></pre>
<script>goCode("contextMenuButtons", 600, 150)</script>
<p>
For an example of defining context menus using HTML, see the <a href="../samples/customContextMenu.html">Custom
ContextMenu sample</a>.
</p>
<p>
A "ContextMenuButton" is just a "Button" with a few properties set.
One of those properties is <a>GraphObject.stretch</a>, which is set to
<code>go.Stretch.Horizontal</code> so that all of the "ContextMenuButton"s
in a "ContextMenu" will be stretch to the same width.
But you can set all of the usual properties on both its "ButtonBorder" Shape
as well as on the button itself.
</p>
<pre class="lang-js" id="contextMenuButtons2"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.add(
new go.Shape("Rectangle", { fill: "gold" }),
new go.TextBlock("Use ContextMenu!", { margin: 5 })
);
diagram.nodeTemplate.contextMenu =
go.GraphObject.build("ContextMenu")
.add(
go.GraphObject.build("ContextMenuButton",
{
click: (e, obj) => shiftNode(obj, -20),
"ButtonBorder.fill": "yellow",
"_buttonFillOver": "cyan"
})
.add(
new go.TextBlock("Shift Left")
),
go.GraphObject.build("ContextMenuButton",
{
click: (e, obj) => shiftNode(obj, +20),
"ButtonBorder.fill": "yellow",
"_buttonFillOver": "cyan"
})
.add(
new go.TextBlock("Shift Right")
),
go.GraphObject.build("ContextMenuButton",
{ isEnabled: false,
click: (e, obj) => alert("won't be alerted") })
.add(
new go.TextBlock("Disabled", { stroke: "gray" })
)
);
function shiftNode(obj, dist) {
const adorn = obj.part;
const node = adorn.adornedPart;
node.diagram.commit(d => {
const pos = node.location.copy();
pos.x += dist;
node.location = pos;
}, "Shift");
}
diagram.model = new go.GraphLinksModel([ { key: 1 } ] );
</code></pre>
<script>goCode("contextMenuButtons2", 600, 150)</script>
<p>
See also the fancier round context menu implemented in
<a href="../samples/radialAdornment.html">Radial Context Menu</a>.
</p>
<h2 id="CheckBoxButtons">CheckBoxButtons and CheckBoxes</h2>
<p>
A "CheckBoxButton" is a "Button" that is configured to toggle the boolean value of a data property.
By default the button is clear when the value is false and shows a check mark when the value is true,
but a great deal of customization is possible.
</p>
<p>
A "CheckBoxButton" is used in the definition of the "CheckBox" Panel, which is a convenient way to associate
any GraphObject as a label for the "CheckBoxButton".
</p>
<p>
The third argument to <a>GraphObject.build</a> when defining a "CheckBox" or "CheckBoxButton"
should be a string that names the data property holding the checked state of the "CheckBoxButton".
If you do not want clicking on the button to toggle the value of a data property,
specify a data property name that is the empty string.
</p>
<p>
Many examples of "CheckBox"es with various customizations are shown in the <a
href="../samples/CheckBoxes.html">CheckBoxes</a> sample.
</p>
<p>
You can find the definition of a "TriStateCheckBoxButton" in the <a
href="../samples/triStateCheckBoxTree.html">Tri-State CheckBox Tree</a> sample.
</p>
<h2 id="Toggles">ToggleSwitches and Toggles</h2>
<p>
A "ToggleSwitch" is a "Button" that is configured to toggle the boolean value of a data property.
By default the button shows a circular Shape at either end of a "Capsule" Shape
depending on the value of the boolean property, but a great deal of customization is possible.
</p>
<p>
A "ToggleSwitch" is used in the definition of the "Toggle" Panel, which is a convenient way to associate
any GraphObject as a label for the "ToggleSwitch".
</p>
<p>
The third argument to <a>GraphObject.build</a> when defining a "Toggle" or "ToggleSwitch"
should be a string that names the data property holding the checked state of the "ToggleSwitch".
If you do not want clicking on the button to toggle the value of a data property,
specify a data property name that is the empty string.
</p>
<p>
Many examples of "Toggle"s with various customization are shown in the
<a href="../samples/Toggles.html">Toggles</a> sample.
</p>
<h2 id="AutoRepeatButtons">AutoRepeat Buttons</h2>
<p>
The "AutoRepeatButton" had been defined for many years in the definition of the "ScrollingTable" extension.
It has now been moved into the main library to make it easier to use both in your code as well as in various extensions.
You can see it in action in the <a href="../samples/ScrollingTable.html">ScrollingTable</a> sample.
</p>
<h2 id="ButtonDefinitions">Button Definitions</h2>
<p>
The implementation of all predefined buttons is provided in <a href="../extensions/Buttons.js">Buttons.js</a>
in the Extensions directory.
You may wish to copy and adapt these definitions when creating your own buttons.
You may also wish to theme your own buttons, described on the
<a href="./theming.html#BuilderObjects">theming intro page</a>.
</p>
<p>
Note that the definitions of those buttons makes use of the <a>GraphObject.defineBuilder</a> static function.
That extends the behavior of <a>GraphObject.build</a>
to allow the creation of fairly complex visual trees by name with optional arguments.
You can find the definitions of various kinds of builders in the samples and extensions, such as at:
<ul>
<li>
<a href="../extensions/Buttons.js">Buttons.js</a>,
showing the implementation of all of the built-in buttons.
</li>
<li>
<a href="../extensions/HyperlinkText.js">HyperlinkText.js</a>,
implementing underlined text that calls <code>window.open</code>,
used in the <a href="../samples/Hyperlink.html">Hyperlink</a> sample.
</li>
<li>
<a href="../extensions/ScrollingTable.js">ScrollingTable.js</a>,
implementing a "ScrollingTable" Panel using two "AutoRepeatButtons",
used in the <a href="../samples/ScrollingTable.html">ScrollingTable</a> sample.
</li>
</ul>
</p>
</div>
</div>
<footer class="bg-white text-gray-900 border-t border-t-gray-200">
<div class="w-full max-w-screen-lg mx-auto px-4 py-6">
<p id="version" class="text-xs text-gray-900 m-0"></p>
<div class="text-sm px-0 mb-4 grid grid-cols-2 sm:grid-cols-3 gap-y-10">
<div>
<h2 class="text-base font-semibold text-nwoods-primary">GoJS</h2>
<ul class="list-none space-y-4 md:space-y-1 px-0">
<li>
<a href="../samples/index.html">Samples</a>
</li>
<li>
<a href="../learn/index.html">Learn</a>
</li>
<li>
<a href="../intro/index.html">Intro</a>
</li>
<li>
<a href="../api/index.html">API</a>
</li>
<li>
<a href="../changelog.html">Changelog</a>
</li>
<li>
<a href="https://github.com/NorthwoodsSoftware/GoJS" target="_blank" rel="noopener">GitHub</a>
</li>
</ul>
</div>
<div>
<h2 class="text-base font-semibold text-nwoods-primary">Support</h2>
<ul class="list-none space-y-4 md:space-y-1 px-0">
<li>
<a href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
</li>
<li>
<a href="https://forum.nwoods.com/c/gojs" target="_blank" rel="noopener">Forum</a>
</li>
<li>
<a href="https://nwoods.com/app/activate.aspx?sku=gojs" target="_blank" rel="noopener">Activate</a>
</li>
<li>
<a href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</li>
<li>
<a href="https://nwoods.com/register.html" target="_blank" rel="noopener">Register</a>
</li>
</ul>
</div>
<div>
<h2 class="text-base font-semibold text-nwoods-primary">Company</h2>
<ul class="list-none space-y-4 md:space-y-1 px-0">
<li>
<a target="_blank" href="https://nwoods.com" target="_blank" rel="noopener">Northwoods</a>
</li>
<li>
<a target="_blank" href="https://nwoods.com/about.html" target="_blank" rel="noopener">About Us</a>
</li>
<li>
<a target="_blank" href="https://nwoods.com/contact.html" target="_blank" rel="noopener">Contact Us</a>
</li>
<li>
<a target="_blank" href="https://nwoods.com/consulting.html" target="_blank" rel="noopener">Consulting</a>
</li>
<li>
<a target="_blank" href="https://twitter.com/northwoodsgo" target="_blank" rel="noopener">Twitter</a>
</li>
</ul>
</div>
</div>
<p class="text-sm text-gray-900 md:mb-6">
Copyright 1998-2025 <a href="https://nwoods.com">Northwoods Software</a>
</p>
</div>
</footer>
</body>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-S5QK8VSK84"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-S5QK8VSK84');
var getOutboundLink = function (url, label) {
gtag('event', 'click', {
'event_category': 'outbound',
'event_label': label,
'transport_type': 'beacon'
});
}
const params = new URL(document.location).searchParams
let a = params.get('a');
if (a) localStorage.setItem('a', a);
a = localStorage.getItem('a');
if (a) {
const links = [...document.body.getElementsByTagName("a")].filter((l) => l.href.includes('nwoods.com'));
for (const l of links) {
const url = new URL(l.href);
url.searchParams.set('a', a);
l.href = url;
}
}
</script>
<script src="../assets/js/prism.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script>
<script src="../assets/js/goDoc.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
if (window.go)
document
.getElementById('version')
.textContent = "GoJS version " + go.version;
if (window.goDoc)
window.goDoc();
var d = window.diagrams;
for (var i = 0; i < d.length; i++) {
var dargs = d[i];
goCodeExecute(dargs[0], dargs[1], dargs[2], dargs[3], dargs[4]);
}
if (window.extra)
window.extra();
}
);
</script>
</html>