feat(modeler): add bendpoints

It is now possible to add bendpoints to flows or drag bendpoints to
update their position / trigger reconnects.

Upon bendpoint move a rule is checked to figure out whether or not a
bendpoint operation is allowed or not.

Closes #123
Closes #138
Closes #139
Closes #165
This commit is contained in:
Nico Rehwaldt 2015-01-14 18:48:06 +01:00 committed by jdotzki
parent 2236965d4b
commit 85e512c97d
9 changed files with 159 additions and 30 deletions

View File

@ -65,6 +65,7 @@ Modeler.prototype._modelingModules = [
require('diagram-js/lib/features/keyboard'),
require('diagram-js/lib/features/snapping'),
require('diagram-js/lib/features/move'),
require('diagram-js/lib/features/bendpoints'),
require('diagram-js/lib/features/resize'),
require('diagram-js/lib/features/lasso-tool'),
require('./features/modeling'),

View File

@ -195,11 +195,12 @@ Viewer.prototype.importDefinitions = function(definitions, done) {
}
this.definitions = definitions;
this.diagram = this._createDiagram(this.options);
this._init(this.diagram);
var diagram = this.diagram = this._createDiagram(this.options);
Importer.importBpmnDiagram(this.diagram, definitions, done);
this._init(diagram);
Importer.importBpmnDiagram(diagram, definitions, done);
} catch (e) {
done(e);
}
@ -270,7 +271,6 @@ Viewer.prototype.on = function(event, handler) {
var diagram = this.diagram,
listeners = this.__listeners = this.__listeners || [];
listeners = this.__listeners || [];
listeners.push({ event: event, handler: handler });
if (diagram) {

View File

@ -34,7 +34,12 @@ function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) {
}
}
this.executed([ 'connection.layout', 'connection.create' ], cropConnection);
this.executed([
'connection.layout',
'connection.create',
'connection.reconnectEnd',
'connection.reconnectStart'
], cropConnection);
this.reverted([ 'connection.layout' ], function(e) {
delete e.context.cropped;
@ -78,8 +83,21 @@ function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) {
self.updateConnection(e.context.connection);
}
this.executed([ 'connection.create', 'connection.move', 'connection.delete' ], updateConnection);
this.reverted([ 'connection.create', 'connection.move', 'connection.delete' ], updateConnection);
this.executed([
'connection.create',
'connection.move',
'connection.delete',
'connection.reconnectEnd',
'connection.reconnectStart'
], updateConnection);
this.reverted([
'connection.create',
'connection.move',
'connection.delete',
'connection.reconnectEnd',
'connection.reconnectStart'
], updateConnection);
// update waypoints
@ -87,8 +105,21 @@ function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) {
self.updateConnectionWaypoints(e.context.connection);
}
this.executed([ 'connection.layout', 'connection.move' ], updateConnectionWaypoints);
this.reverted([ 'connection.layout', 'connection.move' ], updateConnectionWaypoints);
this.executed([
'connection.layout',
'connection.move',
'connection.updateWaypoints',
'connection.reconnectEnd',
'connection.reconnectStart'
], updateConnectionWaypoints);
this.reverted([
'connection.layout',
'connection.move',
'connection.updateWaypoints',
'connection.reconnectEnd',
'connection.reconnectStart'
], updateConnectionWaypoints);
}
module.exports = BpmnUpdater;

View File

