create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
474 lines (423 loc) • 19.6 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"/>
<meta name="description" content="A simple network configuration editor." />
<meta itemprop="description" content="A simple network configuration editor." />
<meta property="og:description" content="A simple network configuration editor." />
<meta name="twitter:description" content="A simple network configuration editor." />
<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="Network Configuration Diagram Editor With Bars" />
<meta property="og:title" content="Network Configuration Diagram Editor With Bars" />
<meta name="twitter:title" content="Network Configuration Diagram Editor With Bars" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/network.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/network.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/network.png" />
<meta property="og:url" content="https://gojs.net/latest/samples/network.html" />
<meta property="twitter:url" content="https://gojs.net/latest/samples/network.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Network Configuration Diagram Editor With Bars | GoJS Diagramming Library
</title>
</head>
<body>
<!-- This top nav is not part of the sample code -->
<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="flex flex-col prose">
<div class="w-full max-w-screen-xl mx-auto">
<!-- * * * * * * * * * * * * * -->
<!-- Start of GoJS sample code -->
<script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script>
<div id="allSampleContent" class="p-4 w-full">
<script src="../extensions/Figures.js"></script>
<script id="code">
// A custom Link class that routes directly to the closest horizontal point of an "HBar" node.
// If the regular node is too far to the left or right of the "HBar" node, the link connects
// with the closest end of the "HBar" node.
class BarLink extends go.Link {
constructor(init) {
super();
if (init) Object.assign(this, init);
}
computeSpot(from, port) {
if (from && this.toNode && this.toNode.category === 'HBar') return go.Spot.TopBottomSides;
if (!from && this.fromNode && this.fromNode.category === 'HBar') return go.Spot.TopBottomSides;
return super.computeSpot(from, port);
}
getLinkPoint(node, port, spot, from, ortho, othernode, otherport) {
if (!from && node.category === 'HBar') {
var op = super.getLinkPoint(othernode, otherport, this.computeSpot(!from), !from, ortho, node, port);
var r = port.getDocumentBounds();
var y = op.y > r.centerY ? r.bottom : r.top;
if (op.x < r.left) return new go.Point(r.left, y);
if (op.x > r.right) return new go.Point(r.right, y);
return new go.Point(op.x, y);
} else {
return super.getLinkPoint(node, port, spot, from, ortho, othernode, otherport);
}
}
getLinkDirection(node, port, linkpoint, spot, from, ortho, othernode, otherport) {
if (node.category === 'HBar' || othernode.category === 'HBar') {
var p = port.getDocumentPoint(go.Spot.Center);
var op = otherport.getDocumentPoint(go.Spot.Center);
var below = op.y > p.y;
return below ? 90 : 270;
} else {
return super.getLinkDirection(node, port, linkpoint, spot, from, ortho, othernode, otherport);
}
}
}
// end BarLink class
function init() {
myDiagram = new go.Diagram('myDiagramDiv', {
'linkingTool.direction': go.LinkingDirection.ForwardsOnly,
'undoManager.isEnabled': true
});
// when the document is modified, add a "*" to the title and enable the "Save" button
myDiagram.addDiagramListener('Modified', e => {
const button = document.getElementById('saveModel');
if (button) button.disabled = !myDiagram.isModified;
const idx = document.title.indexOf('*');
if (myDiagram.isModified) {
if (idx < 0) document.title += '*';
} else {
if (idx >= 0) document.title = document.title.slice(0, idx);
}
});
myDiagram.nodeTemplateMap.add('Generator',
new go.Node('Spot', { locationSpot: go.Spot.Center, selectionObjectName: 'BODY' })
.bindTwoWay('location', 'location', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('ACVoltageSource', {
name: 'BODY',
stroke: 'white',
strokeWidth: 3,
fill: 'transparent',
background: 'darkblue',
width: 40,
height: 40,
margin: 5,
portId: '',
fromLinkable: true,
cursor: 'pointer',
fromSpot: go.Spot.TopBottomSides,
toSpot: go.Spot.TopBottomSides
}),
new go.TextBlock({ alignment: go.Spot.Right, alignmentFocus: go.Spot.Left, editable: true })
.bindTwoWay('text')
));
myDiagram.nodeTemplateMap.add('Connector',
new go.Node('Spot', { locationSpot: go.Spot.Center, selectionObjectName: 'BODY' })
.bindTwoWay('location', 'location', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('Circle', {
name: 'BODY',
stroke: null,
fill: new go.Brush('Linear', { 0: 'lightgray', 1: 'gray' }),
background: 'gray',
width: 20,
height: 20,
margin: 2,
portId: '',
fromLinkable: true,
cursor: 'pointer',
fromSpot: go.Spot.TopBottomSides,
toSpot: go.Spot.TopBottomSides
}),
new go.TextBlock({ alignment: go.Spot.Right, alignmentFocus: go.Spot.Left, editable: true })
.bindTwoWay('text')
));
myDiagram.nodeTemplateMap.add('Consumer',
new go.Node('Spot', {
locationSpot: go.Spot.Center,
locationObjectName: 'BODY',
selectionObjectName: 'BODY'
})
.bindTwoWay('location', 'location', go.Point.parse, go.Point.stringify)
.add(
new go.Picture('images/pc.jpg', {
name: 'BODY',
width: 50,
height: 40,
margin: 2,
portId: '',
fromLinkable: true,
cursor: 'pointer',
fromSpot: go.Spot.TopBottomSides,
toSpot: go.Spot.TopBottomSides
}),
new go.TextBlock({ alignment: go.Spot.Right, alignmentFocus: go.Spot.Left, editable: true })
.bindTwoWay('text')
));
myDiagram.nodeTemplateMap.add('HBar',
new go.Node('Spot', {
layerName: 'Background',
// special resizing: just at the ends
resizable: true,
resizeObjectName: 'SHAPE',
resizeAdornmentTemplate:
new go.Adornment('Spot')
.add(
new go.Placeholder(),
new go.Shape({ // left resize handle
alignment: go.Spot.Left,
cursor: 'col-resize',
desiredSize: new go.Size(6, 6),
fill: 'lightblue',
stroke: 'dodgerblue'
}),
new go.Shape({ // right resize handle
alignment: go.Spot.Right,
cursor: 'col-resize',
desiredSize: new go.Size(6, 6),
fill: 'lightblue',
stroke: 'dodgerblue'
})
)
})
.bindTwoWay('location', 'location', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('Rectangle', {
name: 'SHAPE',
fill: 'black',
stroke: null,
strokeWidth: 0,
width: 1000,
height: 4,
minSize: new go.Size(100, 4),
maxSize: new go.Size(Infinity, 4),
portId: '',
toLinkable: true
})
.bindTwoWay('desiredSize', 'size', go.Size.parse, go.Size.stringify)
.bind('fill'),
new go.TextBlock({ alignment: go.Spot.Right, alignmentFocus: go.Spot.Left, editable: true })
.bindTwoWay('text')
));
myDiagram.linkTemplate =
new BarLink({ // subclass defined above
routing: go.Routing.Orthogonal,
relinkableFrom: true,
relinkableTo: true,
toPortChanged: (link, oldport, newport) => {
if (newport instanceof go.Shape) link.path.stroke = newport.fill;
}
})
.add(
new go.Shape({ strokeWidth: 2 })
);
// start off with a simple diagram
load();
// initialize Palette
myPalette = new go.Palette('myPaletteDiv', {
nodeTemplateMap: myDiagram.nodeTemplateMap,
layout: new go.GridLayout({
cellSize: new go.Size(2, 2),
isViewportSized: true
})
});
myPalette.model.nodeDataArray = [
{ text: 'Generator', category: 'Generator' },
{ text: 'Consumer', category: 'Consumer' },
{ text: 'Connector', category: 'Connector' },
{ text: 'Bar', category: 'HBar', size: '100 4' }
];
// remove cursors on all ports in the Palette
// make TextBlocks invisible, to reduce size of Nodes
myPalette.nodes.each(node => {
node.ports.each(port => (port.cursor = ''));
node.elements.each(tb => {
if (tb instanceof go.TextBlock) tb.visible = false;
});
});
// initialize Overview
myOverview = new go.Overview('myOverviewDiv', {
observed: myDiagram,
contentAlignment: go.Spot.Center
});
}
// save a model to and load a model from JSON text, displayed below the Diagram
function save() {
document.getElementById('mySavedModel').value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById('mySavedModel').value);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div style="display: flex; flex-direction: column; margin-right: 2px">
<div id="myPaletteDiv" style="flex-grow: 1; width: 100px; background-color: whitesmoke; border: solid 1px black"></div>
<div id="myOverviewDiv" style="margin-top: 2px; width: 100px; height: 100px; background-color: lightgray; border: solid 1px black"></div>
</div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 400px; border: solid 1px black"></div>
</div>
<p>
This sample creates and uses a custom <a>Link</a> class <code>BarLink</code>.
It draws the perpendicular line between the <a>Node</a> and the HBar
<a>Node</a>. When the <a>Node</a> doesn't line up horizontally with the HBAR
it will draw an Orthogonal <a>Link</a> at close between them as possible.
</p>
<div id="buttons">
<button id="loadModel" onclick="load()">Load</button>
<button id="saveModel" onclick="save()">Save</button>
</div>
<textarea id="mySavedModel" style="width: 100%; height: 300px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{"key":0, "text":"Gen1", "category":"Generator", "location":"300 0"},
{"key":1, "text":"Bar1", "category":"HBar", "location":"100 100", "size":"500 4", "fill":"green"},
{"key":3, "text":"Cons1", "category":"Consumer", "location":"53 234"},
{"key":2, "text":"Bar2", "category":"HBar", "location":"0 300", "size":"600 4", "fill":"orange"},
{"key":4, "text":"Conn1", "category":"Connector", "location":"232.5 207.75"},
{"key":5, "text":"Cons3", "category":"Consumer", "location":"357.5 230.75"},
{"key":6, "text":"Cons2", "category":"Consumer", "location":"484.5 164.75"}
],
"linkDataArray": [
{"from":0, "to":1},
{"from":0, "to":2},
{"from":3, "to":2},
{"from":4, "to":1},
{"from":4, "to":2},
{"from":5, "to":2},
{"from":6, "to":1}
]}
</textarea>
</div>
</div>
<!-- * * * * * * * * * * * * * -->
<!-- End of GoJS sample code -->
</div>
<div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto">
<hr/>
<h3 class="text-xl">GoJS Features in this sample</h3>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Links</h4>
<p>
The <a href="../api/symbols/Link.html" target="api">Link</a> class is used to implement a visual relationship between nodes.
Links are normally created by the presence of link data objects in the <a href="../api/symbols/GraphLinksModel.html#linkDataArray" target="api">GraphLinksModel.linkDataArray</a>
or by a parent key reference as the value of the <a href="../api/symbols/TreeModel.html#nodeParentKeyProperty" target="api">TreeModel.nodeParentKeyProperty</a> of a node data object
in a <a href="../api/symbols/TreeModel.html" target="api">TreeModel</a>.
More information can be found in the <a href="../intro/links.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#links">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Palette</h4>
<p>
A <a href="../api/symbols/Palette.html" target="api">Palette</a> is a subclass of <a href="../api/symbols/Diagram.html" target="api">Diagram</a> that is used to display a number of <a href="../api/symbols/Part.html" target="api">Part</a>s that
can be dragged into the diagram that is being modified by the user.
The initialization of a <a href="../api/symbols/Palette.html" target="api">Palette</a> is just like the initialization of any <a href="../api/symbols/Diagram.html" target="api">Diagram</a>.
Like Diagrams, you can have more than one Palette on the page at the same time.
</p>
<p>
More information can be found in the <a href="../intro/palette.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#palette">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Overview Diagrams</h4>
<p>
An <a href="../api/symbols/Overview.html" target="api">Overview</a> is a subclass of <a href="../api/symbols/Diagram.html" target="api">Diagram</a> that is used to display all of the <a href="../api/symbols/Part.html" target="api">Part</a>s
of another diagram and to show where that diagram's viewport is relative to all of those parts.
The user can also scroll the overviewed diagram by clicking or dragging within the overview.
</p>
<p>
The initialization of an <a href="../api/symbols/Overview.html" target="api">Overview</a> is just a matter of setting <a href="../api/symbols/Overview.html#observed" target="api">Overview.observed</a>
to refer to the <a href="../api/symbols/Diagram.html" target="api">Diagram</a> that you want it to show. So there needs to be a DIV for your main diagram,
for which you create a Diagram in the normal manner, and a separate DIV for your overview, for which you
create the Overview in a very simple manner.
</p>
<p>
More information can be found in the <a href="../intro/overview.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#overview">Related samples</a>
</p>
<hr>
</div>
</div>
</body>
<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>