Skip to main content

Groups

## Groups

Groups are elements in your UI that act as a container for zero or more Nodes or other Groups. In the UI, these can be collapsed, and Edges to/from the Nodes/Groups inside the Group are then temporarily displaced to the Group container. There is no limit imposed on how deeply Groups may be nested.

Working with Groups#

Groups have in common with Nodes, Edges and Ports the two concepts of id and of type, and as with the other graph objects, type will be set to "default" if it cannot be determined. Group IDs and types either follow the default rules (ie. they are given by the id and type parameters, respectively, in the Group's data), or they are derived by applying the current idFunction and typeFunction.

Groups, as with the other graph objects, can have arbitrary JSON data associated with them.


Loading Groups#

The Toolkit's default JSON parser can parse information about groups, and about node membership in groups:

const t = jsPlumbToolkitBrowserUI.newInstance()t.load({  data:{    groups:[      { id:"g1", type:"groupType1" },      { id:"g2", type:"groupType1" }    ],    nodes:[      { id:"1", group:"g1" },      { id:"2", group:"g1" },      { id:"3", group:"g2" },      { id:"4", group:"g2" }    ],    edges:[      { "source":"1", target:"2" },      { "source":"1", target:"3" },      { "source":"4", target:"3" },      { "source":"4", target:"2" }    ]  }})

Here, we have a groups section which lists out all the groups in the dataset. Then in the nodes we map each Node to a Group, which is expected to be declared in the payload.

TOP


Rendering#

Groups are rendered using client side templates just as Nodes are. They are declared inside the view alongside Nodes, Edges and Ports. A simple example to start:

toolkit.render({  ...  view:{    nodes:{        "default":{            templateId:"tmplNode",            events:{                click:function(p) {                    alert("You clicked on node " + p.node.id);                }            }        }    },    groups:{        "groupType1":{            templateId:"tmplGroupType1",            constrain:true        }    }  }});

Here we declare that Groups of type groupType1 are to be rendered using the template tmplGroupType1, and that members of the Group are constrained to the Group element (that is, they cannot be dragged outside of the Group's bounds).

There are several flags available to control the drag behaviour of members of a group:

  • droppable True by default - indicates that nodes/groups may be dropped onto the group, either from some other group or from the main canvas.

  • constrain False by default - nodes/groups may be dragged outside of the bounds of the group. When you do drag a node/group outside of the bounds of its parent, what happens next depends on the other flags you have set and where you have dropped it. orphan:true, for instance, will cause the node/group to be removed from its parent group. revert will reinstate the node/group's position inside its parent, unless it was dropped on another group.

  • revert True by default - a node/group dragged outside of its parent group will, unless dropped on another group, revert back to its position inside the group.

  • prune False by default. If true, Nodes dropped outside of the Group (and not dropped onto another Group) are removed from the dataset (not just the Group...the entire dataset).

  • orphan False by default. If true, nodes/groups dropped outside of the group (and not dropped onto another group) are removed from the group (but remain in the dataset). When you set this to true, revert is automatically forced to false.

  • dropOverride False by default. If true, Nodes dropped onto other Groups first have any rules established by this Group applied. For instance, if the Groups stated prune:true, then the Node would be removed from the dataset rather than be dropped onto another Group.

  • autoSize

  • autoGrow

  • autoShrink

Collapsing/Expanding Groups#

You can collapse/expand a group using the collapseGroup and expandGroup methods on the Surface widget. When you collapse a Group, any Edges from any of the member Nodes/Groups in the Group to Nodes/Groups outside of the Group are relocated to the Group's container, and a CSS class is applied to the Group's container, indicating the collapsed state. When you subsequently expand the Group, the Edges are placed back onto their appropriate Nodes/Groups.

It is important to note that when a Group is collapsed, the jsPlumb Toolkit does not hide the member Nodes automatically for you. But a CSS class of jtk-group-collapsed is added to the Group's container, for you to handle this in your CSS.

The Endpoint and Anchor to be used in the collapsed state can be specified in the Group definition in the view:

toolkit.render({  ...  view:{        groups:{      "groupType1":{          templateId:"tmplGroupType1",          endpoint:"Blank",          anchor:"Continuous"      }    }  }  ...});

Any valid Anchor/Endpoint (including custom Endpoints) can be used here.

Magnetizing a collapsed/expanded Group#

By default, the Surface widget will run the Magnetizer whenever a group is collapsed or expanded: when a Group is expanded, surrounding elements are adjusted so as to ensure the group does not intersect with any other element. When a group is collapsed, the rest of the elements in the view are gathered in towards the collapsed group. This behaviour can be switched off - see the afterGroupChange flag in the Surface widget magnetizer options

Templating#

Specifying the canvas#

It is not necessarily the case that you wish to use your entire Group template as the parent of the Group's members. You can set a data-jtk-group-content attribute on the element that you wish to have acting as the parent for the members:

<script type="jtk" id="tmplMyGroup">    <div class="aGroup">        <h1>${title}</h1>        <div data-jtk-group-content="true" class="aGroupInner">            <!-- Child elements go here -->        </div>    </div></script>

Whenever a Group is resized by the auto sizing code in the Surface, the Surface looks for an element in the Group with this attribute, and if found, this is the element to which the Surface applies the change of size. Otherwise the size is applied to the Group's main element. Keep this in mind from a CSS perspective: your CSS should allow the size of the content area to mandate the size of its parent. Scroll/auto overflow is not supported inside a Group element.

TOP


Drag and Drop Groups#

You can drag Groups from a palette onto a Surface using the Drop Manager, with two differences from how you would do this to drag a Nide:

  • you need to set data-jtk-is-group="true" as an attribute on an element that you wish to drag on to the Surface as a Group
  • the current GroupFactory will be called to prepare a dataset for your new Group, rather than the current NodeFactory.

Here's an example from the Groups demo that ships with the Toolkit (showing both Nodes and Groups, but we're just going to focus on the Groups setup):

<div class="sidebar node-palette">    <div title="Drag Node to canvas" data-node-type="node" class="sidebar-item">        <i class="icon-tablet"></i>Drag Node    </div>    <div title="Drag Group to canvas" data-jtk-is-group="true" data-node-type="group" class="sidebar-item">        <i class="icon-tablet"></i>Drag Group    </div></div>

The Toolkit instance is configured with both a nodeFactory and a groupFactory:

const toolkit = jsPlumbToolkitBrowserUI.newInstance({    groupFactory:(type:string, data:ObjectData, callback:(o:ObjectData)=>any) => {        data.title = "Group " + (toolkit.getGroupCount() + 1)        callback(data)    },    nodeFactory:(type:string, data:ObjectData, callback:(o:ObjectData)=>any) => {        data.name = (toolkit.getNodeCount() + 1)        callback(data)    }});

...and the call to configure the drag/drop is no different to that which you'd make just for Nodes:

new SurfaceDropManager({    surface:renderer,    source:document.querySelector(".node-palette"),    selector:"[data-node-type]",    dataGenerator:function(e) {        return {            type:"default"        };    }});

TOP


Autosizing Groups#

Groups can be configured to automatically resize themselves to encompass the extents of their child Nodes, via the autoSize flag on a Group definition in a View:

 view:{     groups:{         "groupType1":{             templateId:"tmplGroupTypeOne",             autoSize:true         },         "groupType2":{             templateId:"tmplGroupTypeOne",             autoSize:true,             maxSize:[600,600]         }     } }

In this example, both Group types are declared to auto size, but groupType2 will grow to a maximum of 600 pixels in each axis.

Autosizing is run after a data load (ie. you call toolkit.load(...)), or when data exists in a Toolkit instance and you call toolkit.render(...).

You can run auto sizing on demand (on all Groups):


surface.autoSizeGroups()

or on a single group:


surface.sizeGroupToFit(group:Group)

TOP


Layouts#

By default, every group has an Absolute layout assigned to it. If your node data has left/top properties in it, these values will automatically be used to place nodes inside of their parent groups.

Specifying in the view#

To specify the layout for a specific type of group, set it in that group's entry in your view:


view:{
   ...
   groups:{     "someGroupType":{       ...       layout:{           type:HierarchicalLayout.type,   // (or just use 'Hierarchical' as a string            options:{               orientation:"vertical"           }       }     }   }
   ...}

The format of the layout parameter is identical to the layout parameter in the root of the view.

Layout on demand#

You can force a layout in a group like this:

 surface.relayoutGroup(group:string|Group)

This will cause a layout to be run immediately on the given Group (which may be passed in as the Group object, or just its id)

Relationship to group size#

By default, a layout in a group will cause the auto size routine to be run for the group immediately afterwards.