@ -46,7 +46,11 @@ Modeling.prototype.connect = function(source, target, attrs) {
targetBo = target.businessObject;
if (!attrs) {
if (sourceBo.$instanceOf('bpmn:FlowNode') && targetBo.$instanceOf('bpmn:FlowNode')) {
if (sourceBo.$instanceOf('bpmn:FlowNode') &&
targetBo.$instanceOf('bpmn:FlowNode') &&
!sourceBo.$instanceOf('bpmn:EndEvent') &&
!targetBo.$instanceOf('bpmn:StartEvent')) {
attrs = {
type: 'bpmn:SequenceFlow'
};

View File

@ -19,23 +19,64 @@ ModelingRules.prototype.init = function() {
// rules
this.addRule('connection.create', function(context) {
var source = context.source,
target = context.target;
function canConnect(source, target, connection) {
if (!source || source.labelTarget || !target || target.labelTarget) {
return null;
}
return source.businessObject.$parent === target.businessObject.$parent &&
source.businessObject.$instanceOf('bpmn:FlowNode') &&
!source.businessObject.$instanceOf('bpmn:EndEvent') &&
!target.businessObject.$instanceOf('bpmn:StartEvent') &&
(target.businessObject.$instanceOf('bpmn:FlowNode') ||
target.businessObject.$instanceOf('bpmn:TextAnnotation'));
var sourceBo = source.businessObject,
targetBo = target.businessObject,
connectionBo = connection && connection.businessObject;
if (sourceBo.$parent !== targetBo.$parent) {
return false;
}
if (connectionBo && connectionBo.$instanceOf('bpmn:SequenceFlow')) {
if (!sourceBo.$instanceOf('bpmn:FlowNode') ||
!targetBo.$instanceOf('bpmn:FlowNode') ||
sourceBo.$instanceOf('bpmn:EndEvent') ||
targetBo.$instanceOf('bpmn:StartEvent')) {
return false;
}
}
return (sourceBo.$instanceOf('bpmn:FlowNode') ||
sourceBo.$instanceOf('bpmn:TextAnnotation')) &&
(targetBo.$instanceOf('bpmn:FlowNode') ||
targetBo.$instanceOf('bpmn:TextAnnotation'));
}
this.addRule('connection.create', function(context) {
var source = context.source,
target = context.target;
return canConnect(source, target);
});
this.addRule('connection.reconnectStart', function(context) {
var connection = context.connection,
source = context.hover,
target = connection.target;
return canConnect(source, target, connection);
});
this.addRule('connection.reconnectEnd', function(context) {
var connection = context.connection,
source = connection.source,
target = context.hover;
return canConnect(source, target, connection);
});
this.addRule('connection.updateWaypoints', function(context) {
// OK! but visually ignore
return null;
});
this.addRule('shape.resize', function(context) {

View File

@ -83,6 +83,37 @@ describe('Modeler', function() {
});
describe('bendpoint editing support', function() {
var Events = require('diagram-js/test/util/Events');
it('should allow to edit bendpoints', function(done) {
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
createModeler(xml, function(err, viewer) {
// given
var bendpointMove = viewer.get('bendpointMove'),
dragging = viewer.get('dragging'),
elementRegistry = viewer.get('elementRegistry'),
createEvent = Events.scopedCreate(viewer.get('canvas'));
// assume
expect(bendpointMove).toBeDefined();
// when
bendpointMove.start(createEvent({ x: 0, y: 0 }), elementRegistry.get('SequenceFlow_1'), 1);
dragging.move(createEvent({ x: 200, y: 200 }));
done(err);
});
});
});
it('should handle errors', function(done) {
var xml = 'invalid stuff';

View File

@ -1,7 +1,6 @@
'use strict';
var Matchers = require('../../../Matchers'),
TestHelper = require('../../../TestHelper');
var TestHelper = require('../../../TestHelper');
/* global bootstrapModeler, inject */
@ -18,9 +17,6 @@ var LabelUtil = require('../../../../lib/util/Label');
describe('features/modeling - append shape', function() {
beforeEach(Matchers.addDeepEquals);
var diagramXML = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
var testModules = [ coreModule, modelingModule ];

View File

@ -0,0 +1,29 @@
'use strict';
var TestHelper = require('../../../TestHelper');
/* global bootstrapModeler, inject */
var _ = require('lodash');
var fs = require('fs');
var modelingModule = require('../../../../lib/features/modeling'),
bendpointsModule = require('diagram-js/lib/features/bendpoints'),
coreModule = require('../../../../lib/core');
describe('features/bendpoints', function() {
var diagramXML = fs.readFileSync('test/fixtures/bpmn/features/drop/drop.bpmn', 'utf8');
var testModules = [ coreModule, bendpointsModule, modelingModule ];
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('should contain bendpoints', inject(function(bendpoints) {
expect(bendpoints).toBeDefined();
}));
});

View File

@ -1,7 +1,6 @@
'use strict';
var Matchers = require('../../../Matchers'),
TestHelper = require('../../../TestHelper');
var TestHelper = require('../../../TestHelper');
/* global bootstrapModeler, inject */
@ -15,9 +14,6 @@ var modelingModule = require('../../../../lib/features/modeling'),
describe('features/move - drop', function() {
beforeEach(Matchers.addDeepEquals);
var diagramXML = fs.readFileSync('test/fixtures/bpmn/features/drop/drop.bpmn', 'utf8');
var diagramXML2 = fs.readFileSync('test/fixtures/bpmn/features/drop/recursive-task.bpmn', 'utf8');