feat(modeling): add auto placement from context menu
Elements will automatically be created at appropriate positions when context menu create entries are being clicked (rather than dragged). This marks a major step forward for mobile modeling, too as dragging, especially dragging out from very small controls is very cumbersome to do. Things we take into account: * for bpmn:FlowNodes, we try to compute the current distance between elements on the flow based on connections going in and out of the flow nodes source element * for bpmn:TextAnnotation we assume placement of the element top right of the source shape * for bpmn:DataObject and friends we assume a placement bottom right of the source shape * for all elements, we try not to place elements on top of each other; i.e. new elements will be pushed up or down accordingly, if an element at a chosen position does already exist Integration into other services: * context pad provider works with autoPlace, if available and defaults to drag start without * auto placed elements are selected and direct editing may conditionally be activated based on element type (LabelEditingProvider knows the rules) Users can out out of autoPlace by specifying the configuration property `config.contextPad.autoPlace = false`. Closes #563 BREAKING CHANGE: * This breaks the default interaction from the context pad; if you rely on clicking to start the drag you can opt out of autoPlace: ``` new BpmnJS({ contextPad: { autoPlace: false } }); ```
This commit is contained in:
parent
6b5277b936
commit
ae96f3714d
|
@ -185,6 +185,7 @@ Modeler.prototype._modelingModules = [
|
||||||
require('diagram-js/lib/features/move'),
|
require('diagram-js/lib/features/move'),
|
||||||
require('diagram-js/lib/features/resize'),
|
require('diagram-js/lib/features/resize'),
|
||||||
require('./features/auto-resize'),
|
require('./features/auto-resize'),
|
||||||
|
require('./features/auto-place'),
|
||||||
require('./features/editor-actions'),
|
require('./features/editor-actions'),
|
||||||
require('./features/context-pad'),
|
require('./features/context-pad'),
|
||||||
require('./features/keyboard'),
|
require('./features/keyboard'),
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var is = require('../../util/ModelUtil').is;
|
||||||
|
var isAny = require('../modeling/util/ModelingUtil').isAny;
|
||||||
|
|
||||||
|
var getTextAnnotationPosition = require('./AutoPlaceUtil').getTextAnnotationPosition,
|
||||||
|
getDataElementPosition = require('./AutoPlaceUtil').getDataElementPosition,
|
||||||
|
getFlowNodePosition = require('./AutoPlaceUtil').getFlowNodePosition,
|
||||||
|
getDefaultPosition = require('./AutoPlaceUtil').getDefaultPosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that places elements connected to existing ones
|
||||||
|
* to an appropriate position in an _automated_ fashion.
|
||||||
|
*
|
||||||
|
* @param {EventBus} eventBus
|
||||||
|
* @param {Modeling} modeling
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// notify interested parties on new shape placed
|
||||||
|
emit('autoPlace.end', {
|
||||||
|
shape: newShape
|
||||||
|
});
|
||||||
|
|
||||||
|
return newShape;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoPlace.$inject = [
|
||||||
|
'eventBus',
|
||||||
|
'modeling'
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = AutoPlace;
|
||||||
|
|
||||||
|
|
||||||
|
/////////// helpers /////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the new position for the target element to
|
||||||
|
* connect to source.
|
||||||
|
*
|
||||||
|
* @param {djs.model.Shape} source
|
||||||
|
* @param {djs.model.Shape} element
|
||||||
|
*
|
||||||
|
* @return {Point}
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select element after auto placement.
|
||||||
|
*
|
||||||
|
* @param {EventBus} eventBus
|
||||||
|
* @param {Selection} selection
|
||||||
|
*/
|
||||||
|
function AutoPlaceSelectionBehavior(eventBus, selection) {
|
||||||
|
|
||||||
|
eventBus.on('autoPlace.end', 500, function(e) {
|
||||||
|
selection.select(e.shape);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoPlaceSelectionBehavior.$inject = [
|
||||||
|
'eventBus',
|
||||||
|
'selection'
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = AutoPlaceSelectionBehavior;
|
|
@ -0,0 +1,262 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var is = require('../../util/ModelUtil').is;
|
||||||
|
|
||||||
|
var getMid = require('diagram-js/lib/layout/LayoutUtil').getMid,
|
||||||
|
asTRBL = require('diagram-js/lib/layout/LayoutUtil').asTRBL,
|
||||||
|
getOrientation = require('diagram-js/lib/layout/LayoutUtil').getOrientation;
|
||||||
|
|
||||||
|
var find = require('lodash/collection/find'),
|
||||||
|
reduce = require('lodash/collection/reduce'),
|
||||||
|
filter = require('lodash/collection/filter');
|
||||||
|
|
||||||
|
var DEFAULT_HORIZONTAL_DISTANCE = 50;
|
||||||
|
|
||||||
|
var MAX_HORIZONTAL_DISTANCE = 250;
|
||||||
|
|
||||||
|
// padding to detect element placement
|
||||||
|
var PLACEMENT_DETECTION_PAD = 10;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always try to place element right of source;
|
||||||
|
* compute actual distance from previous nodes in flow.
|
||||||
|
*/
|
||||||
|
function getFlowNodePosition(source, element) {
|
||||||
|
|
||||||
|
var sourceTrbl = asTRBL(source);
|
||||||
|
var sourceMid = getMid(source);
|
||||||
|
|
||||||
|
var horizontalDistance = getFlowNodeDistance(source, element);
|
||||||
|
|
||||||
|
var position = {
|
||||||
|
x: sourceTrbl.right + horizontalDistance + element.width / 2,
|
||||||
|
y: sourceMid.y
|
||||||
|
};
|
||||||
|
|
||||||
|
var existingTarget;
|
||||||
|
|
||||||
|
// make sure we don't place targets a
|
||||||
|
while ((existingTarget = getConnectedAtPosition(source, position, element))) {
|
||||||
|
|
||||||
|
position = {
|
||||||
|
x: position.x,
|
||||||
|
y: Math.max(
|
||||||
|
existingTarget.y + existingTarget.height + 30 + element.height / 2,
|
||||||
|
position.y + 80
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getFlowNodePosition = getFlowNodePosition;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
function getFlowNodeDistance(source, element) {
|
||||||
|
|
||||||
|
var sourceTrbl = asTRBL(source);
|
||||||
|
|
||||||
|
// is connection a reference to consider?
|
||||||
|
var isReference = function(c) {
|
||||||
|
return is(c, 'bpmn:SequenceFlow');
|
||||||
|
};
|
||||||
|
|
||||||
|
var nodes = [].concat(
|
||||||
|
filter(source.outgoing, isReference).map(function(c) {
|
||||||
|
return {
|
||||||
|
shape: c.target,
|
||||||
|
weight: 5,
|
||||||
|
distanceTo: function(shape) {
|
||||||
|
var shapeTrbl = asTRBL(shape);
|
||||||
|
|
||||||
|
return shapeTrbl.left - sourceTrbl.right;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
filter(source.incoming, isReference).map(function(c) {
|
||||||
|
return {
|
||||||
|
shape: c.source,
|
||||||
|
weight: 1,
|
||||||
|
distanceTo: function(shape) {
|
||||||
|
var shapeTrbl = asTRBL(shape);
|
||||||
|
|
||||||
|
return sourceTrbl.left - shapeTrbl.right;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getFlowNodeDistance = getFlowNodeDistance;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always try to place text annotations top right of source.
|
||||||
|
*/
|
||||||
|
function getTextAnnotationPosition(source, element) {
|
||||||
|
|
||||||
|
var sourceTrbl = asTRBL(source);
|
||||||
|
|
||||||
|
// todo: adaptive
|
||||||
|
var position = {
|
||||||
|
x: sourceTrbl.right + 30 + element.width / 2,
|
||||||
|
y: sourceTrbl.top - 50 - element.height / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
var existingTarget;
|
||||||
|
|
||||||
|
while ((existingTarget = getConnectedAtPosition(source, position, element))) {
|
||||||
|
|
||||||
|
// escape to top
|
||||||
|
position = {
|
||||||
|
x: position.x,
|
||||||
|
y: Math.min(
|
||||||
|
existingTarget.y - 30 - element.height / 2,
|
||||||
|
position.y - 30
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getTextAnnotationPosition = getTextAnnotationPosition;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always put element bottom right of source.
|
||||||
|
*/
|
||||||
|
function getDataElementPosition(source, element) {
|
||||||
|
|
||||||
|
var sourceTrbl = asTRBL(source);
|
||||||
|
|
||||||
|
var position = {
|
||||||
|
x: sourceTrbl.right + 50 + element.width / 2,
|
||||||
|
y: sourceTrbl.bottom + 40 + element.width / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
var existingTarget;
|
||||||
|
|
||||||
|
while ((existingTarget = getConnectedAtPosition(source, position, element))) {
|
||||||
|
|
||||||
|
// escape to right
|
||||||
|
position = {
|
||||||
|
x: Math.min(
|
||||||
|
existingTarget.x + existingTarget.width + 30 + element.width / 2,
|
||||||
|
position.x + 80
|
||||||
|
),
|
||||||
|
y: position.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getDataElementPosition = getDataElementPosition;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always put element right of source per default.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getDefaultPosition = getDefaultPosition;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return target at given position, if defined.
|
||||||
|
*/
|
||||||
|
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 targets = source.outgoing.map(function(c) {
|
||||||
|
return c.target;
|
||||||
|
});
|
||||||
|
|
||||||
|
var sources = source.incoming.map(function(c) {
|
||||||
|
return c.source;
|
||||||
|
});
|
||||||
|
|
||||||
|
var allConnected = [].concat(targets, sources);
|
||||||
|
|
||||||
|
return find(allConnected, function(target) {
|
||||||
|
var orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD);
|
||||||
|
|
||||||
|
return orientation === 'intersect';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getConnectedAtPosition = getConnectedAtPosition;
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
__init__: [ 'autoPlaceSelectionBehavior' ],
|
||||||
|
autoPlace: [ 'type', require('./AutoPlace') ],
|
||||||
|
autoPlaceSelectionBehavior: [ 'type', require('./AutoPlaceSelectionBehavior') ]
|
||||||
|
};
|
|
@ -14,10 +14,12 @@ var assign = require('lodash/object/assign'),
|
||||||
/**
|
/**
|
||||||
* A provider for BPMN 2.0 elements context pad
|
* A provider for BPMN 2.0 elements context pad
|
||||||
*/
|
*/
|
||||||
function ContextPadProvider(eventBus, contextPad, modeling, elementFactory,
|
function ContextPadProvider(config, injector, eventBus, contextPad, modeling,
|
||||||
connect, create, popupMenu,
|
elementFactory, connect, create, popupMenu,
|
||||||
canvas, rules, translate) {
|
canvas, rules, translate) {
|
||||||
|
|
||||||
|
config = config || {};
|
||||||
|
|
||||||
contextPad.registerProvider(this);
|
contextPad.registerProvider(this);
|
||||||
|
|
||||||
this._contextPad = contextPad;
|
this._contextPad = contextPad;
|
||||||
|
@ -32,6 +34,9 @@ function ContextPadProvider(eventBus, contextPad, modeling, elementFactory,
|
||||||
this._rules = rules;
|
this._rules = rules;
|
||||||
this._translate = translate;
|
this._translate = translate;
|
||||||
|
|
||||||
|
if (config.autoPlace !== false) {
|
||||||
|
this._autoPlace = injector.get('autoPlace', false);
|
||||||
|
}
|
||||||
|
|
||||||
eventBus.on('create.end', 250, function(event) {
|
eventBus.on('create.end', 250, function(event) {
|
||||||
var shape = event.context.shape;
|
var shape = event.context.shape;
|
||||||
|
@ -49,6 +54,8 @@ function ContextPadProvider(eventBus, contextPad, modeling, elementFactory,
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextPadProvider.$inject = [
|
ContextPadProvider.$inject = [
|
||||||
|
'config.contextPad',
|
||||||
|
'injector',
|
||||||
'eventBus',
|
'eventBus',
|
||||||
'contextPad',
|
'contextPad',
|
||||||
'modeling',
|
'modeling',
|
||||||
|
@ -75,7 +82,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
|
||||||
popupMenu = this._popupMenu,
|
popupMenu = this._popupMenu,
|
||||||
canvas = this._canvas,
|
canvas = this._canvas,
|
||||||
rules = this._rules,
|
rules = this._rules,
|
||||||
|
autoPlace = this._autoPlace,
|
||||||
translate = this._translate;
|
translate = this._translate;
|
||||||
|
|
||||||
var actions = {};
|
var actions = {};
|
||||||
|
@ -137,19 +144,27 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
|
||||||
title = translate('Append {type}', { type: type.replace(/^bpmn\:/, '') });
|
title = translate('Append {type}', { type: type.replace(/^bpmn\:/, '') });
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendListener(event, element) {
|
function appendStart(event, element) {
|
||||||
|
|
||||||
var shape = elementFactory.createShape(assign({ type: type }, options));
|
var shape = elementFactory.createShape(assign({ type: type }, options));
|
||||||
create.start(event, shape, element);
|
create.start(event, shape, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var append = autoPlace ? function(event, element) {
|
||||||
|
var shape = elementFactory.createShape(assign({ type: type }, options));
|
||||||
|
|
||||||
|
autoPlace.append(element, shape);
|
||||||
|
} : appendStart;
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
group: 'model',
|
group: 'model',
|
||||||
className: className,
|
className: className,
|
||||||
title: title,
|
title: title,
|
||||||
action: {
|
action: {
|
||||||
dragstart: appendListener,
|
dragstart: appendStart,
|
||||||
click: appendListener
|
click: append
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,10 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, res
|
||||||
activateDirectEdit(element);
|
activateDirectEdit(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
eventBus.on('autoPlace.end', 500, function(event) {
|
||||||
|
activateDirectEdit(event.shape);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
function activateDirectEdit(element, force) {
|
function activateDirectEdit(element, force) {
|
||||||
if (force ||
|
if (force ||
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
|
||||||
|
<bpmn:process id="Process_1" isExecutable="false">
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>SequenceFlow_16tlpj7</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:task id="TASK_0" name="TASK_0">
|
||||||
|
<bpmn:incoming>SequenceFlow_16tlpj7</bpmn:incoming>
|
||||||
|
<bpmn:incoming>SequenceFlow_19p2kv6</bpmn:incoming>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_16tlpj7" sourceRef="StartEvent_1" targetRef="TASK_0" />
|
||||||
|
<bpmn:task id="TASK_1" name="TASK_1">
|
||||||
|
<bpmn:incoming>SequenceFlow_0s1mty3</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>SequenceFlow_0b5s2a7</bpmn:outgoing>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:startEvent id="StartEvent_0f2fdfi">
|
||||||
|
<bpmn:outgoing>SequenceFlow_0s1mty3</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_0s1mty3" sourceRef="StartEvent_0f2fdfi" targetRef="TASK_1" />
|
||||||
|
<bpmn:task id="TASK_2" name="TASK_2">
|
||||||
|
<bpmn:incoming>SequenceFlow_0b5s2a7</bpmn:incoming>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_0b5s2a7" sourceRef="TASK_1" targetRef="TASK_2" />
|
||||||
|
<bpmn:task id="TASK_3" name="TASK_3">
|
||||||
|
<bpmn:outgoing>SequenceFlow_18dnq8n</bpmn:outgoing>
|
||||||
|
<bpmn:dataOutputAssociation id="DataOutputAssociation_16lcc1g">
|
||||||
|
<bpmn:targetRef>DataStoreReference_0r0lie7</bpmn:targetRef>
|
||||||
|
</bpmn:dataOutputAssociation>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:task id="TASK_4" name="TASK_4">
|
||||||
|
<bpmn:incoming>SequenceFlow_18dnq8n</bpmn:incoming>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_18dnq8n" sourceRef="TASK_3" targetRef="TASK_4" />
|
||||||
|
<bpmn:task id="TASK_5" name="TASK_5">
|
||||||
|
<bpmn:incoming>SequenceFlow_0n4l6q7</bpmn:incoming>
|
||||||
|
<bpmn:incoming>SequenceFlow_10nwqsy</bpmn:incoming>
|
||||||
|
<bpmn:incoming>SequenceFlow_13ubee5</bpmn:incoming>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:startEvent id="StartEvent_0wxeenz">
|
||||||
|
<bpmn:outgoing>SequenceFlow_0n4l6q7</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:startEvent id="StartEvent_1m0lwft">
|
||||||
|
<bpmn:outgoing>SequenceFlow_10nwqsy</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:startEvent id="StartEvent_06jwo6i">
|
||||||
|
<bpmn:outgoing>SequenceFlow_13ubee5</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_0n4l6q7" sourceRef="StartEvent_0wxeenz" targetRef="TASK_5" />
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_10nwqsy" sourceRef="StartEvent_1m0lwft" targetRef="TASK_5" />
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_13ubee5" sourceRef="StartEvent_06jwo6i" targetRef="TASK_5" />
|
||||||
|
<bpmn:startEvent id="START_EVENT_1" name="START_EVENT_1" />
|
||||||
|
<bpmn:intermediateThrowEvent id="IntermediateThrowEvent_0yy98gf">
|
||||||
|
<bpmn:outgoing>SequenceFlow_19p2kv6</bpmn:outgoing>
|
||||||
|
</bpmn:intermediateThrowEvent>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_19p2kv6" sourceRef="IntermediateThrowEvent_0yy98gf" targetRef="TASK_0" />
|
||||||
|
<bpmn:dataStoreReference id="DataStoreReference_0r0lie7" />
|
||||||
|
<bpmn:subProcess id="SUBPROCESS_1" name="SUBPROCESS_1" />
|
||||||
|
<bpmn:textAnnotation id="TextAnnotation_0czqc1j" />
|
||||||
|
<bpmn:association id="Association_1ebqqnb" sourceRef="TASK_3" targetRef="TextAnnotation_0czqc1j" />
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
||||||
|
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||||
|
<dc:Bounds x="44" y="76" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="17" y="112" width="90" height="20" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="TASK_0_di" bpmnElement="TASK_0">
|
||||||
|
<dc:Bounds x="121" y="54" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_16tlpj7_di" bpmnElement="SequenceFlow_16tlpj7">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="80" y="94" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="121" y="94" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="100.5" y="73" width="0" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="TASK_1_di" bpmnElement="TASK_1">
|
||||||
|
<dc:Bounds x="121" y="183" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="StartEvent_0f2fdfi_di" bpmnElement="StartEvent_0f2fdfi">
|
||||||
|
<dc:Bounds x="44" y="205" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="17" y="241" width="0" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_0s1mty3_di" bpmnElement="SequenceFlow_0s1mty3">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="80" y="223" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="121" y="223" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="100.5" y="202" width="0" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="TASK_2_di" bpmnElement="TASK_2">
|
||||||
|
<dc:Bounds x="279" y="183" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_0b5s2a7_di" bpmnElement="SequenceFlow_0b5s2a7">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="221" y="223" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="279" y="223" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="250" y="202" width="0" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="TASK_3_di" bpmnElement="TASK_3">
|
||||||
|
<dc:Bounds x="596" y="127" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="TextAnnotation_0czqc1j_di" bpmnElement="TextAnnotation_0czqc1j">
|
||||||
|
<dc:Bounds x="698" y="56" width="100" height="30" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Association_1ebqqnb_di" bpmnElement="Association_1ebqqnb">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="687" y="128" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="732" y="86" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="TASK_4_di" bpmnElement="TASK_4">
|
||||||
|
<dc:Bounds x="1030" y="127" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_18dnq8n_di" bpmnElement="SequenceFlow_18dnq8n">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="696" y="167" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="1030" y="167" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="818" y="146" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="TASK_5_di" bpmnElement="TASK_5">
|
||||||
|
<dc:Bounds x="121" y="390" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="StartEvent_0wxeenz_di" bpmnElement="StartEvent_0wxeenz">
|
||||||
|
<dc:Bounds x="10" y="353" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="-17" y="393" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="StartEvent_1m0lwft_di" bpmnElement="StartEvent_1m0lwft">
|
||||||
|
<dc:Bounds x="44" y="412" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="17" y="452" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="StartEvent_06jwo6i_di" bpmnElement="StartEvent_06jwo6i">
|
||||||
|
<dc:Bounds x="10" y="479" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="-17" y="519" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_0n4l6q7_di" bpmnElement="SequenceFlow_0n4l6q7">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="46" y="371" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="84" y="371" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="84" y="408" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="121" y="408" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="54" y="384" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_10nwqsy_di" bpmnElement="SequenceFlow_10nwqsy">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="80" y="430" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="121" y="430" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="56" y="409" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_13ubee5_di" bpmnElement="SequenceFlow_13ubee5">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="46" y="497" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="84" y="497" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="84" y="449" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="121" y="449" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="54" y="467" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="START_EVENT_1_di" bpmnElement="START_EVENT_1">
|
||||||
|
<dc:Bounds x="966" y="246" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="941" y="286" width="87" height="24" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="IntermediateThrowEvent_0yy98gf_di" bpmnElement="IntermediateThrowEvent_0yy98gf">
|
||||||
|
<dc:Bounds x="219" y="5" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="192" y="45" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_19p2kv6_di" bpmnElement="SequenceFlow_19p2kv6">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="219" y="23" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="171" y="23" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="171" y="54" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="150" y="2" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="DataStoreReference_0r0lie7_di" bpmnElement="DataStoreReference_0r0lie7">
|
||||||
|
<dc:Bounds x="689" y="245" width="50" height="50" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="669" y="299" width="90" height="12" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="DataOutputAssociation_16lcc1g_di" bpmnElement="DataOutputAssociation_16lcc1g">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="671" y="207" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="695" y="245" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="SUBPROCESS_1_di" bpmnElement="SUBPROCESS_1" isExpanded="true">
|
||||||
|
<dc:Bounds x="525" y="308" width="350" height="200" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
|
@ -0,0 +1,170 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../../../TestHelper');
|
||||||
|
|
||||||
|
/* global bootstrapModeler, inject */
|
||||||
|
|
||||||
|
var autoPlaceModule = require('../../../../lib/features/auto-place'),
|
||||||
|
modelingModule = require('../../../../lib/features/modeling'),
|
||||||
|
selectionModule = require('diagram-js/lib/features/selection'),
|
||||||
|
labelEditingModule = require('../../../../lib/features/label-editing'),
|
||||||
|
coreModule = require('../../../../lib/core');
|
||||||
|
|
||||||
|
|
||||||
|
describe('features/auto-place', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('./AutoPlace.bpmn');
|
||||||
|
|
||||||
|
|
||||||
|
describe('element placement', function() {
|
||||||
|
|
||||||
|
before(bootstrapModeler(diagramXML, {
|
||||||
|
modules: [
|
||||||
|
coreModule,
|
||||||
|
modelingModule,
|
||||||
|
autoPlaceModule,
|
||||||
|
selectionModule
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
function autoPlace(cfg) {
|
||||||
|
|
||||||
|
var element = cfg.element,
|
||||||
|
behind = cfg.behind,
|
||||||
|
expectedBounds = cfg.expectedBounds;
|
||||||
|
|
||||||
|
return inject(function(autoPlace, elementRegistry, elementFactory) {
|
||||||
|
|
||||||
|
var sourceEl = elementRegistry.get(behind);
|
||||||
|
|
||||||
|
// assume
|
||||||
|
expect(sourceEl).to.exist;
|
||||||
|
|
||||||
|
if (typeof element === 'string') {
|
||||||
|
element = { type: element };
|
||||||
|
}
|
||||||
|
|
||||||
|
var shape = elementFactory.createShape(element);
|
||||||
|
|
||||||
|
// when
|
||||||
|
var placedShape = autoPlace.append(sourceEl, shape);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(placedShape).to.have.bounds(expectedBounds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
describe('should place bpmn:FlowNode', function() {
|
||||||
|
|
||||||
|
it('at default distance after START_EVENT_1', autoPlace({
|
||||||
|
element: 'bpmn:Task',
|
||||||
|
behind: 'START_EVENT_1',
|
||||||
|
expectedBounds: { x: 1052, y: 224, width: 100, height: 80 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('at incoming distance after TASK_0', autoPlace({
|
||||||
|
element: 'bpmn:Task',
|
||||||
|
behind: 'TASK_0',
|
||||||
|
expectedBounds: { x: 262, y: 54, width: 100, height: 80 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('at incoming distance / quorum after TASK_5', autoPlace({
|
||||||
|
element: 'bpmn:Task',
|
||||||
|
behind: 'TASK_5',
|
||||||
|
expectedBounds: { x: 296, y: 390, width: 100, height: 80 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('at existing outgoing / below TASK_2', autoPlace({
|
||||||
|
element: 'bpmn:Task',
|
||||||
|
behind: 'TASK_1',
|
||||||
|
expectedBounds: { x: 279, y: 293, width: 100, height: 80 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('ignoring existing, far away outgoing of TASK_3', autoPlace({
|
||||||
|
element: 'bpmn:Task',
|
||||||
|
behind: 'TASK_3',
|
||||||
|
expectedBounds: { x: 746, y: 127, width: 100, height: 80 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('behind bpmn:SubProcess', autoPlace({
|
||||||
|
element: 'bpmn:Task',
|
||||||
|
behind: 'SUBPROCESS_1',
|
||||||
|
expectedBounds: { x: 925, y: 368, width: 100, height: 80 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('should place bpmn:DataStoreReference', function() {
|
||||||
|
|
||||||
|
it('bottom right of source', autoPlace({
|
||||||
|
element: 'bpmn:DataStoreReference',
|
||||||
|
behind: 'TASK_3',
|
||||||
|
expectedBounds: { x: 769, y: 247, width: 50, height: 50 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('should place bpmn:TextAnnotation', function() {
|
||||||
|
|
||||||
|
it('top right of source', autoPlace({
|
||||||
|
element: 'bpmn:TextAnnotation',
|
||||||
|
behind: 'TASK_2',
|
||||||
|
expectedBounds: { x: 409, y: 103, width: 100, height: 30 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('above existing', autoPlace({
|
||||||
|
element: 'bpmn:TextAnnotation',
|
||||||
|
behind: 'TASK_3',
|
||||||
|
expectedBounds: { x: 726, y: -4, width: 100, height: 30 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('modeling flow', function() {
|
||||||
|
|
||||||
|
before(bootstrapModeler(diagramXML, {
|
||||||
|
modules: [
|
||||||
|
coreModule,
|
||||||
|
modelingModule,
|
||||||
|
autoPlaceModule,
|
||||||
|
selectionModule,
|
||||||
|
labelEditingModule
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should select + direct edit on autoPlace', inject(
|
||||||
|
function(autoPlace, elementRegistry, elementFactory, selection, directEditing) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var el = elementFactory.createShape({ type: 'bpmn:Task' });
|
||||||
|
|
||||||
|
var source = elementRegistry.get('TASK_2');
|
||||||
|
|
||||||
|
// when
|
||||||
|
var newShape = autoPlace.append(source, el);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(selection.get()).to.eql([ newShape ]);
|
||||||
|
|
||||||
|
expect(directEditing.isActive()).to.be.true;
|
||||||
|
expect(directEditing._active.element).to.equal(newShape);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -19,7 +19,8 @@ var contextPadModule = require('../../../../lib/features/context-pad'),
|
||||||
modelingModule = require('../../../../lib/features/modeling'),
|
modelingModule = require('../../../../lib/features/modeling'),
|
||||||
replaceMenuModule = require('../../../../lib/features/popup-menu'),
|
replaceMenuModule = require('../../../../lib/features/popup-menu'),
|
||||||
createModule = require('diagram-js/lib/features/create'),
|
createModule = require('diagram-js/lib/features/create'),
|
||||||
customRulesModule = require('../../../util/custom-rules');
|
customRulesModule = require('../../../util/custom-rules'),
|
||||||
|
autoPlaceModule = require('../../../../lib/features/auto-place');
|
||||||
|
|
||||||
|
|
||||||
describe('features - context-pad', function() {
|
describe('features - context-pad', function() {
|
||||||
|
@ -432,6 +433,89 @@ describe('features - context-pad', function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('auto place', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('../../../fixtures/bpmn/simple.bpmn');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, {
|
||||||
|
modules: testModules.concat(autoPlaceModule)
|
||||||
|
}));
|
||||||
|
|
||||||
|
var container;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
container = TestContainer.get(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should trigger', inject(function(elementRegistry, contextPad) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var element = elementRegistry.get('Task_1');
|
||||||
|
|
||||||
|
contextPad.open(element);
|
||||||
|
|
||||||
|
// mock event
|
||||||
|
var event = {
|
||||||
|
clientX: 100,
|
||||||
|
clientY: 100,
|
||||||
|
target: padEntry(container, 'append.gateway'),
|
||||||
|
preventDefault: function() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
contextPad.trigger('click', event);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(element.outgoing).to.have.length(1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('disabled auto-place', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('../../../fixtures/bpmn/simple.bpmn');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, {
|
||||||
|
modules: testModules.concat(autoPlaceModule),
|
||||||
|
contextPad: {
|
||||||
|
autoPlace: false
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
var container;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
container = TestContainer.get(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should default to drag start', inject(function(elementRegistry, contextPad, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var element = elementRegistry.get('Task_1');
|
||||||
|
|
||||||
|
contextPad.open(element);
|
||||||
|
|
||||||
|
// mock event
|
||||||
|
var event = {
|
||||||
|
clientX: 100,
|
||||||
|
clientY: 100,
|
||||||
|
target: padEntry(container, 'append.gateway'),
|
||||||
|
preventDefault: function() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
contextPad.trigger('click', event);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(dragging.context()).to.exist;
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue