mirror of
https://github.com/sartography/bpmn-js.git
synced 2025-02-23 06:08:19 +00:00
chore(auto-place): move common feature to diagram-js
https://github.com/bpmn-io/dmn-js/issues/470
This commit is contained in:
parent
6e38e2b827
commit
e03a4b2c59
@ -1,58 +0,0 @@
|
||||
import { getNewShapePosition } from './AutoPlaceUtil';
|
||||
|
||||
|
||||
/**
|
||||
* A service that places elements connected to existing ones
|
||||
* to an appropriate position in an _automated_ fashion.
|
||||
*
|
||||
* @param {EventBus} eventBus
|
||||
* @param {Modeling} modeling
|
||||
*/
|
||||
export default function AutoPlace(eventBus, modeling) {
|
||||
|
||||
function emit(event, payload) {
|
||||
return eventBus.fire(event, payload);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append shape to source at appropriate position.
|
||||
*
|
||||
* @param {djs.model.Shape} source
|
||||
* @param {djs.model.Shape} shape
|
||||
*
|
||||
* @return {djs.model.Shape} appended shape
|
||||
*/
|
||||
this.append = function(source, shape) {
|
||||
|
||||
emit('autoPlace.start', {
|
||||
source: source,
|
||||
shape: shape
|
||||
});
|
||||
|
||||
// allow others to provide the position
|
||||
var position = emit('autoPlace', {
|
||||
source: source,
|
||||
shape: shape
|
||||
});
|
||||
|
||||
if (!position) {
|
||||
position = getNewShapePosition(source, shape);
|
||||
}
|
||||
|
||||
var newShape = modeling.appendShape(source, shape, position, source.parent);
|
||||
|
||||
emit('autoPlace.end', {
|
||||
source: source,
|
||||
shape: newShape
|
||||
});
|
||||
|
||||
return newShape;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
AutoPlace.$inject = [
|
||||
'eventBus',
|
||||
'modeling'
|
||||
];
|
@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Select element after auto placement.
|
||||
*
|
||||
* @param {EventBus} eventBus
|
||||
* @param {Selection} selection
|
||||
*/
|
||||
export default function AutoPlaceSelectionBehavior(eventBus, selection) {
|
||||
|
||||
eventBus.on('autoPlace.end', 500, function(e) {
|
||||
selection.select(e.shape);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
AutoPlaceSelectionBehavior.$inject = [
|
||||
'eventBus',
|
||||
'selection'
|
||||
];
|
@ -1,430 +0,0 @@
|
||||
import { is } from '../../util/ModelUtil';
|
||||
import { isAny } from '../modeling/util/ModelingUtil';
|
||||
|
||||
import {
|
||||
getMid,
|
||||
asTRBL,
|
||||
getOrientation
|
||||
} from 'diagram-js/lib/layout/LayoutUtil';
|
||||
|
||||
import {
|
||||
find,
|
||||
reduce
|
||||
} from 'min-dash';
|
||||
|
||||
var DEFAULT_HORIZONTAL_DISTANCE = 50;
|
||||
|
||||
var MAX_HORIZONTAL_DISTANCE = 250;
|
||||
|
||||
// padding to detect element placement
|
||||
var PLACEMENT_DETECTION_PAD = 10;
|
||||
|
||||
/**
|
||||
* Find the new position for the target element to
|
||||
* connect to source.
|
||||
*
|
||||
* @param {djs.model.Shape} source
|
||||
* @param {djs.model.Shape} element
|
||||
*
|
||||
* @return {Point}
|
||||
*/
|
||||
export function getNewShapePosition(source, element) {
|
||||
|
||||
if (is(element, 'bpmn:TextAnnotation')) {
|
||||
return getTextAnnotationPosition(source, element);
|
||||
}
|
||||
|
||||
if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
|
||||
return getDataElementPosition(source, element);
|
||||
}
|
||||
|
||||
if (is(element, 'bpmn:FlowNode')) {
|
||||
return getFlowNodePosition(source, element);
|
||||
}
|
||||
|
||||
return getDefaultPosition(source, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always try to place element right of source;
|
||||
* compute actual distance from previous nodes in flow.
|
||||
*/
|
||||
export function getFlowNodePosition(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
var sourceMid = getMid(source);
|
||||
|
||||
var horizontalDistance = getFlowNodeDistance(source, element);
|
||||
|
||||
var orientation = 'left',
|
||||
rowSize = 80,
|
||||
margin = 30;
|
||||
|
||||
if (is(source, 'bpmn:BoundaryEvent')) {
|
||||
orientation = getOrientation(source, source.host, -25);
|
||||
|
||||
if (orientation.indexOf('top') !== -1) {
|
||||
margin *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
function getVerticalDistance(orient) {
|
||||
if (orient.indexOf('top') != -1) {
|
||||
return -1 * rowSize;
|
||||
} else if (orient.indexOf('bottom') != -1) {
|
||||
return rowSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
var position = {
|
||||
x: sourceTrbl.right + horizontalDistance + element.width / 2,
|
||||
y: sourceMid.y + getVerticalDistance(orientation)
|
||||
};
|
||||
|
||||
var escapeDirection = {
|
||||
y: {
|
||||
margin: margin,
|
||||
rowSize: rowSize
|
||||
}
|
||||
};
|
||||
|
||||
return deconflictPosition(source, element, position, escapeDirection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute best distance between source and target,
|
||||
* based on existing connections to and from source.
|
||||
*
|
||||
* @param {djs.model.Shape} source
|
||||
* @param {djs.model.Shape} element
|
||||
*
|
||||
* @return {number} distance
|
||||
*/
|
||||
export function getFlowNodeDistance(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
|
||||
// is connection a reference to consider?
|
||||
function isReference(c) {
|
||||
return is(c, 'bpmn:SequenceFlow');
|
||||
}
|
||||
|
||||
function toTargetNode(weight) {
|
||||
|
||||
return function(shape) {
|
||||
return {
|
||||
shape: shape,
|
||||
weight: weight,
|
||||
distanceTo: function(shape) {
|
||||
var shapeTrbl = asTRBL(shape);
|
||||
|
||||
return shapeTrbl.left - sourceTrbl.right;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function toSourceNode(weight) {
|
||||
return function(shape) {
|
||||
return {
|
||||
shape: shape,
|
||||
weight: weight,
|
||||
distanceTo: function(shape) {
|
||||
var shapeTrbl = asTRBL(shape);
|
||||
|
||||
return sourceTrbl.left - shapeTrbl.right;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// we create a list of nodes to take into consideration
|
||||
// for calculating the optimal flow node distance
|
||||
//
|
||||
// * weight existing target nodes higher than source nodes
|
||||
// * only take into account individual nodes once
|
||||
//
|
||||
var nodes = reduce([].concat(
|
||||
getTargets(source, isReference).map(toTargetNode(5)),
|
||||
getSources(source, isReference).map(toSourceNode(1))
|
||||
), function(nodes, node) {
|
||||
|
||||
// filter out shapes connected twice via source or target
|
||||
nodes[node.shape.id + '__weight_' + node.weight] = node;
|
||||
|
||||
return nodes;
|
||||
}, {});
|
||||
|
||||
// compute distances between source and incoming nodes;
|
||||
// group at the same time by distance and expose the
|
||||
// favourite distance as { fav: { count, value } }.
|
||||
var distancesGrouped = reduce(nodes, function(result, node) {
|
||||
|
||||
var shape = node.shape,
|
||||
weight = node.weight,
|
||||
distanceTo = node.distanceTo;
|
||||
|
||||
var fav = result.fav,
|
||||
currentDistance,
|
||||
currentDistanceCount,
|
||||
currentDistanceEntry;
|
||||
|
||||
currentDistance = distanceTo(shape);
|
||||
|
||||
// ignore too far away peers
|
||||
// or non-left to right modeled nodes
|
||||
if (currentDistance < 0 || currentDistance > MAX_HORIZONTAL_DISTANCE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
currentDistanceEntry = result[String(currentDistance)] =
|
||||
result[String(currentDistance)] || {
|
||||
value: currentDistance,
|
||||
count: 0
|
||||
};
|
||||
|
||||
// inc diff count
|
||||
currentDistanceCount = currentDistanceEntry.count += 1 * weight;
|
||||
|
||||
if (!fav || fav.count < currentDistanceCount) {
|
||||
result.fav = currentDistanceEntry;
|
||||
}
|
||||
|
||||
return result;
|
||||
}, { });
|
||||
|
||||
|
||||
if (distancesGrouped.fav) {
|
||||
return distancesGrouped.fav.value;
|
||||
} else {
|
||||
return DEFAULT_HORIZONTAL_DISTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always try to place text annotations top right of source.
|
||||
*/
|
||||
export function getTextAnnotationPosition(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
|
||||
var position = {
|
||||
x: sourceTrbl.right + element.width / 2,
|
||||
y: sourceTrbl.top - 50 - element.height / 2
|
||||
};
|
||||
|
||||
var escapeDirection = {
|
||||
y: {
|
||||
margin: -30,
|
||||
rowSize: 20
|
||||
}
|
||||
};
|
||||
|
||||
return deconflictPosition(source, element, position, escapeDirection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always put element bottom right of source.
|
||||
*/
|
||||
export function getDataElementPosition(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
|
||||
var position = {
|
||||
x: sourceTrbl.right - 10 + element.width / 2,
|
||||
y: sourceTrbl.bottom + 40 + element.width / 2
|
||||
};
|
||||
|
||||
var escapeDirection = {
|
||||
x: {
|
||||
margin: 30,
|
||||
rowSize: 30
|
||||
}
|
||||
};
|
||||
|
||||
return deconflictPosition(source, element, position, escapeDirection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always put element right of source per default.
|
||||
*/
|
||||
export function getDefaultPosition(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
|
||||
var sourceMid = getMid(source);
|
||||
|
||||
// simply put element right next to source
|
||||
return {
|
||||
x: sourceTrbl.right + DEFAULT_HORIZONTAL_DISTANCE + element.width / 2,
|
||||
y: sourceMid.y
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all connected elements around the given source.
|
||||
*
|
||||
* This includes:
|
||||
*
|
||||
* - connected elements
|
||||
* - host connected elements
|
||||
* - attachers connected elements
|
||||
*
|
||||
* @param {djs.model.Shape} source
|
||||
* @param {djs.model.Shape} element
|
||||
*
|
||||
* @return {Array<djs.model.Shape>}
|
||||
*/
|
||||
function getAutoPlaceClosure(source, element) {
|
||||
|
||||
var allConnected = getConnected(source);
|
||||
|
||||
if (source.host) {
|
||||
allConnected = allConnected.concat(getConnected(source.host));
|
||||
}
|
||||
|
||||
if (source.attachers) {
|
||||
allConnected = allConnected.concat(source.attachers.reduce(function(shapes, attacher) {
|
||||
return shapes.concat(getConnected(attacher));
|
||||
}, []));
|
||||
}
|
||||
|
||||
return allConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return target at given position, if defined.
|
||||
*
|
||||
* This takes connected elements from host and attachers
|
||||
* into account, too.
|
||||
*/
|
||||
export function getConnectedAtPosition(source, position, element) {
|
||||
|
||||
var bounds = {
|
||||
x: position.x - (element.width / 2),
|
||||
y: position.y - (element.height / 2),
|
||||
width: element.width,
|
||||
height: element.height
|
||||
};
|
||||
|
||||
var closure = getAutoPlaceClosure(source, element);
|
||||
|
||||
return find(closure, function(target) {
|
||||
|
||||
if (target === element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD);
|
||||
|
||||
return orientation === 'intersect';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new, position for the given element
|
||||
* based on the given element that is not occupied
|
||||
* by some element connected to source.
|
||||
*
|
||||
* Take into account the escapeDirection (where to move
|
||||
* on positioning clashes) in the computation.
|
||||
*
|
||||
* @param {djs.model.Shape} source
|
||||
* @param {djs.model.Shape} element
|
||||
* @param {Point} position
|
||||
* @param {Object} escapeDelta
|
||||
*
|
||||
* @return {Point}
|
||||
*/
|
||||
export function deconflictPosition(source, element, position, escapeDelta) {
|
||||
|
||||
function nextPosition(existingElement) {
|
||||
|
||||
var newPosition = {
|
||||
x: position.x,
|
||||
y: position.y
|
||||
};
|
||||
|
||||
[ 'x', 'y' ].forEach(function(axis) {
|
||||
|
||||
var axisDelta = escapeDelta[axis];
|
||||
|
||||
if (!axisDelta) {
|
||||
return;
|
||||
}
|
||||
|
||||
var dimension = axis === 'x' ? 'width' : 'height';
|
||||
|
||||
var margin = axisDelta.margin,
|
||||
rowSize = axisDelta.rowSize;
|
||||
|
||||
if (margin < 0) {
|
||||
newPosition[axis] = Math.min(
|
||||
existingElement[axis] + margin - element[dimension] / 2,
|
||||
position[axis] - rowSize + margin
|
||||
);
|
||||
} else {
|
||||
newPosition[axis] = Math.max(
|
||||
existingTarget[axis] + existingTarget[dimension] + margin + element[dimension] / 2,
|
||||
position[axis] + rowSize + margin
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
var existingTarget;
|
||||
|
||||
// deconflict position until free slot is found
|
||||
while ((existingTarget = getConnectedAtPosition(source, position, element))) {
|
||||
position = nextPosition(existingTarget);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// helpers //////////////////////
|
||||
|
||||
function noneFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function getConnected(element, connectionFilter) {
|
||||
return [].concat(
|
||||
getTargets(element, connectionFilter),
|
||||
getSources(element, connectionFilter)
|
||||
);
|
||||
}
|
||||
|
||||
function getSources(shape, connectionFilter) {
|
||||
|
||||
if (!connectionFilter) {
|
||||
connectionFilter = noneFilter;
|
||||
}
|
||||
|
||||
return shape.incoming.filter(connectionFilter).map(function(c) {
|
||||
return c.source;
|
||||
});
|
||||
}
|
||||
|
||||
function getTargets(shape, connectionFilter) {
|
||||
|
||||
if (!connectionFilter) {
|
||||
connectionFilter = noneFilter;
|
||||
}
|
||||
|
||||
return shape.outgoing.filter(connectionFilter).map(function(c) {
|
||||
return c.target;
|
||||
});
|
||||
}
|
18
lib/features/auto-place/BpmnAutoPlace.js
Normal file
18
lib/features/auto-place/BpmnAutoPlace.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { getNewShapePosition } from './BpmnAutoPlaceUtil';
|
||||
|
||||
|
||||
/**
|
||||
* BPMN auto-place behavior.
|
||||
*
|
||||
* @param {EventBus} eventBus
|
||||
*/
|
||||
export default function AutoPlace(eventBus) {
|
||||
eventBus.on('autoPlace', function(context) {
|
||||
var shape = context.shape,
|
||||
source = context.source;
|
||||
|
||||
return getNewShapePosition(source, shape);
|
||||
});
|
||||
}
|
||||
|
||||
AutoPlace.$inject = [ 'eventBus' ];
|
134
lib/features/auto-place/BpmnAutoPlaceUtil.js
Normal file
134
lib/features/auto-place/BpmnAutoPlaceUtil.js
Normal file
@ -0,0 +1,134 @@
|
||||
import { is } from '../../util/ModelUtil';
|
||||
import { isAny } from '../modeling/util/ModelingUtil';
|
||||
|
||||
import {
|
||||
getMid,
|
||||
asTRBL,
|
||||
getOrientation
|
||||
} from 'diagram-js/lib/layout/LayoutUtil';
|
||||
|
||||
import {
|
||||
deconflictPosition,
|
||||
getConnectedDistance
|
||||
} from 'diagram-js/lib/features/auto-place/AutoPlaceUtil';
|
||||
|
||||
|
||||
/**
|
||||
* Find the new position for the target element to
|
||||
* connect to source.
|
||||
*
|
||||
* @param {djs.model.Shape} source
|
||||
* @param {djs.model.Shape} element
|
||||
*
|
||||
* @return {Point}
|
||||
*/
|
||||
export function getNewShapePosition(source, element) {
|
||||
|
||||
if (is(element, 'bpmn:TextAnnotation')) {
|
||||
return getTextAnnotationPosition(source, element);
|
||||
}
|
||||
|
||||
if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
|
||||
return getDataElementPosition(source, element);
|
||||
}
|
||||
|
||||
if (is(element, 'bpmn:FlowNode')) {
|
||||
return getFlowNodePosition(source, element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always try to place element right of source;
|
||||
* compute actual distance from previous nodes in flow.
|
||||
*/
|
||||
export function getFlowNodePosition(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
var sourceMid = getMid(source);
|
||||
|
||||
var horizontalDistance = getConnectedDistance(source, 'x', function(connection) {
|
||||
return is(connection, 'bpmn:SequenceFlow');
|
||||
});
|
||||
|
||||
var orientation = 'left',
|
||||
rowSize = 80,
|
||||
margin = 30;
|
||||
|
||||
if (is(source, 'bpmn:BoundaryEvent')) {
|
||||
orientation = getOrientation(source, source.host, -25);
|
||||
|
||||
if (orientation.indexOf('top') !== -1) {
|
||||
margin *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
function getVerticalDistance(orient) {
|
||||
if (orient.indexOf('top') != -1) {
|
||||
return -1 * rowSize;
|
||||
} else if (orient.indexOf('bottom') != -1) {
|
||||
return rowSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
var position = {
|
||||
x: sourceTrbl.right + horizontalDistance + element.width / 2,
|
||||
y: sourceMid.y + getVerticalDistance(orientation)
|
||||
};
|
||||
|
||||
var escapeDirection = {
|
||||
y: {
|
||||
margin: margin,
|
||||
rowSize: rowSize
|
||||
}
|
||||
};
|
||||
|
||||
return deconflictPosition(source, element, position, escapeDirection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always try to place text annotations top right of source.
|
||||
*/
|
||||
export function getTextAnnotationPosition(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
|
||||
var position = {
|
||||
x: sourceTrbl.right + element.width / 2,
|
||||
y: sourceTrbl.top - 50 - element.height / 2
|
||||
};
|
||||
|
||||
var escapeDirection = {
|
||||
y: {
|
||||
margin: -30,
|
||||
rowSize: 20
|
||||
}
|
||||
};
|
||||
|
||||
return deconflictPosition(source, element, position, escapeDirection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always put element bottom right of source.
|
||||
*/
|
||||
export function getDataElementPosition(source, element) {
|
||||
|
||||
var sourceTrbl = asTRBL(source);
|
||||
|
||||
var position = {
|
||||
x: sourceTrbl.right - 10 + element.width / 2,
|
||||
y: sourceTrbl.bottom + 40 + element.width / 2
|
||||
};
|
||||
|
||||
var escapeDirection = {
|
||||
x: {
|
||||
margin: 30,
|
||||
rowSize: 30
|
||||
}
|
||||
};
|
||||
|
||||
return deconflictPosition(source, element, position, escapeDirection);
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import AutoPlace from './AutoPlace';
|
||||
import AutoPlaceSelectionBehavior from './AutoPlaceSelectionBehavior';
|
||||
import AutoPlaceModule from 'diagram-js/lib/features/auto-place';
|
||||
|
||||
import BpmnAutoPlace from './BpmnAutoPlace';
|
||||
|
||||
export default {
|
||||
__init__: [ 'autoPlaceSelectionBehavior' ],
|
||||
autoPlace: [ 'type', AutoPlace ],
|
||||
autoPlaceSelectionBehavior: [ 'type', AutoPlaceSelectionBehavior ]
|
||||
__depends__: [ AutoPlaceModule ],
|
||||
__init__: [ 'bpmnAutoPlace' ],
|
||||
bpmnAutoPlace: [ 'type', BpmnAutoPlace ]
|
||||
};
|
@ -1,11 +1,13 @@
|
||||
import { getNewShapePosition } from '../../auto-place/AutoPlaceUtil';
|
||||
import { getNewShapePosition } from '../../auto-place/BpmnAutoPlaceUtil';
|
||||
|
||||
import { getMid } from 'diagram-js/lib/layout/LayoutUtil';
|
||||
import { is } from '../../../util/ModelUtil';
|
||||
|
||||
var HIGH_PRIORITY = 2000;
|
||||
|
||||
|
||||
export default function AutoPlaceBehavior(eventBus, gridSnapping) {
|
||||
eventBus.on('autoPlace', function(context) {
|
||||
eventBus.on('autoPlace', HIGH_PRIORITY, function(context) {
|
||||
var source = context.source,
|
||||
sourceMid = getMid(source),
|
||||
shape = context.shape;
|
||||
|
@ -11,14 +11,12 @@ import selectionModule from 'diagram-js/lib/features/selection';
|
||||
|
||||
import { getBusinessObject } from '../../../../lib/util/ModelUtil';
|
||||
|
||||
import { getMid } from 'diagram-js/lib/layout/LayoutUtil';
|
||||
|
||||
|
||||
describe('features/auto-place', function() {
|
||||
|
||||
describe('element placement', function() {
|
||||
|
||||
var diagramXML = require('./AutoPlace.bpmn');
|
||||
var diagramXML = require('./BpmnAutoPlace.bpmn');
|
||||
|
||||
before(bootstrapModeler(diagramXML, {
|
||||
modules: [
|
||||
@ -116,7 +114,7 @@ describe('features/auto-place', function() {
|
||||
|
||||
describe('integration', function() {
|
||||
|
||||
var diagramXML = require('./AutoPlace.bpmn');
|
||||
var diagramXML = require('./BpmnAutoPlace.bpmn');
|
||||
|
||||
before(bootstrapModeler(diagramXML, {
|
||||
modules: [
|
||||
@ -174,7 +172,7 @@ describe('features/auto-place', function() {
|
||||
|
||||
describe('multi connection handling', function() {
|
||||
|
||||
var diagramXML = require('./AutoPlace.multi-connection.bpmn');
|
||||
var diagramXML = require('./BpmnAutoPlace.multi-connection.bpmn');
|
||||
|
||||
before(bootstrapModeler(diagramXML, {
|
||||
modules: [
|
||||
@ -209,7 +207,7 @@ describe('features/auto-place', function() {
|
||||
|
||||
describe('boundary event connection handling', function() {
|
||||
|
||||
var diagramXML = require('./AutoPlace.boundary-events.bpmn');
|
||||
var diagramXML = require('./BpmnAutoPlace.boundary-events.bpmn');
|
||||
|
||||
before(bootstrapModeler(diagramXML, {
|
||||
modules: [
|
||||
@ -241,12 +239,14 @@ describe('features/auto-place', function() {
|
||||
expectedBounds: { x: 242, y: -27, width: 100, height: 80 }
|
||||
}));
|
||||
|
||||
|
||||
it('should place top right of BOUNDARY_TOP_RIGHT without infinite loop', autoPlace({
|
||||
element: 'bpmn:Task',
|
||||
behind: 'BOUNDARY_TOP_RIGHT',
|
||||
expectedBounds: { x: 473, y: -27, width: 100, height: 80 }
|
||||
}));
|
||||
|
||||
|
||||
it('should place top right of BOUNDARY_SUBPROCESS_TOP', autoPlace({
|
||||
element: 'bpmn:Task',
|
||||
behind: 'BOUNDARY_SUBPROCESS_TOP',
|
||||
@ -255,114 +255,10 @@ describe('features/auto-place', function() {
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('eventbus integration', function() {
|
||||
|
||||
var diagramXML = require('./AutoPlace.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, {
|
||||
modules: [
|
||||
autoPlaceModule,
|
||||
coreModule,
|
||||
labelEditingModule,
|
||||
modelingModule,
|
||||
selectionModule
|
||||
]
|
||||
}));
|
||||
|
||||
|
||||
it('<autoPlace.start>', inject(
|
||||
function(autoPlace, elementFactory, elementRegistry, eventBus) {
|
||||
|
||||
// given
|
||||
var element = elementFactory.createShape({ type: 'bpmn:Task' });
|
||||
|
||||
var source = elementRegistry.get('TASK_2');
|
||||
|
||||
var listener = sinon.spy(function(event) {
|
||||
|
||||
// then
|
||||
expect(event.shape).to.equal(element);
|
||||
expect(event.source).to.equal(source);
|
||||
});
|
||||
|
||||
eventBus.on('autoPlace.start', listener);
|
||||
|
||||
// when
|
||||
autoPlace.append(source, element);
|
||||
|
||||
expect(listener).to.have.been.called;
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
it('<autoPlace>', inject(
|
||||
function(autoPlace, elementFactory, elementRegistry, eventBus) {
|
||||
|
||||
// given
|
||||
var element = elementFactory.createShape({ type: 'bpmn:Task' });
|
||||
|
||||
var source = elementRegistry.get('TASK_2');
|
||||
|
||||
var listener = sinon.spy(function(event) {
|
||||
|
||||
// then
|
||||
expect(event.shape).to.equal(element);
|
||||
expect(event.source).to.equal(source);
|
||||
|
||||
return {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
});
|
||||
|
||||
eventBus.on('autoPlace', listener);
|
||||
|
||||
// when
|
||||
autoPlace.append(source, element);
|
||||
|
||||
expect(listener).to.have.been.called;
|
||||
|
||||
expect(getMid(element)).to.eql({
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
it('<autoPlace.end>', inject(
|
||||
function(autoPlace, elementFactory, elementRegistry, eventBus) {
|
||||
|
||||
// given
|
||||
var element = elementFactory.createShape({ type: 'bpmn:Task' });
|
||||
|
||||
var source = elementRegistry.get('TASK_2');
|
||||
|
||||
var listener = sinon.spy(function(event) {
|
||||
|
||||
// then
|
||||
expect(event.shape).to.equal(element);
|
||||
expect(event.source).to.equal(source);
|
||||
});
|
||||
|
||||
eventBus.on('autoPlace.end', listener);
|
||||
|
||||
// when
|
||||
autoPlace.append(source, element);
|
||||
|
||||
expect(listener).to.have.been.called;
|
||||
}
|
||||
));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// helpers //////////////////////
|
||||
// helpers //////////
|
||||
|
||||
function autoPlace(cfg) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user