feat(rules): support attach from context-menu
* take target attach and parent hints on `connection.create` rule into account to implement create from context-menu Related to bpmn-io/diagram-js#242 Closes #742
This commit is contained in:
parent
11354e951c
commit
367399d509
|
@ -22,6 +22,7 @@ var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider');
|
|||
|
||||
var isBoundaryAttachment = require('../snapping/BpmnSnappingUtil').getBoundaryAttachment;
|
||||
|
||||
|
||||
/**
|
||||
* BPMN specific modeling rule
|
||||
*/
|
||||
|
@ -39,9 +40,32 @@ BpmnRules.prototype.init = function() {
|
|||
|
||||
this.addRule('connection.create', function(context) {
|
||||
var source = context.source,
|
||||
target = context.target;
|
||||
target = context.target,
|
||||
hints = context.hints || {},
|
||||
targetParent = hints.targetParent,
|
||||
targetAttach = hints.targetAttach;
|
||||
|
||||
return canConnect(source, target);
|
||||
// don't allow incoming connections on
|
||||
// newly created boundary events
|
||||
// to boundary events
|
||||
if (targetAttach) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// temporarily set target parent for scoping
|
||||
// checks to work
|
||||
if (targetParent) {
|
||||
target.parent = targetParent;
|
||||
}
|
||||
|
||||
try {
|
||||
return canConnect(source, target);
|
||||
} finally {
|
||||
// unset temporary target parent
|
||||
if (targetParent) {
|
||||
target.parent = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.addRule('connection.reconnectStart', function(context) {
|
||||
|
@ -87,13 +111,23 @@ BpmnRules.prototype.init = function() {
|
|||
canInsert(shapes, target, position);
|
||||
});
|
||||
|
||||
this.addRule([ 'shape.create', 'shape.append' ], function(context) {
|
||||
var target = context.target,
|
||||
shape = context.shape,
|
||||
source = context.source,
|
||||
position = context.position;
|
||||
this.addRule('shape.create', function(context) {
|
||||
return canCreate(
|
||||
context.shape,
|
||||
context.target,
|
||||
context.source,
|
||||
context.position
|
||||
);
|
||||
});
|
||||
|
||||
return canAttach([ shape ], target, source, position) || canCreate(shape, target, source, position);
|
||||
this.addRule('shape.attach', function(context) {
|
||||
|
||||
return canAttach(
|
||||
context.shape,
|
||||
context.target,
|
||||
null,
|
||||
context.position
|
||||
);
|
||||
});
|
||||
|
||||
this.addRule('element.copy', function(context) {
|
||||
|
@ -173,17 +207,16 @@ function isSame(a, b) {
|
|||
|
||||
function getOrganizationalParent(element) {
|
||||
|
||||
var bo = getBusinessObject(element);
|
||||
|
||||
while (bo && !is(bo, 'bpmn:Process')) {
|
||||
if (is(bo, 'bpmn:Participant')) {
|
||||
return bo.processRef || bo;
|
||||
do {
|
||||
if (is(element, 'bpmn:Process')) {
|
||||
return getBusinessObject(element);
|
||||
}
|
||||
|
||||
bo = bo.$parent;
|
||||
}
|
||||
if (is(element, 'bpmn:Participant')) {
|
||||
return getBusinessObject(element).processRef;
|
||||
}
|
||||
} while ((element = element.parent));
|
||||
|
||||
return bo;
|
||||
}
|
||||
|
||||
function isTextAnnotation(element) {
|
||||
|
@ -228,21 +261,20 @@ function isMessageFlowTarget(element) {
|
|||
|
||||
function getScopeParent(element) {
|
||||
|
||||
var bo = getBusinessObject(element);
|
||||
var parent = element;
|
||||
|
||||
if (is(bo, 'bpmn:Participant')) {
|
||||
return null;
|
||||
}
|
||||
while ((parent = parent.parent)) {
|
||||
|
||||
while (bo) {
|
||||
bo = bo.$parent;
|
||||
if (is(parent, 'bpmn:FlowElementsContainer')) {
|
||||
return getBusinessObject(parent);
|
||||
}
|
||||
|
||||
if (is(bo, 'bpmn:FlowElementsContainer')) {
|
||||
return bo;
|
||||
if (is(parent, 'bpmn:Participant')) {
|
||||
return getBusinessObject(parent).processRef;
|
||||
}
|
||||
}
|
||||
|
||||
return bo;
|
||||
return null;
|
||||
}
|
||||
|
||||
function isSameScope(a, b) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../../../TestHelper');
|
||||
var getBpmnJS = require('../../../TestHelper').getBpmnJS;
|
||||
|
||||
var TestContainer = require('mocha-test-container-support');
|
||||
|
||||
|
@ -171,7 +171,7 @@ describe('features - context-pad', function() {
|
|||
|
||||
function expectContextPadEntries(elementOrId, expectedEntries) {
|
||||
|
||||
TestHelper.getBpmnJS().invoke(function(elementRegistry, contextPad) {
|
||||
getBpmnJS().invoke(function(elementRegistry, contextPad) {
|
||||
|
||||
var element = typeof elementOrId === 'string' ? elementRegistry.get(elementOrId) : elementOrId;
|
||||
|
||||
|
@ -279,11 +279,68 @@ describe('features - context-pad', function() {
|
|||
});
|
||||
|
||||
|
||||
describe('create', function() {
|
||||
|
||||
var diagramXML = require('../../../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, {
|
||||
modules: testModules
|
||||
}));
|
||||
|
||||
|
||||
it('should attach boundary event', inject(function(dragging, contextPad, elementRegistry) {
|
||||
|
||||
// given
|
||||
var task = elementRegistry.get('Task_1');
|
||||
|
||||
// when
|
||||
contextPad.open(task);
|
||||
|
||||
contextPad.trigger('dragstart', padEvent('append.intermediate-event'));
|
||||
|
||||
dragging.move(canvasEvent({ x: task.x, y: task.y }));
|
||||
dragging.hover({ element: task });
|
||||
dragging.move(canvasEvent({ x: task.x + 80, y: task.y + 70 }));
|
||||
dragging.end();
|
||||
|
||||
// then
|
||||
expect(task.attachers).to.have.length(1);
|
||||
}));
|
||||
|
||||
|
||||
it('should attach boundary event to other target', inject(
|
||||
function(dragging, contextPad, elementRegistry) {
|
||||
|
||||
// given
|
||||
var task = elementRegistry.get('Task_1');
|
||||
|
||||
var subProcess = elementRegistry.get('SubProcess_1');
|
||||
|
||||
// when
|
||||
contextPad.open(task);
|
||||
|
||||
contextPad.trigger('dragstart', padEvent('append.intermediate-event'));
|
||||
|
||||
dragging.move(canvasEvent({ x: subProcess.x, y: subProcess.y }));
|
||||
dragging.hover({ element: subProcess });
|
||||
dragging.move(canvasEvent({ x: subProcess.x + 80, y: subProcess.y + 5 }));
|
||||
dragging.end();
|
||||
|
||||
// then
|
||||
expect(subProcess.attachers).to.have.length(1);
|
||||
})
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('replace', function() {
|
||||
|
||||
var diagramXML = require('../../../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||
beforeEach(bootstrapModeler(diagramXML, {
|
||||
modules: testModules
|
||||
}));
|
||||
|
||||
var container;
|
||||
|
||||
|
@ -297,20 +354,15 @@ describe('features - context-pad', function() {
|
|||
// given
|
||||
var element = elementRegistry.get('StartEvent_1'),
|
||||
padding = 5,
|
||||
replaceMenuRect,
|
||||
padMenuRect;
|
||||
padMenuRect,
|
||||
replaceMenuRect;
|
||||
|
||||
contextPad.open(element);
|
||||
padMenuRect = contextPad.getPad(element).html.getBoundingClientRect();
|
||||
|
||||
// mock event
|
||||
var event = {
|
||||
target: padEntry(container, 'replace'),
|
||||
preventDefault: function() {}
|
||||
};
|
||||
|
||||
// when
|
||||
contextPad.trigger('click', event);
|
||||
contextPad.trigger('click', padEvent('replace'));
|
||||
|
||||
padMenuRect = contextPad.getPad(element).html.getBoundingClientRect();
|
||||
replaceMenuRect = domQuery('.bpmn-replace', container).getBoundingClientRect();
|
||||
|
||||
// then
|
||||
|
@ -319,7 +371,7 @@ describe('features - context-pad', function() {
|
|||
}));
|
||||
|
||||
|
||||
it('should not include control if replacement is disallowed',
|
||||
it('should hide wrench if replacement is disallowed',
|
||||
inject(function(elementRegistry, contextPad, customRules) {
|
||||
|
||||
// given
|
||||
|
@ -337,10 +389,11 @@ describe('features - context-pad', function() {
|
|||
|
||||
// then
|
||||
expect(padEntry(padNode, 'replace')).not.to.exist;
|
||||
}));
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
it('should include control if replacement is allowed',
|
||||
it('should show wrench if replacement is allowed',
|
||||
inject(function(elementRegistry, contextPad, customRules) {
|
||||
|
||||
// given
|
||||
|
@ -358,78 +411,88 @@ describe('features - context-pad', function() {
|
|||
|
||||
// then
|
||||
expect(padEntry(padNode, 'replace')).to.exist;
|
||||
}));
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
it('should open the replace menu after an element is created if it has modifier key',
|
||||
inject(function(create, dragging, canvas, elementFactory) {
|
||||
// given
|
||||
var rootShape = canvas.getRootElement(),
|
||||
startEvent = elementFactory.createShape({ type: 'bpmn:StartEvent' }),
|
||||
replaceMenu;
|
||||
describe('create + <CTRL>', function() {
|
||||
|
||||
// when
|
||||
create.start(canvasEvent({ x: 0, y: 0 }), startEvent);
|
||||
it('should open replace',
|
||||
inject(function(create, dragging, canvas, elementFactory) {
|
||||
|
||||
dragging.move(canvasEvent({ x: 50, y: 50 }));
|
||||
dragging.hover({ element: rootShape });
|
||||
dragging.move(canvasEvent({ x: 75, y: 75 }));
|
||||
// given
|
||||
var rootShape = canvas.getRootElement(),
|
||||
startEvent = elementFactory.createShape({ type: 'bpmn:StartEvent' }),
|
||||
replaceMenu;
|
||||
|
||||
dragging.end(canvasEvent({ x: 75, y: 75 }, { ctrlKey: true, metaKey: true }));
|
||||
// when
|
||||
create.start(canvasEvent({ x: 0, y: 0 }), startEvent);
|
||||
|
||||
replaceMenu = domQuery('.bpmn-replace', container);
|
||||
dragging.move(canvasEvent({ x: 50, y: 50 }));
|
||||
dragging.hover({ element: rootShape });
|
||||
dragging.move(canvasEvent({ x: 75, y: 75 }));
|
||||
|
||||
// then
|
||||
expect(replaceMenu).to.exist;
|
||||
}));
|
||||
dragging.end(canvasEvent({ x: 75, y: 75 }, { ctrlKey: true, metaKey: true }));
|
||||
|
||||
replaceMenu = domQuery('.bpmn-replace', container);
|
||||
|
||||
// then
|
||||
expect(replaceMenu).to.exist;
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
it('should open boundary event replace menu after an element is created if it has modifier key',
|
||||
inject(function(create, dragging, canvas, elementFactory, modeling, popupMenu) {
|
||||
// given
|
||||
var rootShape = canvas.getRootElement();
|
||||
var task = elementFactory.createShape({ type: 'bpmn:Task' });
|
||||
var intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
||||
it('should open boundary event replace menu',
|
||||
inject(function(create, dragging, canvas, elementFactory, modeling, popupMenu) {
|
||||
|
||||
modeling.createShape(task, { x: 100, y: 100 }, rootShape);
|
||||
// given
|
||||
var rootShape = canvas.getRootElement();
|
||||
var task = elementFactory.createShape({ type: 'bpmn:Task' });
|
||||
var intermediateEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
||||
|
||||
// when
|
||||
create.start(canvasEvent({ x: 0, y: 0 }), intermediateEvent);
|
||||
modeling.createShape(task, { x: 100, y: 100 }, rootShape);
|
||||
|
||||
dragging.move(canvasEvent({ x: 50, y: 50 }));
|
||||
dragging.hover({ element: task });
|
||||
dragging.move(canvasEvent({ x: 50, y: 65 }));
|
||||
// when
|
||||
create.start(canvasEvent({ x: 0, y: 0 }), intermediateEvent);
|
||||
|
||||
dragging.end(canvasEvent({ x: 50, y: 65 }, { ctrlKey: true, metaKey: true }));
|
||||
dragging.move(canvasEvent({ x: 50, y: 50 }));
|
||||
dragging.hover({ element: task });
|
||||
dragging.move(canvasEvent({ x: 50, y: 65 }));
|
||||
|
||||
// then
|
||||
var replaceMenu = domQuery.all('[data-id$="-boundary"]', popupMenu._current.container);
|
||||
expect(replaceMenu).to.exist;
|
||||
expect(replaceMenu.length).to.eql(13);
|
||||
}));
|
||||
dragging.end(canvasEvent({ x: 50, y: 65 }, { ctrlKey: true, metaKey: true }));
|
||||
|
||||
// then
|
||||
var replaceMenu = domQuery.all('[data-id$="-boundary"]', popupMenu._current.container);
|
||||
expect(replaceMenu).to.exist;
|
||||
expect(replaceMenu.length).to.eql(13);
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
it('should not open the replace menu after an element is created when there is none',
|
||||
inject(function(create, dragging, canvas, elementFactory) {
|
||||
// given
|
||||
var rootShape = canvas.getRootElement(),
|
||||
dataObject = elementFactory.createShape({ type: 'bpmn:DataObjectReference' }),
|
||||
replaceMenu;
|
||||
it('should not open non-existing replace menu',
|
||||
inject(function(create, dragging, canvas, elementFactory) {
|
||||
// given
|
||||
var rootShape = canvas.getRootElement(),
|
||||
dataObject = elementFactory.createShape({ type: 'bpmn:DataObjectReference' }),
|
||||
replaceMenu;
|
||||
|
||||
// when
|
||||
create.start(canvasEvent({ x: 0, y: 0 }), dataObject);
|
||||
// when
|
||||
create.start(canvasEvent({ x: 0, y: 0 }), dataObject);
|
||||
|
||||
dragging.move(canvasEvent({ x: 50, y: 50 }));
|
||||
dragging.hover({ element: rootShape });
|
||||
dragging.move(canvasEvent({ x: 75, y: 75 }));
|
||||
dragging.move(canvasEvent({ x: 50, y: 50 }));
|
||||
dragging.hover({ element: rootShape });
|
||||
dragging.move(canvasEvent({ x: 75, y: 75 }));
|
||||
|
||||
dragging.end(canvasEvent({ x: 75, y: 75 }, { ctrlKey: true, metaKey: true }));
|
||||
dragging.end(canvasEvent({ x: 75, y: 75 }, { ctrlKey: true, metaKey: true }));
|
||||
|
||||
replaceMenu = domQuery('.bpmn-replace', container);
|
||||
replaceMenu = domQuery('.bpmn-replace', container);
|
||||
|
||||
// then
|
||||
expect(replaceMenu).to.not.exist;
|
||||
}));
|
||||
// then
|
||||
expect(replaceMenu).to.not.exist;
|
||||
}
|
||||
));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -442,12 +505,6 @@ describe('features - context-pad', function() {
|
|||
modules: testModules.concat(autoPlaceModule)
|
||||
}));
|
||||
|
||||
var container;
|
||||
|
||||
beforeEach(function() {
|
||||
container = TestContainer.get(this);
|
||||
});
|
||||
|
||||
|
||||
it('should trigger', inject(function(elementRegistry, contextPad) {
|
||||
|
||||
|
@ -457,12 +514,7 @@ describe('features - context-pad', function() {
|
|||
contextPad.open(element);
|
||||
|
||||
// mock event
|
||||
var event = {
|
||||
clientX: 100,
|
||||
clientY: 100,
|
||||
target: padEntry(container, 'append.gateway'),
|
||||
preventDefault: function() {}
|
||||
};
|
||||
var event = padEvent('append.gateway');
|
||||
|
||||
// when
|
||||
contextPad.trigger('click', event);
|
||||
|
@ -522,3 +574,19 @@ describe('features - context-pad', function() {
|
|||
function padEntry(element, name) {
|
||||
return domQuery('[data-action="' + name + '"]', element);
|
||||
}
|
||||
|
||||
|
||||
function padEvent(entry) {
|
||||
|
||||
return getBpmnJS().invoke(function(overlays) {
|
||||
|
||||
var target = padEntry(overlays._overlayRoot, entry);
|
||||
|
||||
return {
|
||||
target: target,
|
||||
preventDefault: function() {},
|
||||
clientX: 100,
|
||||
clientY: 100
|
||||
};
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue