feat(modeling): adjust label location based on free space
Reacts on connection create, layout, reconnect and waypoint update to find a suitable place for the label and reposition it. Closes #738
This commit is contained in:
parent
a830c1e1e0
commit
5761e01ffe
|
@ -0,0 +1,189 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var inherits = require('inherits');
|
||||||
|
|
||||||
|
var getOrientation = require('diagram-js/lib/layout/LayoutUtil').getOrientation,
|
||||||
|
getMid = require('diagram-js/lib/layout/LayoutUtil').getMid,
|
||||||
|
asTRBL = require('diagram-js/lib/layout/LayoutUtil').asTRBL,
|
||||||
|
substract = require('diagram-js/lib/util/Math').substract;
|
||||||
|
|
||||||
|
var LabelUtil = require('../../../util/LabelUtil');
|
||||||
|
|
||||||
|
var hasExternalLabel = LabelUtil.hasExternalLabel;
|
||||||
|
|
||||||
|
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A component that makes sure that external labels are added
|
||||||
|
* together with respective elements and properly updated (DI wise)
|
||||||
|
* during move.
|
||||||
|
*
|
||||||
|
* @param {EventBus} eventBus
|
||||||
|
* @param {Modeling} modeling
|
||||||
|
*/
|
||||||
|
function AdaptiveLabelPositioningBehavior(eventBus, modeling) {
|
||||||
|
|
||||||
|
CommandInterceptor.call(this, eventBus);
|
||||||
|
|
||||||
|
this.postExecuted([
|
||||||
|
'connection.create',
|
||||||
|
'connection.layout',
|
||||||
|
'connection.reconnectEnd',
|
||||||
|
'connection.reconnectStart',
|
||||||
|
'connection.updateWaypoints'
|
||||||
|
], function(event) {
|
||||||
|
|
||||||
|
var context = event.context,
|
||||||
|
connection = context.connection;
|
||||||
|
|
||||||
|
var source = connection.source,
|
||||||
|
target = connection.target;
|
||||||
|
|
||||||
|
checkLabelAdjustment(source);
|
||||||
|
checkLabelAdjustment(target);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function checkLabelAdjustment(element) {
|
||||||
|
|
||||||
|
// skip hidden or non-existing labels
|
||||||
|
if (!hasExternalLabel(element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var optimalPosition = getOptimalPosition(element);
|
||||||
|
|
||||||
|
// no optimal position found
|
||||||
|
if (!optimalPosition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustLabelPosition(element, optimalPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ELEMENT_LABEL_DISTANCE = 10;
|
||||||
|
|
||||||
|
function adjustLabelPosition(element, orientation) {
|
||||||
|
|
||||||
|
var elementMid = getMid(element),
|
||||||
|
label = element.label,
|
||||||
|
labelMid = getMid(label);
|
||||||
|
|
||||||
|
var elementTrbl = asTRBL(element);
|
||||||
|
|
||||||
|
var newLabelMid;
|
||||||
|
|
||||||
|
switch (orientation) {
|
||||||
|
case 'top':
|
||||||
|
newLabelMid = {
|
||||||
|
x: elementMid.x,
|
||||||
|
y: elementTrbl.top - ELEMENT_LABEL_DISTANCE - label.height / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'left':
|
||||||
|
|
||||||
|
newLabelMid = {
|
||||||
|
x: elementTrbl.left - ELEMENT_LABEL_DISTANCE - label.width / 2,
|
||||||
|
y: elementMid.y
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'bottom':
|
||||||
|
|
||||||
|
newLabelMid = {
|
||||||
|
x: elementMid.x,
|
||||||
|
y: elementTrbl.bottom + ELEMENT_LABEL_DISTANCE + label.height / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'right':
|
||||||
|
|
||||||
|
newLabelMid = {
|
||||||
|
x: elementTrbl.right + ELEMENT_LABEL_DISTANCE + label.width / 2,
|
||||||
|
y: elementMid.y
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var delta = substract(newLabelMid, labelMid);
|
||||||
|
|
||||||
|
modeling.moveShape(label, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inherits(AdaptiveLabelPositioningBehavior, CommandInterceptor);
|
||||||
|
|
||||||
|
AdaptiveLabelPositioningBehavior.$inject = [
|
||||||
|
'eventBus',
|
||||||
|
'modeling'
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = AdaptiveLabelPositioningBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the optimal label position around an element
|
||||||
|
* or _undefined_, if none was found.
|
||||||
|
*
|
||||||
|
* @param {Shape} element
|
||||||
|
*
|
||||||
|
* @return {String} positioning identifier
|
||||||
|
*/
|
||||||
|
function getOptimalPosition(element) {
|
||||||
|
|
||||||
|
var labelMid = getMid(element.label);
|
||||||
|
|
||||||
|
var elementMid = getMid(element);
|
||||||
|
|
||||||
|
var labelOrientation = getApproximateOrientation(elementMid, labelMid);
|
||||||
|
|
||||||
|
if (!isAligned(labelOrientation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var takenAlignments = [].concat(
|
||||||
|
element.incoming.map(function(c) {
|
||||||
|
return c.waypoints[c.waypoints.length - 2 ];
|
||||||
|
}),
|
||||||
|
element.outgoing.map(function(c) {
|
||||||
|
return c.waypoints[1];
|
||||||
|
})
|
||||||
|
).map(function(point) {
|
||||||
|
return getApproximateOrientation(elementMid, point);
|
||||||
|
});
|
||||||
|
|
||||||
|
var freeAlignments = ALIGNMENTS.filter(function(alignment) {
|
||||||
|
|
||||||
|
return takenAlignments.indexOf(alignment) === -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTHING TO DO; label already aligned a.O.K.
|
||||||
|
if (freeAlignments.indexOf(labelOrientation) !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freeAlignments[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var ALIGNMENTS = [
|
||||||
|
'top',
|
||||||
|
'bottom',
|
||||||
|
'left',
|
||||||
|
'right'
|
||||||
|
];
|
||||||
|
|
||||||
|
function getApproximateOrientation(p0, p1) {
|
||||||
|
return getOrientation(p1, p0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAligned(orientation) {
|
||||||
|
return ALIGNMENTS.indexOf(orientation) !== -1;
|
||||||
|
}
|
|
@ -1,44 +1,46 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
__init__: [
|
__init__: [
|
||||||
|
'adaptiveLabelPositioningBehavior',
|
||||||
'appendBehavior',
|
'appendBehavior',
|
||||||
'copyPasteBehavior',
|
'copyPasteBehavior',
|
||||||
'createBoundaryEventBehavior',
|
'createBoundaryEventBehavior',
|
||||||
'createDataObjectBehavior',
|
'createDataObjectBehavior',
|
||||||
'dropOnFlowBehavior',
|
|
||||||
'createParticipantBehavior',
|
'createParticipantBehavior',
|
||||||
'dataInputAssociationBehavior',
|
'dataInputAssociationBehavior',
|
||||||
'deleteLaneBehavior',
|
'deleteLaneBehavior',
|
||||||
|
'dropOnFlowBehavior',
|
||||||
'importDockingFix',
|
'importDockingFix',
|
||||||
'labelBehavior',
|
'labelBehavior',
|
||||||
'modelingFeedback',
|
'modelingFeedback',
|
||||||
|
'removeElementBehavior',
|
||||||
'removeParticipantBehavior',
|
'removeParticipantBehavior',
|
||||||
'replaceConnectionBehavior',
|
'replaceConnectionBehavior',
|
||||||
'replaceElementBehaviour',
|
'replaceElementBehaviour',
|
||||||
'resizeLaneBehavior',
|
'resizeLaneBehavior',
|
||||||
'unsetDefaultFlowBehavior',
|
'toggleElementCollapseBehaviour',
|
||||||
'updateFlowNodeRefsBehavior',
|
|
||||||
'removeElementBehavior',
|
|
||||||
'unclaimIdBehavior',
|
'unclaimIdBehavior',
|
||||||
'toggleElementCollapseBehaviour'
|
'unsetDefaultFlowBehavior',
|
||||||
|
'updateFlowNodeRefsBehavior'
|
||||||
],
|
],
|
||||||
|
adaptiveLabelPositioningBehavior: [ 'type', require('./AdaptiveLabelPositioningBehavior') ],
|
||||||
appendBehavior: [ 'type', require('./AppendBehavior') ],
|
appendBehavior: [ 'type', require('./AppendBehavior') ],
|
||||||
copyPasteBehavior: [ 'type', require('./CopyPasteBehavior') ],
|
copyPasteBehavior: [ 'type', require('./CopyPasteBehavior') ],
|
||||||
createBoundaryEventBehavior: [ 'type', require('./CreateBoundaryEventBehavior') ],
|
createBoundaryEventBehavior: [ 'type', require('./CreateBoundaryEventBehavior') ],
|
||||||
createDataObjectBehavior: [ 'type', require('./CreateDataObjectBehavior') ],
|
createDataObjectBehavior: [ 'type', require('./CreateDataObjectBehavior') ],
|
||||||
dropOnFlowBehavior: [ 'type', require('./DropOnFlowBehavior') ],
|
|
||||||
createParticipantBehavior: [ 'type', require('./CreateParticipantBehavior') ],
|
createParticipantBehavior: [ 'type', require('./CreateParticipantBehavior') ],
|
||||||
dataInputAssociationBehavior: [ 'type', require('./DataInputAssociationBehavior') ],
|
dataInputAssociationBehavior: [ 'type', require('./DataInputAssociationBehavior') ],
|
||||||
deleteLaneBehavior: [ 'type', require('./DeleteLaneBehavior') ],
|
deleteLaneBehavior: [ 'type', require('./DeleteLaneBehavior') ],
|
||||||
|
dropOnFlowBehavior: [ 'type', require('./DropOnFlowBehavior') ],
|
||||||
importDockingFix: [ 'type', require('./ImportDockingFix') ],
|
importDockingFix: [ 'type', require('./ImportDockingFix') ],
|
||||||
labelBehavior: [ 'type', require('./LabelBehavior') ],
|
labelBehavior: [ 'type', require('./LabelBehavior') ],
|
||||||
modelingFeedback: [ 'type', require('./ModelingFeedback') ],
|
modelingFeedback: [ 'type', require('./ModelingFeedback') ],
|
||||||
removeParticipantBehavior: [ 'type', require('./RemoveParticipantBehavior') ],
|
|
||||||
replaceConnectionBehavior: [ 'type', require('./ReplaceConnectionBehavior') ],
|
replaceConnectionBehavior: [ 'type', require('./ReplaceConnectionBehavior') ],
|
||||||
|
removeParticipantBehavior: [ 'type', require('./RemoveParticipantBehavior') ],
|
||||||
replaceElementBehaviour: [ 'type', require('./ReplaceElementBehaviour') ],
|
replaceElementBehaviour: [ 'type', require('./ReplaceElementBehaviour') ],
|
||||||
resizeLaneBehavior: [ 'type', require('./ResizeLaneBehavior') ],
|
resizeLaneBehavior: [ 'type', require('./ResizeLaneBehavior') ],
|
||||||
unsetDefaultFlowBehavior: [ 'type', require('./UnsetDefaultFlowBehavior') ],
|
|
||||||
updateFlowNodeRefsBehavior: [ 'type', require('./UpdateFlowNodeRefsBehavior') ],
|
|
||||||
removeElementBehavior: [ 'type', require('./RemoveElementBehavior') ],
|
removeElementBehavior: [ 'type', require('./RemoveElementBehavior') ],
|
||||||
|
toggleElementCollapseBehaviour : [ 'type', require('./ToggleElementCollapseBehaviour') ],
|
||||||
unclaimIdBehavior: [ 'type', require('./UnclaimIdBehavior') ],
|
unclaimIdBehavior: [ 'type', require('./UnclaimIdBehavior') ],
|
||||||
toggleElementCollapseBehaviour : [ 'type', require('./ToggleElementCollapseBehaviour') ]
|
updateFlowNodeRefsBehavior: [ 'type', require('./UpdateFlowNodeRefsBehavior') ],
|
||||||
|
unsetDefaultFlowBehavior: [ 'type', require('./UnsetDefaultFlowBehavior') ]
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="sid-38422fae-e03e-43a3-bef4-bd33b32041b2" targetNamespace="http://bpmn.io/bpmn" exporter="http://bpmn.io" exporterVersion="0.10.1">
|
||||||
|
<process id="Process_1" isExecutable="false">
|
||||||
|
<exclusiveGateway id="LabelBottom" name="BOTTOM" />
|
||||||
|
<exclusiveGateway id="LabelLeft" name="LEFT" />
|
||||||
|
<exclusiveGateway id="LabelTop" name="TOP">
|
||||||
|
<outgoing>SequenceFlow_1</outgoing>
|
||||||
|
</exclusiveGateway>
|
||||||
|
<exclusiveGateway id="LabelRight" name="RIGHT">
|
||||||
|
<outgoing>SequenceFlow_2</outgoing>
|
||||||
|
</exclusiveGateway>
|
||||||
|
<exclusiveGateway id="LabelBottomLeft" name="BOTTOM_LEFT" />
|
||||||
|
<exclusiveGateway id="LabelBottom_2" name="BOTTOM_2">
|
||||||
|
<incoming>SequenceFlow_2</incoming>
|
||||||
|
</exclusiveGateway>
|
||||||
|
<exclusiveGateway id="LabelBottom_3" name="BOTTOM_3">
|
||||||
|
<incoming>SequenceFlow_1</incoming>
|
||||||
|
</exclusiveGateway>
|
||||||
|
<sequenceFlow id="SequenceFlow_1" name="1" sourceRef="LabelTop" targetRef="LabelBottom_3" />
|
||||||
|
<sequenceFlow id="SequenceFlow_2" name="2" sourceRef="LabelRight" targetRef="LabelBottom_2" />
|
||||||
|
<exclusiveGateway id="LabelImpossible" name="IMPOSSIBLE">
|
||||||
|
<incoming>SequenceFlow_1qmllcx</incoming>
|
||||||
|
<incoming>SequenceFlow_0s993e4</incoming>
|
||||||
|
<incoming>SequenceFlow_022at7e</incoming>
|
||||||
|
</exclusiveGateway>
|
||||||
|
<task id="Task">
|
||||||
|
<outgoing>SequenceFlow_1qmllcx</outgoing>
|
||||||
|
<outgoing>SequenceFlow_0s993e4</outgoing>
|
||||||
|
<outgoing>SequenceFlow_022at7e</outgoing>
|
||||||
|
</task>
|
||||||
|
<sequenceFlow id="SequenceFlow_1qmllcx" sourceRef="Task" targetRef="LabelImpossible" />
|
||||||
|
<sequenceFlow id="SequenceFlow_0s993e4" sourceRef="Task" targetRef="LabelImpossible" />
|
||||||
|
<sequenceFlow id="SequenceFlow_022at7e" sourceRef="Task" targetRef="LabelImpossible" />
|
||||||
|
</process>
|
||||||
|
<bpmndi:BPMNDiagram id="BpmnDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="Process_1">
|
||||||
|
<bpmndi:BPMNShape id="LabelBottom_di" bpmnElement="LabelBottom" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="113" y="89" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="107" y="129" width="49" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="LabelLeft_di" bpmnElement="LabelLeft" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="309" y="82" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="263" y="101" width="30" height="24" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="LabelTop_di" bpmnElement="LabelTop" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="309" y="225" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="322" y="198" width="24" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="LabelRight_di" bpmnElement="LabelRight" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="106" y="225" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="168" y="244" width="37" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="LabelBottomLeft_di" bpmnElement="LabelBottomLeft" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="532" y="82" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="487" y="146" width="83" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="LabelBottom_2_di" bpmnElement="LabelBottom_2" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="106" y="370" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="101" y="424" width="61" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="LabelBottom_3_di" bpmnElement="LabelBottom_3" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="309" y="370" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="304" y="424" width="61" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_1_di" bpmnElement="SequenceFlow_1">
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="334" y="275" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="334" y="370" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="346" y="317" width="7" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_2_di" bpmnElement="SequenceFlow_2">
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="131" y="275" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="131" y="370" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="142" y="317" width="8" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="LabelImpossible_di" bpmnElement="LabelImpossible" isMarkerVisible="true">
|
||||||
|
<omgdc:Bounds x="633" y="308" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="694" y="327" width="68" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Task_di" bpmnElement="Task">
|
||||||
|
<omgdc:Bounds x="776" y="293" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_1qmllcx_di" bpmnElement="SequenceFlow_1qmllcx">
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="826" y="293" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="826" y="202" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="658" y="202" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="658" y="308" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="742" y="181" width="0" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_0s993e4_di" bpmnElement="SequenceFlow_0s993e4">
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="826" y="373" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="826" y="424" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="658" y="424" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="658" y="358" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="742" y="403" width="0" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_022at7e_di" bpmnElement="SequenceFlow_022at7e">
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="845" y="373" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="845" y="453" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="594" y="453" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="594" y="333" />
|
||||||
|
<omgdi:waypoint xsi:type="omgdc:Point" x="633" y="333" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<omgdc:Bounds x="719.5" y="432" width="0" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</definitions>
|
|
@ -0,0 +1,241 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../../../../TestHelper');
|
||||||
|
|
||||||
|
/* global bootstrapModeler, inject */
|
||||||
|
|
||||||
|
var getOrientation = require('diagram-js/lib/layout/LayoutUtil').getOrientation;
|
||||||
|
|
||||||
|
var modelingModule = require('../../../../../lib/features/modeling'),
|
||||||
|
coreModule = require('../../../../../lib/core');
|
||||||
|
|
||||||
|
|
||||||
|
describe('modeling/behavior - AdaptiveLabelPositioningBehavior', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('./AdaptiveLabelPositioningBehavior.bpmn');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, {
|
||||||
|
modules: [
|
||||||
|
modelingModule,
|
||||||
|
coreModule
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
function expectLabelOrientation(element, expectedOrientation) {
|
||||||
|
|
||||||
|
var label = element.label;
|
||||||
|
|
||||||
|
// assume
|
||||||
|
expect(label).to.exist;
|
||||||
|
|
||||||
|
// when
|
||||||
|
var orientation = getOrientation(label, element);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(orientation).to.eql(expectedOrientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
describe('on connect', function() {
|
||||||
|
|
||||||
|
it('should move label from LEFT to TOP', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelBottom'),
|
||||||
|
target = elementRegistry.get('LabelLeft');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'bottom');
|
||||||
|
expectLabelOrientation(target, 'top');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should move label from BOTTOM to TOP', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelBottom'),
|
||||||
|
target = elementRegistry.get('LabelRight');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'top');
|
||||||
|
expectLabelOrientation(target, 'right');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should move label from RIGHT to TOP', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelRight'),
|
||||||
|
target = elementRegistry.get('LabelTop');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'top');
|
||||||
|
expectLabelOrientation(target, 'top');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should move label from TOP to LEFT', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelTop'),
|
||||||
|
target = elementRegistry.get('LabelLeft');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'left');
|
||||||
|
expectLabelOrientation(target, 'left');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should move label from TOP to LEFT', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelTop'),
|
||||||
|
target = elementRegistry.get('LabelLeft');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'left');
|
||||||
|
expectLabelOrientation(target, 'left');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should move label from TOP to LEFT (inverse)', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelLeft'),
|
||||||
|
target = elementRegistry.get('LabelTop');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(target, 'left');
|
||||||
|
expectLabelOrientation(source, 'left');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should keep unaligned labels AS IS', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelBottomLeft'),
|
||||||
|
target = elementRegistry.get('LabelTop');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'bottom');
|
||||||
|
expectLabelOrientation(target, 'top');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should keep label where it is, if no options', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelImpossible'),
|
||||||
|
target = elementRegistry.get('Task');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.connect(source, target);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'right');
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('on reconnect', function() {
|
||||||
|
|
||||||
|
it('should move label from TOP to BOTTOM', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_1'),
|
||||||
|
source = elementRegistry.get('LabelTop'),
|
||||||
|
target = elementRegistry.get('LabelLeft');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.reconnectEnd(connection, target, { x: target.x + target.width / 2, y: target.y });
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'bottom');
|
||||||
|
expectLabelOrientation(target, 'left');
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('on target move / layout', function() {
|
||||||
|
|
||||||
|
it('should move label from TOP to BOTTOM', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var source = elementRegistry.get('LabelTop'),
|
||||||
|
target = elementRegistry.get('LabelBottom_3');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.moveElements([ source ], { x: 0, y: 300 });
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'bottom');
|
||||||
|
expectLabelOrientation(target, 'top');
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('on waypoints update', function() {
|
||||||
|
|
||||||
|
it('should move label from RIGHT to TOP', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_2'),
|
||||||
|
source = elementRegistry.get('LabelRight'),
|
||||||
|
target = elementRegistry.get('LabelBottom');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.updateWaypoints(connection, [
|
||||||
|
{
|
||||||
|
original: { x: 131, y: 248 },
|
||||||
|
x: 131,
|
||||||
|
y: 248
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 250,
|
||||||
|
y: 248
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 250,
|
||||||
|
y: 394
|
||||||
|
},
|
||||||
|
{
|
||||||
|
original: { x: 131, y: 394 },
|
||||||
|
x: 131,
|
||||||
|
y: 394
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelOrientation(source, 'top');
|
||||||
|
expectLabelOrientation(target, 'bottom');
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue