gojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
913 lines (860 loc) • 43.8 kB
HTML
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GoJS Panels -- Northwoods Software</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<script src="../release/go.js"></script>
<script src="goIntro.js"></script>
</head>
<body onload="goIntro()">
<div id="container" class="container-fluid">
<div id="content">
<h1>Panels</h1>
<p>
<a>Panel</a>s are <a>GraphObject</a>s that hold other <a>GraphObject</a>s as their elements.
A Panel is responsible for sizing and positioning all of its elements.
Each Panel establishes its own coordinate system.
The elements of a panel are drawn in order, thereby establishing the Z-ordering of those elements.
</p>
<p>
Although there is only one Panel class, there are many different kinds of panels,
each with its own purpose in how it arranges its elements.
When you construct a <a>Panel</a> you usually specify its <a>Panel.type</a> as the constructor argument.
These are the kinds of panels that exist:
</p>
<ul>
<li><a>Panel,Position</a></li>
<li><a>Panel,Vertical</a></li>
<li><a>Panel,Horizontal</a></li>
<li><a>Panel,Auto</a></li>
<li><a>Panel,Spot</a></li>
<li><a>Panel,Table</a> (see the next section about <a href="tablePanels.html">Table Panels</a>)</li>
<li><a>Panel,Viewbox</a></li>
<li><a>Panel,Link</a> (see the section about <a href="linkLabels.html">Link Labels</a>)</li>
<li><a>Panel,Grid</a> (see the section about <a href="grids.html">Grid Patterns</a>)</li>
<li><a>Panel,Graduated</a> (see the section about <a href="graduatedPanels.html">Graduated Panels</a>)</li>
</ul>
<p>
In these simplistic demonstrations, the code programmatically creates a Part and adds it to the Diagram.
Once you learn about models and data binding you will generally not create parts programmatically.
</p>
<p>
Note also that one can only add <a>Part</a>s (i.e. <a>Node</a>s and <a>Link</a>s) to <a>Diagram</a>s,
and that a Part cannot be an element of a Panel.
But all Parts are Panels because the <a>Part</a> class inherits from <a>Panel</a> -- Parts are basically "top-level" Panels.
Thus these examples make use of Parts as top-level objects whereas within a Node you would use a Panel instead of a Part.
</p>
<p>
Panels have no visual elements of their own, so to display their size the
<a>GraphObject.background</a> is often used.
Panels also have <a>Panel.padding</a> in addition to <a>GraphObject.margin</a>.
Unlike margin, backgrounds cover padding. Setting a padding when the Panel is constrained
in size will reduce the total area that it has to arrange its elements. Setting a margin
will not do this -- instead the Panel will expand in size.
</p>
<h2 id="PositionPanels">Position Panels</h2>
<p>
The simplest kind of <a>Panel</a> is "Position" (<a>Panel,Position</a>).
Each element gets its normal size, whether its natural size or a specified <a>GraphObject.desiredSize</a>
(or equivalently the <a>GraphObject.width</a> and <a>GraphObject.height</a>).
</p>
<p>
Each element's position is given by the <a>GraphObject.position</a> property.
If no position is specified, the element is positioned at (0,0).
All positions are in the Panel's own coordinate system, not in the document-wide coordinate system.
Positions may include negative coordinates.
</p>
<p>
The Panel's size is just big enough to hold all of its elements.
If you want it to be a bit bigger than that, you can set the <a>Panel.padding</a> property.
</p>
<pre class="lang-js" id="positionPanels">
diagram.add(
// all Parts are Panels
$(go.Part, go.Panel.Position, // or "Position"
{ background: "lightgray" },
$(go.TextBlock, "default, at (0,0)", { background: "lightgreen" }),
$(go.TextBlock, "(100, 0)", { position: new go.Point(100, 0), background: "lightgreen" }),
$(go.TextBlock, "(0, 100)", { position: new go.Point(0, 100), background: "lightgreen" }),
$(go.TextBlock, "(55, 28)", { position: new go.Point(55, 28), background: "lightgreen" }),
$(go.TextBlock, "(33, 70)", { position: new go.Point(33, 70), background: "lightgreen" }),
$(go.TextBlock, "(100, 100)", { position: new go.Point(100, 100), background: "lightgreen" })
));
</pre>
<script>goCode("positionPanels", 600, 150)</script>
<p>
A Position Panel will always include the (0,0) origin point in its own panel coordinate system.
Thus a Position Panel that has elements whose collective bounds does not include (0,0) is always extended to include the origin.
</p>
<pre class="lang-js" id="zeroPositionPanel">
diagram.add(
$(go.Part, "Position",
{ background: "lightgray" },
$(go.TextBlock, "(-50,50)", { position: new go.Point(-50, 50), background: "lightgreen" }),
$(go.TextBlock, "(50, 50)", { position: new go.Point(50, 50), background: "lightgreen" }),
$(go.TextBlock, "(0, 100)", { position: new go.Point(0, 100), background: "lightgreen" })
));
</pre>
<script>goCode("zeroPositionPanel", 600, 140)</script>
<p>
Note that when you position <a>Shape</a>s within a Position Panel the thickness of their strokes,
<a>Shape.strokeWidth</a>, will be included. If you wish to position multiple Shapes so that their geometries
line up with each other, independent of how thick their strokes are, set
<a>Shape.isGeometryPositioned</a> to true on each of those Shapes.
</p>
<h2 id="VerticalPanels">Vertical Panels</h2>
<p>
A very common kind of <a>Panel</a> is "Vertical" (<a>Panel,Vertical</a>).
In this Panel all of the panel elements are arranged vertically from top to bottom.
Each element gets its normal height and either its normal width or, if stretched, the width of the panel.
If the element's <a>GraphObject.stretch</a> property has any vertical stretch component, it is ignored.
</p>
<p>
If the element's width does not happen to be the same as the width of the panel,
it is aligned horizontally according to its <a>GraphObject.alignment</a> property.
</p>
<p>
The following Vertical Panel shows how narrow objects are aligned horizontally
and how a narrow object may be stretched horizontally.
The width of the whole Panel is determined by the width of the widest object,
which in this case is the first element.
Note how the last element does not set the desired <a>GraphObject.width</a> property,
so that the <a>GraphObject.stretch</a> value is effective.
</p>
<pre class="lang-js" id="verticalPanels">
diagram.add(
$(go.Part, go.Panel.Vertical, // or "Vertical"
{ background: "lightgray" },
$(go.TextBlock, "a longer string", { background: "lightgreen" }),
$(go.TextBlock, "left", { background: "lightgreen", alignment: go.Spot.Left }),
$(go.TextBlock, "center", { background: "lightgreen", alignment: go.Spot.Center }),
$(go.TextBlock, "right", { background: "lightgreen", alignment: go.Spot.Right }),
$(go.TextBlock, "stretch", { background: "lightgreen", stretch: go.GraphObject.Fill })
));
</pre>
<script>goCode("verticalPanels", 600, 150)</script>
<h2 id="ConstrainedWidthVerticalPanels">Constrained Width Vertical Panels</h2>
<p>
A Vertical <a>Panel</a> normally has the width of its widest element and the height that is the sum of all of its elements.
However, you can also set the width and/or height to be larger or smaller than the natural size.
Or if there is a Panel containing this panel, it might impose size constraints on this panel.
If the width and/or height are larger than the natural size, the panel is bigger,
leaving empty space that may be filled with the background brush.
If the width and/or height are smaller than the natural size, the content elements may be clipped.
</p>
<p>
The Vertical Panel below sets the width to be 140, much wider than needed.
You can see how the last element's width is stretched.
</p>
<pre class="lang-js" id="excessWidth">
diagram.add(
$(go.Part, "Vertical",
{ background: "lightgray", width: 140 },
$(go.TextBlock, "a longer string", { background: "lightgreen" }),
$(go.TextBlock, "left", { background: "lightgreen", alignment: go.Spot.Left }),
$(go.TextBlock, "center", { background: "lightgreen", alignment: go.Spot.Center }),
$(go.TextBlock, "right", { background: "lightgreen", alignment: go.Spot.Right }),
$(go.TextBlock, "stretch", { background: "lightgreen", stretch: go.GraphObject.Fill })
));
</pre>
<script>goCode("excessWidth", 600, 150)</script>
<p>
These two Vertical Panels both have a width of 50, much less than natural.
The latter one also has a restricted height.
Note how the text is automatically wrapped to try to fit within the limited width,
because the default value for <a>TextBlock.wrap</a> is to allow wrapping.
</p>
<pre class="lang-js" id="limitedWidth">
diagram.add(
$(go.Part, "Vertical",
{ position: new go.Point(0, 0), background: "lightgray", width: 50 },
$(go.TextBlock, "a longer string", { background: "lightgreen" }),
$(go.TextBlock, "left", { background: "lightgreen", alignment: go.Spot.Left }),
$(go.TextBlock, "center", { background: "lightgreen", alignment: go.Spot.Center }),
$(go.TextBlock, "right", { background: "lightgreen", alignment: go.Spot.Right }),
$(go.TextBlock, "stretch", { background: "lightgreen", stretch: go.GraphObject.Fill })
));
diagram.add(
$(go.Part, "Vertical",
{ position: new go.Point(70, 0), background: "lightgray", width: 50, height: 65 },
$(go.TextBlock, "a longer string", { background: "lightgreen" }),
$(go.TextBlock, "left", { background: "lightgreen", alignment: go.Spot.Left }),
$(go.TextBlock, "center", { background: "lightgreen", alignment: go.Spot.Center }),
$(go.TextBlock, "right", { background: "lightgreen", alignment: go.Spot.Right }),
$(go.TextBlock, "stretch", { background: "lightgreen", stretch: go.GraphObject.Fill })
));
</pre>
<script>goCode("limitedWidth", 600, 150)</script>
<p>
Here is a Vertical Panel with a default <a>GraphObject.stretch</a> of <a>GraphObject,Horizontal</a>.
Because no width is specified for the whole panel, its width is the width of the widest element, in this case the second one.
Note how all of the <a>TextBlock</a>s have the same long width, as highlighted by the lightgreen backgrounds.
However the last TextBlock has a limited width, so it is not stretched.
One can limit the width but not the height by supplying a value of <code>NaN</code> or <code>Infinity</code> for the height.
</p>
<pre class="lang-js" id="defaultStretch">
diagram.add(
$(go.Part, "Vertical",
{ background: "lightgray", defaultStretch: go.GraphObject.Horizontal },
$(go.TextBlock, "short", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "a much longer string", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "medium length", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "short2", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "max 50", { margin: 2, background: "lightgreen", maxSize: new go.Size(50, NaN) })
));
</pre>
<script>goCode("defaultStretch", 600, 150)</script>
<p>
If you change that sample to set the <a>GraphObject.width</a> or <a>GraphObject.desiredSize</a>.width on one or more of the elements
(just the last one in this case), the panel will get a width that is equal to the maximum of the set widths.
The reduced width will cause the other, stretched, elements to be measured with the limited width (50 in this case),
which cause those <a>TextBlock</a>s to wrap to fit within the available width.
</p>
<pre class="lang-js" id="defaultStretch2">
diagram.add(
$(go.Part, "Vertical",
{ background: "lightgray", defaultStretch: go.GraphObject.Horizontal },
$(go.TextBlock, "short", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "a much longer string", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "medium length", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "short2", { margin: 2, background: "lightgreen" }),
$(go.TextBlock, "= 50", { margin: 2, background: "lightgreen", width: 50 })
));
</pre>
<script>goCode("defaultStretch2", 600, 150)</script>
<h2 id="HorizontalPanels">Horizontal Panels</h2>
<p>
Horizontal <a>Panel</a>s are just like Vertical Panels, except that the elements are arranged horizontally instead of vertically.
Elements are never stretched horizontally, but they may be stretched vertically.
Because elements are never stretched horizontally, a stretch value of <a>GraphObject,Fill</a> is the same as <a>GraphObject,Vertical</a>.
</p>
<p>
Note that the last element in both panels do not specify a desired <a>GraphObject.height</a>,
so that the <a>GraphObject.stretch</a> value may be effective.
</p>
<pre class="lang-js" id="horizontalPanels">
diagram.add(
$(go.Part, go.Panel.Horizontal, // or "Horizontal"
{ position: new go.Point(0, 0), background: "lightgray" },
$(go.Shape, { width: 30, fill: "lightgreen", height: 100 }),
$(go.Shape, { width: 30, fill: "lightgreen", height: 50, alignment: go.Spot.Top }),
$(go.Shape, { width: 30, fill: "lightgreen", height: 50, alignment: go.Spot.Center }),
$(go.Shape, { width: 30, fill: "lightgreen", height: 50, alignment: go.Spot.Bottom }),
$(go.Shape, { width: 30, fill: "lightgreen", stretch: go.GraphObject.Fill })
));
diagram.add(
$(go.Part, "Horizontal",
{ position: new go.Point(200, 0), background: "lightgray", height: 120 },
$(go.Shape, { width: 30, fill: "lightgreen", height: 50, alignment: go.Spot.Top }),
$(go.Shape, { width: 30, fill: "lightgreen", height: 50, alignment: go.Spot.Center }),
$(go.Shape, { width: 30, fill: "lightgreen", height: 50, alignment: go.Spot.Bottom }),
$(go.Shape, { width: 30, fill: "lightgreen", stretch: go.GraphObject.Fill })
));
</pre>
<script>goCode("horizontalPanels", 600, 150)</script>
<h3 id="FillingHorizontalAndVerticalPanelsInOppositeDirection">Filling Horizontal and Vertical Panels in Opposite Direction</h3>
<p>
Both Vertical and Horizontal <a>Panel</a>s can have their elements be arranged in the opposite direction:
bottom-to-top for Vertical Panels and right-to-left for Horizontal Panels.
Just set <a>Panel.isOpposite</a> to true.
</p>
<pre class="lang-js" id="opposite">
diagram.add(
$(go.Part, "Horizontal",
{ background: "lightgray", isOpposite: true },
$(go.TextBlock, "0", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "1", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "2", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "3", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "4", { margin: 5, background: "lightgreen" })
));
diagram.add(
$(go.Part, "Vertical",
{ background: "lightgray", isOpposite: true },
$(go.TextBlock, "0", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "1", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "2", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "3", { margin: 5, background: "lightgreen" }),
$(go.TextBlock, "4", { margin: 5, background: "lightgreen" })
));
</pre>
<script>goCode("opposite", 600, 150)</script>
<h2 id="DefaultAlignmentAndStretch">Default Alignment and Stretch</h2>
<p>
Both Vertical and Horizontal <a>Panel</a>s support the <a>Panel.defaultAlignment</a> and <a>Panel.defaultStretch</a> properties.
This is a convenience so that you do not need to set the <a>GraphObject.alignment</a> or <a>GraphObject.stretch</a> property on each element.
</p>
<p>
Here is a Horizontal Panel with a default <a>GraphObject.alignment</a> of <a>Spot,Bottom</a>.
All of the <a>Shape</a>s are aligned at the bottom, even though the default alignment would normally be <a>Spot.Center</a>.
However, the last Shape has its height stretched to the full height of the panel, 90.
In this case the <a>GraphObject.margin</a> provides a little extra space around the object.
</p>
<pre class="lang-js" id="defaultAlignment">
diagram.add(
$(go.Part, "Horizontal",
{ background: "lightgray", height: 90, defaultAlignment: go.Spot.Bottom },
$(go.Shape, { width: 30, margin: 2, fill: "lightgreen", height: 60 }),
$(go.Shape, { width: 30, margin: 2, fill: "lightgreen", height: 30 }),
$(go.Shape, { width: 30, margin: 2, fill: "lightgreen", height: 40 }),
$(go.Shape, { width: 30, margin: 2, fill: "lightgreen", stretch: go.GraphObject.Fill })
));
</pre>
<script>goCode("defaultAlignment", 600, 150)</script>
<p>
Vertical and Horizontal Panels are relatively simple ways of arranging a column or a row of objects.
For more options, you may need to use a <a href="tablePanels.html">Table Panel</a>, even with the same set of objects.
This is especially true when you want more control over the stretching of one or more elements.
</p>
<h2 id="Spots">Spots</h2>
<p>
Before we discuss other kinds of <a>Panel</a>s, we should elaborate a bit about the concept of spots.
<a>Spot</a>s are a way of providing both relative and absolute positioning information.
</p>
<p>
You have already seen many of the most common uses of Spots, for specifying the alignment of objects within a panel,
as constant values of the <a>Spot</a> class:
</p>
<table>
<tr>
<td><a>Spot,TopLeft</a></td> <td><a>Spot,Top</a></td> <td><a>Spot,TopRight</a></td>
</tr>
<tr>
<td><a>Spot,Left</a></td> <td><a>Spot.Center</a></td> <td><a>Spot,Right</a></td>
</tr>
<tr>
<td><a>Spot,BottomLeft</a></td> <td><a>Spot,Bottom</a></td> <td><a>Spot,BottomRight</a></td>
</tr>
</table>
<p>
But Spots are more general than that.
The <a>Spot.x</a> and <a>Spot.y</a> properties can be any number between zero and one, inclusive.
Those values are the fractional distances along the X and Y axes from the top-left corner of an arbitrary rectangle.
So <a>Spot,TopLeft</a> is the same as new go.Spot(0, 0),
<a>Spot,BottomRight</a> is the same as new go.Spot(1, 1),
and <a>Spot,Right</a> is the same as new go.Spot(1, 0.5).
</p>
<p>
Here are the standard nine Spots shown on a rectangular shape.
</p>
<pre class="lang-js" id="standardSpots">
diagram.add(
$(go.Part, go.Panel.Spot, // or "Spot"
$(go.Shape, "Rectangle",
{ fill: "lightgreen", stroke: null, width: 100, height: 50 }),
$(go.TextBlock, "0,0", { alignment: new go.Spot(0, 0) }),
$(go.TextBlock, "0.5,0", { alignment: new go.Spot(0.5, 0) }),
$(go.TextBlock, "1,0", { alignment: new go.Spot(1, 0) }),
$(go.TextBlock, "0,0.5", { alignment: new go.Spot(0, 0.5) }),
$(go.TextBlock, "0.5,0.5", { alignment: new go.Spot(0.5, 0.5) }),
$(go.TextBlock, "1,0.5", { alignment: new go.Spot(1, 0.5) }),
$(go.TextBlock, "0,1", { alignment: new go.Spot(0, 1) }),
$(go.TextBlock, "0.5,1", { alignment: new go.Spot(0.5, 1) }),
$(go.TextBlock, "1,1", { alignment: new go.Spot(1, 1) })
));
</pre>
<script>goCode("standardSpots", 600, 100)</script>
<p>
Besides the fractional positioning of a spot relative to some rectangular area,
you can also specify an absolute offset.
The <a>Spot.offsetX</a> and <a>Spot.offsetY</a> properties determine a point that is
a distance from the fractional point given by <a>Spot.x</a> and <a>Spot.y</a>.
Here we show three TextBlocks near the bottom-left corner and three TextBlocks near the bottom-right corner.
The ones on the left are offset along the X-axis plus or minus 40 units;
the ones on the right are offset along the Y-axis plus or minus 20 units.
TextBlocks are also given a semi-transparent red background to help distinguish their bounds.
</p>
<pre class="lang-js" id="spotOffsets">
var pink = "rgba(255,0,0,.2)";
diagram.add(
$(go.Part, "Spot",
$(go.Shape, "Rectangle",
{ fill: "lightgreen", stroke: null, width: 200, height: 50 }),
// Near bottom-left corner:
$(go.TextBlock, "(-40,0)", { background: pink, alignment: new go.Spot(0, 1, -40, 0) }),
$(go.TextBlock, "(0,0)", { background: pink, alignment: new go.Spot(0, 1, 0, 0) }),
$(go.TextBlock, "(40,0)", { background: pink, alignment: new go.Spot(0, 1, 40, 0) }),
// Near bottom-right corner:
$(go.TextBlock, "(0,-20)", { background: pink, alignment: new go.Spot(1, 1, 0, -20) }),
$(go.TextBlock, "(0,0)", { background: pink, alignment: new go.Spot(1, 1, 0, 0) }),
$(go.TextBlock, "(0,20)", { background: pink, alignment: new go.Spot(1, 1, 0, 20) })
));
</pre>
<script>goCode("spotOffsets", 600, 100)</script>
<h2 id="AutoPanels">Auto Panels</h2>
<p>
Auto <a>Panel</a>s fit a "main" element just around the other elements of the panel.
The main element is usually the furthest back in the Z-order, i.e. the first element, so that the other elements are not obscured by it.
The main element is declared by setting <a>GraphObject.isPanelMain</a> to true;
but often no such element is present, so it uses the very first element of the panel.
</p>
<p>
Typically the Auto Panel will measure the non-"main" elements,
determine a width and a height that can enclose all of them,
and make the "main" element that size or slightly bigger.
You do <em>not</em> set the <a>GraphObject.desiredSize</a> (or <a>GraphObject.width</a> or <a>GraphObject.height</a>) of the "main" element.
</p>
<p>
An Auto Panel is the normal way to implement a border around an object.
Use a <a>Shape</a> as the first/"main" element -- it becomes the border.
The <a>Shape.figure</a> is normally "Rectangle" or "RoundedRectangle" or "Ellipse", as shown below.
The other elements become the "content" for the panel inside the border.
In the examples below there is only a single "content" element, a <a>TextBlock</a>.
We have set the <a>GraphObject.background</a> and <a>Shape.fill</a> properties to help show the sizes and positions of objects.
</p>
<p>
Auto Panels should have two or more elements in them.
</p>
<pre class="lang-js" id="autoPanels">
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(0, 0), background: "lightgray" },
$(go.Shape, "Rectangle", { fill: "lightgreen" }),
$(go.TextBlock, "some text", { background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(100, 0), background: "lightgray" },
$(go.Shape, "RoundedRectangle", { fill: "lightgreen" }),
$(go.TextBlock, "some text", { background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(200, 0), background: "lightgray" },
$(go.Shape, "Ellipse", { fill: "lightgreen" }),
$(go.TextBlock, "some text", { background: "yellow" })
));
</pre>
<script>goCode("autoPanels", 600, 100)</script>
<p>
If you add a <a>GraphObject.margin</a> to the <a>TextBlock</a> in each of the same three panels,
you will add a little space all around the "content" element inside the "main" element.
</p>
<pre class="lang-js" id="marginAutoPanels">
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(0, 0), background: "lightgray" },
$(go.Shape, "Rectangle", { fill: "lightgreen" }),
$(go.TextBlock, "some text", { margin: 2, background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(100, 0), background: "lightgray" },
$(go.Shape, "RoundedRectangle", { fill: "lightgreen" }),
$(go.TextBlock, "some text", { margin: 2, background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(200, 0), background: "lightgray" },
$(go.Shape, "Ellipse", { fill: "lightgreen" }),
$(go.TextBlock, "some text", { margin: 2, background: "yellow" })
));
</pre>
<script>goCode("marginAutoPanels", 600, 100)</script>
<p>
For most <a>Shape</a>s other than "Rectangle" figure we do not want to have the "main" shape be the same size as the "content" element.
Ellipses, for example, need to be significantly larger than the content to avoid having the content spill over the edge of the shape.
This can be controlled by setting the <a>Shape.spot1</a> and <a>Shape.spot2</a> properties, which determine the area where the content should go.
Many of the predefined figures have their own default values for spot1 and spot2.
</p>
<pre class="lang-js" id="spotAreaAutoPanels">
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(0, 0), background: "lightgray" },
$(go.Shape, "RoundedRectangle",
{ fill: "lightgreen", spot1: new go.Spot(0, 0), spot2: new go.Spot(1, 1) }),
$(go.TextBlock, "some text", { background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(100, 0), background: "lightgray" },
$(go.Shape, "RoundedRectangle",
{ fill: "lightgreen",
spot1: new go.Spot(0, 0, 10, 0), spot2: new go.Spot(1, 1, -10, -10) }),
$(go.TextBlock, "some text", { background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(200, 0), background: "lightgray" },
$(go.Shape, "RoundedRectangle",
{ fill: "lightgreen",
spot1: new go.Spot(0, 0, 0, 20), spot2: new go.Spot(1, 1, 0, -20) }),
$(go.TextBlock, "some text", { background: "yellow" })
));
</pre>
<script>goCode("spotAreaAutoPanels", 600, 100)</script>
<p>
The spot1 and spot2 properties on the main <a>Shape</a> are more general and more flexible than specifying the <a>GraphObject.margin</a> on the content element(s).
</p>
<h2 id="ConstrainedSizeAutoPanels">Constrained Size Auto Panels</h2>
<p>
If you constrain the size of the whole panel, there may be less or more space available to fit all of the "content" elements inside the "main" element.
In the following example each Part has a total size of 60x60, causing the "content" <a>TextBlock</a>s to be limited in width and height,
less than the natural width, which results in wrapping of the text.
However there may not be enough height available to show the whole content element(s), causing them to be clipped.
You can see that in the third Part the text is clipped, because there is less available area within an ellipse than within a rectangle.
</p>
<pre class="lang-js" id="autoPanelsConstrained">
diagram.add(
$(go.Part, "Auto",
{ width: 60, height: 60 }, // set the size of the whole panel
{ position: new go.Point(0, 0), background: "lightgray" },
$(go.Shape, "Rectangle", { fill: "lightgreen" }),
$(go.TextBlock, "Some Wrapping Text", { background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ width: 60, height: 60 }, // set the size of the whole panel
{ position: new go.Point(100, 0), background: "lightgray" },
$(go.Shape, "RoundedRectangle", { fill: "lightgreen" }),
$(go.TextBlock, "Some Wrapping Text", { background: "yellow" })
));
diagram.add(
$(go.Part, "Auto",
{ width: 60, height: 60 }, // set the size of the whole panel
{ position: new go.Point(200, 0), background: "lightgray" },
$(go.Shape, "Ellipse", { fill: "lightgreen" }),
$(go.TextBlock, "Some Wrapping Text", { background: "yellow" })
));
</pre>
<script>goCode("autoPanelsConstrained", 600, 100)</script>
<p>
You should not set the size (<a>GraphObject.desiredSize</a> or <a>GraphObject.width</a> or <a>GraphObject.height</a>)
of the "main" element of an Auto Panel.
</p>
<p>
Auto Panels should have two or more elements in them.
</p>
<h2 id="SpotPanels">Spot Panels</h2>
<p>
Spot <a>Panel</a>s are like Auto Panels in that there is a "main" element and there are "other" elements that are not resized.
The "other" elements are positioned about the "main" element based on the <a>GraphObject.alignment</a> property that has a <a>Spot</a> value.
The main feature of Spot Panels, unlike Auto Panels, is that those elements may extend beyond the bounds of the "main" element.
</p>
<p>
This is useful for having the main shape be a specific size and positioning smaller elements at particular places relative to the main shape.
Note in this example that the TextBlocks are centered at the four corners,
causing the panel to be larger than the main shape, as can be seen with the light gray background.
</p>
<pre class="lang-js" id="spotPanels">
diagram.add(
$(go.Part, "Spot",
{ background: "lightgray" },
$(go.Shape, "Rectangle",
{ fill: "lightgreen", width: 100, height: 50 }),
$(go.TextBlock, "TL", { background: "yellow", alignment: go.Spot.TopLeft }),
$(go.TextBlock, "TR", { background: "yellow", alignment: go.Spot.TopRight }),
$(go.TextBlock, "BL", { background: "yellow", alignment: go.Spot.BottomLeft }),
$(go.TextBlock, "BR", { background: "yellow", alignment: go.Spot.BottomRight })
));
</pre>
<script>goCode("spotPanels", 600, 100)</script>
<p>
A Spot Panel aligns its content elements in the general location given by its <a>GraphObject.alignment</a>.
The precise point in the content element that is positioned defaults to <a>Spot.Center</a>, as seen above.
But you can set the element's <a>GraphObject.alignmentFocus</a> to use a different spot.
For example, if you use the same alignmentFocus as the alignment, the elements will be just inside the main element's bounds:
</p>
<pre class="lang-js" id="focusInsideSpotPanels">
diagram.add(
$(go.Part, "Spot",
{ background: "lightgray" },
$(go.Shape, "Rectangle",
{ fill: "lightgreen", width: 100, height: 50 }),
$(go.TextBlock, "TL", { background: "yellow",
alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.TopLeft }),
$(go.TextBlock, "TR", { background: "yellow",
alignment: go.Spot.TopRight, alignmentFocus: go.Spot.TopRight }),
$(go.TextBlock, "BL", { background: "yellow",
alignment: go.Spot.BottomLeft, alignmentFocus: go.Spot.BottomLeft }),
$(go.TextBlock, "BR", { background: "yellow",
alignment: go.Spot.BottomRight, alignmentFocus: go.Spot.BottomRight })
));
</pre>
<script>goCode("focusInsideSpotPanels", 600, 100)</script>
<p>
If you use the opposite alignmentFocus as the alignment, the elements will be just outside the main element's bounds:
</p>
<pre class="lang-js" id="focusOutsideSpotPanels">
diagram.add(
$(go.Part, "Spot",
{ background: "lightgray" },
$(go.Shape, "Rectangle",
{ fill: "lightgreen", width: 100, height: 50 }),
$(go.TextBlock, "TL", { background: "yellow",
alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.BottomRight }),
$(go.TextBlock, "TR", { background: "yellow",
alignment: go.Spot.TopRight, alignmentFocus: go.Spot.BottomLeft }),
$(go.TextBlock, "BL", { background: "yellow",
alignment: go.Spot.BottomLeft, alignmentFocus: go.Spot.TopRight }),
$(go.TextBlock, "BR", { background: "yellow",
alignment: go.Spot.BottomRight, alignmentFocus: go.Spot.TopLeft })
));
</pre>
<script>goCode("focusOutsideSpotPanels", 600, 100)</script>
<p>
alignmentFocus offsetX/Y will also work to offset the alignmentFocus point, the same way it works for Link labels:
</p>
<pre class="lang-js" id="focusSpotPanels2">
diagram.layout = $(go.GridLayout,
{ wrappingColumn: 10, wrappingWidth: 900, isViewportSized: false });
var blue = "rgba(0,0,255,.2)";
diagram.add(
$(go.Part, "Vertical",
{ locationObjectName: 'main' },
$(go.Panel, "Spot",
$(go.Shape, "Rectangle",
{ name: 'main', fill: "lightgreen", stroke: null, width: 100, height: 100 }),
$(go.Shape, "Rectangle",
{ fill: "lightcoral", stroke: null, width: 30, height: 30,
alignment: go.Spot.TopRight, alignmentFocus: go.Spot.TopRight
})
),
$(go.TextBlock, "alignment: TopRight,\n alignmentFocus: TopRight",
{ font: '11px sans-serif' })
));
diagram.add(
$(go.Part, "Vertical",
{ locationObjectName: 'main' },
$(go.Panel, "Spot",
$(go.Shape, "Rectangle",
{ name: 'main', fill: "lightgreen", stroke: null, width: 100, height: 100 }),
$(go.Shape, "Rectangle",
{ fill: "lightcoral", stroke: null, width: 30, height: 30,
alignment: go.Spot.TopRight, alignmentFocus: go.Spot.BottomRight
})
),
$(go.TextBlock, "alignment: TopRight,\n alignmentFocus: BottomRight",
{ font: '11px sans-serif' })
));
diagram.add(
$(go.Part, "Vertical",
{ locationObjectName: 'main' },
$(go.Panel, "Spot",
$(go.Shape, "Rectangle",
{ name: 'main', fill: "lightgreen", stroke: null, width: 100, height: 100 }),
$(go.Shape, "Rectangle",
{ fill: "lightcoral", stroke: null, width: 30, height: 30,
// BottomRight with offsetX = 15
alignment: go.Spot.TopRight, alignmentFocus: new go.Spot(1, 1, 15, 0)
})
),
$(go.TextBlock, "alignment: TopRight,\n alignmentFocus: BottomRight with offsetX = 15",
{ font: '11px sans-serif' })
));
</pre>
<script>goCode("focusSpotPanels2", 700, 250)</script>
<h3 id="AligningSubElementsSpotPanels">Aligning to sub-elements with Spot Panels</h3>
<p>
You may find it necessary to align an object nested inside a Spot panel with that panel's main element.
This is often the case when you want an element of a Spot panel to appear to have its own text label or other decorator.
</p>
<p>
To do this, you can use <a>Panel.alignmentFocusName</a>.
In the example below, a Spot panel contains a main element and another Panel. We want to align the corners of the main element
the shape within this panel, so we give it a name and set alignmentFocusName on the panel.
</p>
<pre class="lang-js" id="alignmentFocusName">
diagram.add(
$(go.Node, "Spot",
// Main shape
$(go.Shape, { strokeWidth: 4, fill: 'lime' }),
// Instead of aligning this Panel, we want to align the shape inside of it, to the corner of the main shape
$(go.Panel, "Horizontal",
{ background: 'rgba(255,0,0,0.1)', alignmentFocusName: 'shape', alignment: go.Spot.TopRight, alignmentFocus: go.Spot.BottomLeft },
$(go.TextBlock, "some\nlong label", { margin: 8 }),
$(go.Shape, "RoundedRectangle", { width: 20, height: 20, name: 'shape', strokeWidth: 0, fill: 'red' },
new go.Binding("fill", "color"))
)
)
);
</pre>
<script>goCode("alignmentFocusName", 600, 300)</script>
<!-- begin new -->
<h3 id="StretchingWithSpotPanels">Stretching with Spot Panels</h3>
<p>
When a non-main element in a Spot panel stretches, it takes on the width and/or height of the main element.
This can be useful for aligning elements within the Panel.
</p>
<p>
In the example below, the red main element has three elements around it which stretch to its side's length.
The main element is the <a>Part.resizeObject</a>, and as it changes size the stretched elements will change size accordingly.
</p>
<pre class="lang-js" id="StretchSpotPanels">
diagram.add(
$(go.Part, "Spot",
{
resizable: true,
resizeObjectName: 'MAIN'
},
$(go.Shape, "Rectangle", { name: 'MAIN', strokeWidth: 0, width: 80, height: 60, fill: 'rgba(255,0,0, .8)' }), // red
$(go.Shape, "Rectangle", { stretch: go.GraphObject.Vertical, strokeWidth: 0, width: 20, fill: 'rgba(0,255,0, .3)', // green
alignment: go.Spot.Left,
alignmentFocus: go.Spot.Right
}),
$(go.Shape, "Rectangle", { stretch: go.GraphObject.Vertical, strokeWidth: 0, width: 20, fill: 'rgba(0,0,255, .3)' , // blue
alignment: go.Spot.Right,
alignmentFocus: go.Spot.Left
}),
$(go.Shape, "Rectangle", { stretch: go.GraphObject.Horizontal, strokeWidth: 0, height: 20, fill: 'rgba(255,0,255, .3)' , // pink
alignment: go.Spot.Bottom,
alignmentFocus: go.Spot.Top
})
));
diagram.select(diagram.parts.first());
</pre>
<script>goCode("StretchSpotPanels", 600, 300)</script>
<!-- end new -->
<h3 id="ConstrainingSizeWithSpotPanels">Constraining size with Spot Panels</h3>
<p>
If you constrain the size of the whole panel, the panel may clip its elements.
For example, when the whole panel must be 100x50, there is room horizontally but not vertically
for the main element plus all of its other elements after arranging them.
</p>
<pre class="lang-js" id="clipping">
diagram.add(
$(go.Part, "Spot",
{ background: "lightgray",
width: 100, height: 50 }, // it is unusual to set the size!
$(go.Shape, "Rectangle", { fill: "lightgreen", width: 40, height: 40 }),
$(go.TextBlock, "TL", { background: "yellow",
alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.BottomRight }),
$(go.TextBlock, "TR", { background: "yellow",
alignment: go.Spot.TopRight, alignmentFocus: go.Spot.BottomLeft }),
$(go.TextBlock, "BL", { background: "yellow",
alignment: go.Spot.BottomLeft, alignmentFocus: go.Spot.TopRight }),
$(go.TextBlock, "BR", { background: "yellow",
alignment: go.Spot.BottomRight, alignmentFocus: go.Spot.TopLeft })
));
</pre>
<script>goCode("clipping", 600, 100)</script>
<p>
Spot Panels should have two or more elements in them.
</p>
<p>
Remember that the elements of every panel are drawn in order.
Normally you want the main element to be behind all of the other elements, so the main element will come first.
However if you want the main element to be in front of some or all of the other elements,
you can move the main element not to be the first element of the panel,
if you also set its <a>GraphObject.isPanelMain</a> property to true.
</p>
<pre class="lang-js" id="spotZorder">
diagram.add(
$(go.Part, "Spot",
{ background: "lightgray" },
$(go.TextBlock, "TL", { background: "yellow", alignment: go.Spot.TopLeft }),
$(go.TextBlock, "TR", { background: "yellow", alignment: go.Spot.TopRight }),
$(go.TextBlock, "BL", { background: "yellow", alignment: go.Spot.BottomLeft }),
$(go.TextBlock, "BR", { background: "yellow", alignment: go.Spot.BottomRight }),
// NOTE: the main element isn't first, so it must be declared by setting isPanelMain to true
$(go.Shape, "Rectangle",
{ isPanelMain: true },
{ fill: "lightgreen", width: 100, height: 50 })
));
</pre>
<script>goCode("spotZorder", 600, 100)</script>
<p>
Note how the opaque Shape, explicitly declared to be the main element, is now visually in front of
the non-main elements of the Spot Panel because it has been moved to be the last element in the panel.
</p>
<p>
Without setting <a>GraphObject.isPanelMain</a> to true on the desired main element, in this example
<a>Panel.findMainElement</a> would return the first TextBlock.
This would cause all of the other elements to be arranged around that TextBlock.
Since the TextBlock is small and the rectangular Shape is big and opaque,
the Shape would cover all of the other TextBlocks, so the user might not see any text,
depending on the size and alignment of those other TextBlocks.
</p>
<h3 id="ClippingWithSpotPanels">Clipping with Spot Panels</h3>
<p>
Spot Panels can set <a>Panel.isClipping</a> to true to use the main Panel element as a clipping area instead of a drawn Shape.
If used, the main element must be a Shape and its stroke and fill will not be drawn.
When <a>Panel.isClipping</a> is true, the Spot panel will size itself to be the <strong>intersection</strong> of the main element bounds and
all other elements' bounds, rather than the union of these bounds.
</p>
<p>
Example:
<pre class="lang-js" id="clipPictures">
diagram.layout = $(go.GridLayout);
// Without Panel.isClipping
diagram.add(
$(go.Part, "Spot",
{ scale: 2 },
$(go.Shape, "Circle", { width: 55, height: 55, strokeWidth: 0 } ),
$(go.Picture, "../samples/images/55x55.png",
{ width: 55, height: 55 }
)
)
);
// Using Panel.isClipping
diagram.add(
$(go.Part, "Spot",
{ isClipping: true, scale: 2 },
$(go.Shape, "Circle", { width: 55, height: 55, strokeWidth: 0 } ),
$(go.Picture, "../samples/images/55x55.png",
{ width: 55, height: 55 }
)
)
);
// Using Panel.isClipping and also having a surrounding panel
diagram.add(
$(go.Part, "Spot",
{ scale: 2 },
$(go.Shape, "Circle", { width: 65, height: 65, strokeWidth: 0, fill: 'red' } ),
$(go.Panel, "Spot",
{ isClipping: true },
$(go.Shape, "Circle", { width: 55, height: 55, strokeWidth: 0 } ),
$(go.Picture, "../samples/images/55x55.png",
{ width: 55, height: 55 }
)
)
)
);
</pre>
<script>goCode("clipPictures", 500, 200)</script>
<h2 id="ViewboxPanels">Viewbox Panels</h2>
<p>
Viewbox <a>Panel</a>s contain only a single element that is rescaled to fit the size of the Panel.
</p>
<p>
This is useful for taking an arbitrary element, especially a <a>Panel</a>, and automatically squeezing it to fit in a small fixed-size area.
The same can be achieved by setting the <a>GraphObject.scale</a> on that element, but with a Viewbox Panel that computation is performed automatically.
</p>
<p>
In this diagram there are two copies of the same Auto <a>Panel</a>,
each consisting of a <a>Picture</a> and a caption <a>TextBlock</a> surrounded by an Ellipse <a>Shape</a>.
The one on the left is inside a Viewbox <a>Panel</a> forced to fit in an 80x80 area;
the one on the right is its natural size.
Note that you can still see all of the elements of the panel at a reduced scale so that it can fit inside the Viewbox panel.
But because the nested panel is taller than it is wider, there is empty space on the sides of the 80x80 Viewbox.
</p>
<pre class="lang-js" id="viewboxPanel">
diagram.add(
$(go.Part, go.Panel.Viewbox, // or "Viewbox"
{ position: new go.Point(0, 0), background: "lightgray",
width: 80, height: 80 },
$(go.Panel, "Auto",
$(go.Shape, "Ellipse", { fill: "lightgreen" }),
$(go.Panel, "Vertical",
$(go.Picture, { source: "images/120x160.png" }),
$(go.TextBlock, "a 120x160 kitten")
)
)
));
diagram.add(
$(go.Part, "Auto",
{ position: new go.Point(100, 0), background: "lightgray" },
$(go.Shape, "Ellipse", { fill: "lightgreen" }),
$(go.Panel, "Vertical",
$(go.Picture, { source: "images/120x160.png" }),
$(go.TextBlock, "a 120x160 kitten")
)
));
</pre>
<script>goCode("viewboxPanel", 600, 270)</script>
</div>
</div>
</body>
</html>