Relative width in auto panel


In one of my diagrams, I have a group and its member nodes looking like the following:

The enclosing group (titled “[Value Stream] Acquire Product”) is implemented as an auto panel containing the segmented banner at the top, then the icon and title, and then a standard placeholder holding the member nodes.

The banner at the top is constructed from an item array, and can contain an arbitrary number of colored segments. Each segment is implemented as a TextBlock with a dynamic background color, text and tooltip. Additionally, the relative width it should span is defined through a bound property named “widthPercentage”.

Basically, what I want from the banner is to:

  • span the whole width of the auto panel, whatever it is
  • have each colored segment span a width that corresponds to its widthPercentage

The way I tried to do it is to bind the “width” property for each segment’s TextBlock, and derive it from the widthPercentage value and the enclosing auto panel’s bounds. This approach somewhat works for groups with static content. However, it fails for groups whose width changes because of dynamic content (e.g. member nodes being added or removed).

The reason for this is pretty clear: the main auto panel tries to compute its width by considering, among others, the width of the banner, which in turn is derived from the main auto panel’s width.

My question: is there a way out of this cycle? Is there a way to tell GoJS in effect: set the width for this element as X% of its parent auto panel’s width, whatever it is? Or alternatively, is there a way to tell an auto panel to ignore a given element when computing its size, so that the cycle can be broken?

Or is there some other approach that could work here?

The solution isn’t obvious to me at this moment; please give us some time to consider the possibilities.

Unfortunately our Table panel does not have proportional row/column sizing.

For now, you may need to create something that dynamically updates the size of the items in the item array, as the group changes. We have one example like that, though its with the proportional sizes of pie slices in a pie chart:

The part about sizing segments dynamically based on the group’s size is already in place, and mostly works for static groups. The fundamental issue remains the seemingly unavoidable cycle between the group’s main auto panel and each segment when they try to size themselves based on each other’s size.

I had already taken a look at the pie chart example, but couldn’t derive a solution from it that can apply to the core issue.

If there’s no way to specify relative widths for the standard implementation of an auto panel, would it be possible to extend and customize it? If I could just make it ignore certain elements when computing its size, I may be able to get the end result I’m looking for.

Creating your own custom panels is something we’re looking at for 2.0, but its not available at this time.

What’s your Group structure exactly? Ideally I’m envisioning something like this:

Auto Panel
    Shape (RoundedRect)
    Vertical Panel
        Table Panel (that stretches horizontally, that contains the item array)
                items (1 column per item, and also stretches horizontally, with the .width set by some func)

That would be for-sure the right thing to do if we had proportional table rows/columns, but needs some custom code to do right now, like what the pie chart is doing.

Yes, that is essentially the parent group’s structure.

Once again though, I don’t see how the pie chart example could suggest a solution to the fundamental issue. In that example, there is no cycle to break since the radius is fixed and it’s just a matter of calculating each slice’s size as a fraction of the static circumference.

In my case, I have an auto panel that tries to size itself based on the components it contains, some of which try to size themselves based on the auto panel’s size. How would you suggest addressing that?

Is it not the case that:

  • The Placeholder is sized by the contents of the Group + layout
  • The table (if it stretches) is sized by its sibling, the placeholder
  • The vertical panel’s width is sized by the placeholder (only non-stretching item inside of it)
  • The shape and auto panel are sized by the vertical panel

In that design, everything that needs a size subscribes to only one source for its size, so I don’t see the cyclic nature. What’s different?

The group’s structure that you outlined is a good sketch of the real thing, but the actual implementation contains more components that can influence the auto panel’s width.

For example, the title appearing under the banner is fully dynamic, editable by the user and so can vary in size. For groups that have few member nodes, the title is going to drive the auto panel’s width, not the placeholder.

I have other node templates where I need to implement a segmented banner where the placeholder can never be the single determinant for the auto panel’s final width.

So I have tried basing everything on the placeholder’s width instead of the auto panel’s, but for those reasons I found it just cannot work.

I’m not sure even any changes to auto panel would allow a fix, if you have a truly circular setup.

You can’t simplify your node template so that no one part is relying on another?

Not really. I thought about taking the banner out of the auto panel, but it’s not an option since the banner has to appear within the node border.

This is hacked together from the Pie chart sample, so some of the terminology in the code no longer fits, but it shows you could could make such a proportional bar dynamic:

It’s not ideal, since you have to manually update all of the groups, and you need to decide when that’s important to you. Right now it only does so at the end of every transaction, so if you move a Node, making the placeholder larger, the group’s bar will proportionally resize to fit but only at the end of the transaction. If your users cannot move nodes, that’s less of a concern, but I’m sure there are other interactive concerns in your app.

If you want me to modify the example more to show another scenario, let me know.

Sorry that there is no easy and efficient built in way to do this.