BPMN shapes
JointJS+ provides you with a set of Business Process Model and Notation 2.0 shapes. They are designed to be easily configurable with the Inspector.
The plugin consists of flow shapes (Activities, Events, Gateways), connection objects (Flows), several icons and artifacts (DataObjects, Groups, Annotation). It also covers models introduced in version 2.0: Conversations and Connections.
The BPMN shapes are contained inside shapes.bpmn2
namespace and you can use them as we described earlier.
Basic shapesβ
Here is an example of basic BPMN shapes:
Markers handlingβ
Multiple shapes in BPMN namespace contain different markers. The marker value is stored as a data attribute on the marker icon (SVG path) element. An interaction with the marker icon can be caught and handled on the dia.Paper. In the example below the model's marker name is displayed on interaction with the icon.
paper.on('element:marker:pointerdown', (cellView, evt) => {
const markerName = evt.target.dataset.iconType;
if (markerName) {
alert(markerName);
}
});
Poolsβ
JointJS+ also provides composite pool shapes to use in your applications: CompositePool as the outer rectangle, which may contain several Swimlanes and Phases. These map straightforwardly to BPMN pools, lanes and milestones.
JointJS+ composite pool shapes come in two distinct orientations:
HorizontalPool
/HeaderedHorizontalPool
with embeddedHorizontalSwimlane
andVerticalPhase
elements.VerticalPool
/HeaderedVerticalPool
with embeddedVerticalSwimlane
andHorizontalPhase
elements.
Swimlanes and phases of composite pools can be added / removed using built-in methods:
compositePool.addSwimlane()
andcompositePool.removeSwimlane()
compositePool.addPhase()
andcompositePool.removePhase()
Each individual composite pool shape can be be independently styled, just like any other JointJS element.
Here is an example of basic composite pool shapes (HorizontalPool compatible shapes on the left, VerticalPool compatible shapes on the right):
Here is an example of complex composite pool shapes:
BPMNFreeTransformβ
The BPMNFreeTransform
UI component renders a special FreeTransform
control panel above a CompositePool / Swimlane / Phase / basic BPMN shape. It respects CompositePool's model attributes and keeps all components within a CompositePool in sync while any one of them is resizing.
Exampleβ
paper.on('element:pointerup', cellView => {
new ui.BPMNFreeTransform({ cellView });
});
You can see BPMNFreeTransform
in action in the Pools examples above.
Legacy Poolsβ
Deprecated. Use the new composite pool functionality.
JointJS+ also provides complex Pool shapes to use in your applications.
The BPMN Pool consists of lanes and milestones. Let's describe how they work in terms of JointJS shapes.
Each lane and milestone group has a unique id that can be used to target those groups or individual elements within groups. Passing string to id
property of a lane or milestone will create an alias to that given group.
Custom id must be unique and the same id cannot be assigned for more than 1 milestone or 1 lane group.
Lane group id starts with a 'lanes' prefix and a combination of numbers that is based on position within an array and nest level (sublanes). Each lane group contains the following elements:
- lane rectangle (prefix
lane
) - header rectangle (prefix
header
, note that header won't be added if label is undefined) - label text (prefix
label
, note that label won't be added if it is undefined)
For example let's consider this lanes structure:
lanes: [
{
label: 'lane' // group id: 'lanes_0', lane id: 'lane_0', header id: 'header_0', label id: 'label_0'
},
{
label: 'lane with sublanes', // group id: 'lanes_1', lane id: 'lane_1', header id: 'header_1', label id: 'label_1'
sublanes: [
{
label: 'sublane 1' // group id: 'lanes_1_0', lane id: 'lane_1_0', header id: 'header_1_0', label id: 'label_1_0'
},
{
label: 'sublane 2' // group id: 'lanes_1_1', lane id: 'lane_1_1', header id: 'header_1_1', label id: 'label_1_1'
}
]
},
// example with custom id
{
id: 'customId',
label: 'lane with custom id', // group id: 'lanes_customId', lane id: 'lane_customId', header id: 'header_customId', label id: 'label_customId'
sublanes: [
{
label: 'sublane 1' // group id: 'lanes_2_0', lane id: 'lane_2_0', header id: 'header_2_0', label id: 'label_2_0'
},
{
id: 'sublaneId',
label: 'sublane 2' // group id: 'lanes_sublaneId', lane id: 'lane_sublaneId', header id: 'header_sublaneId', label id: 'label_sublaneId'
}
]
}
]
Similarly, each milestone group id starts with a 'milestone' prefix. Milestone group contains the following elements:
- header rectangle (prefix
milestoneHeader
) - label text (prefix
milestoneLabel
) - milestone line (prefix
milestoneLine
)
Example milestones structure:
milestones: [
{
label: 'milestone 1' // group id: 'milestone_0', header id: 'milestoneHeader_0', label id: 'milestoneLabel_0', line id: 'milestoneLine_0'
},
{
label: 'milestone 2' // group id: 'milestone_1', header id: 'milestoneHeader_1', label id: 'milestoneLabel_1', line id: 'milestoneLine_1'
},
// example with custom id
{
id: 'customId',
label: 'milestone 3' // group id: 'milestone_customId', header id: 'milestoneHeader_customId', label id: 'milestoneLabel_customId', line id: 'milestoneLine_customId'
}
]
Here is an example of Pool shapes:
SwimlaneBoundary element toolβ
The SwimlaneBoundary
element tool renders a rectangular border to show the bounding box of a bpmn2.Pool
and bpmn2.HeaderedPool
pool lane.
Exampleβ
const boundaryTool = new elementTools.SwimlaneBoundary({
laneId: 'customLaneId',
padding: 20
});
SwimlaneTransform element toolβ
The SwimlaneTransform
element tool allows you to resize lanes in bpmn2.Pool
or bpmn2.HeaderedPool
shape,
from the UI. It renders 4 handles around selected lane which you drag and use to resize lane in particular direction.
Examplesβ
const transformTool = new SwimlaneTransform({
laneId: 'customLaneId',
padding: 8,
minSize: 50,
focusOpacity: 0.5
constraintsPadding: 20,
});
Example with custom constraint points:
graph.on('change:parent', function(element, parentId) {
if (parentId) {
const pool = graph.getCell(parentId);
const [laneId] = pool.getLanesFromPoint(element.getBBox().center());
element.prop('laneId', laneId);
} else {
element.prop('laneId', null);
}
});
const transformTool = new SwimlaneTransform({
laneId: 'customLaneId',
constraintsPadding: 30,
minSizeConstraints: (model, laneId, handleSide) => {
const minPoints = [];
const embedCells = model.getEmbeddedCells();
if (embedCells.length === 0) return;
embedCells.forEach(cell => {
if (cell.prop('laneId') === laneId) {
const cellBBox = cell.getBBox();
minPoints.push(cellBBox.topLeft());
minPoints.push(cellBBox.topRight());
minPoints.push(cellBBox.bottomLeft());
minPoints.push(cellBBox.bottomRight());
}
});
if (handleSide === 'left' || handleSide === 'right') {
const embedsBBox = model.graph.getCellsBBox(embedCells);
// add left most and right most points
minPoints.push(embedsBBox.origin());
minPoints.push(embedsBBox.corner());
}
return minPoints;
},
maxSizeConstraints: (model, laneId) => {
const maxPoints = [];
const embedCells = model.getEmbeddedCells();
if (embedCells.length === 0) return;
embedCells.forEach(cell => {
if (cell.prop('laneId') !== laneId) {
const cellBBox = cell.getBBox();
maxPoints.push(cellBBox.topLeft());
maxPoints.push(cellBBox.topRight());
maxPoints.push(cellBBox.bottomLeft());
maxPoints.push(cellBBox.bottomRight());
}
});
return maxPoints;
}
});