Skip to content

Commit

Permalink
feat(custom-elements): add custom connections
Browse files Browse the repository at this point in the history
closes #12
  • Loading branch information
philippfromme authored and nikku committed May 30, 2016
1 parent abae2a2 commit 9fc57b4
Show file tree
Hide file tree
Showing 19 changed files with 1,414 additions and 476 deletions.
31 changes: 28 additions & 3 deletions custom-elements/Gruntfile.js
Expand Up @@ -3,6 +3,7 @@
module.exports = function(grunt) {

require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);

var path = require('path');

Expand All @@ -13,6 +14,10 @@ module.exports = function(grunt) {
return path.join(path.dirname(require.resolve(project)), file);
}

// configures browsers to run test against
// any of [ 'PhantomJS', 'Chrome', 'Firefox', 'IE']
var TEST_BROWSERS = ((process.env.TEST_BROWSERS || '').replace(/^\s+|\s+$/, '') || 'PhantomJS').split(/\s*,\s*/g);

// project configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
Expand All @@ -34,8 +39,10 @@ module.exports = function(grunt) {
browserify: {
options: {
browserifyOptions: {
// make sure we do not include browser shims unnecessarily
builtins: false,
debug: true,
// strip unnecessary built-ins
builtins: [ 'events' ],
// make sure we do not include Node stubs unnecessarily
insertGlobalVars: {
process: function () {
return 'undefined';
Expand Down Expand Up @@ -111,6 +118,20 @@ module.exports = function(grunt) {
]
}
}
},
karma: {
options: {
configFile: 'test/config/karma.unit.js'
},
single: {
singleRun: true,
autoWatch: false,

browsers: TEST_BROWSERS
},
unit: {
browsers: TEST_BROWSERS
}
}
});

Expand All @@ -125,5 +146,9 @@ module.exports = function(grunt) {
'watch'
]);

grunt.registerTask('default', [ 'jshint', 'build' ]);
grunt.registerTask('test', [ 'karma:single' ]);

grunt.registerTask('auto-test', [ 'karma:unit' ]);

grunt.registerTask('default', [ 'jshint', 'test', 'build' ]);
};
44 changes: 34 additions & 10 deletions custom-elements/README.md
@@ -1,31 +1,45 @@
# custom elements

> Advanced example. Works with [bpmn-js](https://github.com/bpmn-io/bpmn-js) version v0.12.
> Advanced example. Works with [bpmn-js](https://github.com/bpmn-io/bpmn-js) version v0.15.
This example shows how custom element support can be added to [bpmn-js](https://github.com/bpmn-io/bpmn-js).
This example shows how to add support for custom shapes and connections to [bpmn-js](https://github.com/bpmn-io/bpmn-js).


## About

This example creates a custom BPMN modeler that can display and add custom shapes to BPMN 2.0 diagrams.
This example creates a custom BPMN modeler that can display and add custom shapes and connections to BPMN 2.0 diagrams.

The renderer ships with custom rules that define which modeling operations are possible on the custom elements.
It can import custom shapes from a [JSON](http://json.org/) descriptor and updates their properties during modeling.
The renderer ships with custom rules that define which modeling operations are possible on custom shapes and connections.
It can import custom shapes and connections from a [JSON](http://json.org/) descriptor and updates their properties during modeling.

![demo application screenshot](https://raw.githubusercontent.com/bpmn-io/bpmn-js-examples/master/custom-elements/docs/screenshot.png "bpmn-js custom elements example")
![demo application screenshot](docs/screenshot.png "bpmn-js custom elements example")


## Usage Summary

The example provides a [custom modeler](https://github.com/bpmn-io/bpmn-js-examples/blob/master/custom-elements/app/custom-modeler/index.js). After instantiation, the modeler allows you to set and get custom shapes.
The example provides a [custom modeler](https://github.com/bpmn-io/bpmn-js-examples/blob/master/custom-elements/app/custom-modeler/index.js). After instantiation, the modeler allows you to add and get custom shapes and connections.

```javascript
// set custom elements
// add custom elements
var customElements = [
{ type: 'custom:circle', x: 100, y: 300 }
{
"type":"custom:triangle",
"id":"CustomTriangle_1",
"x":300,
"y":300
},
{
"type":"custom:connection",
"id":"CustomConnection_1",
"source":"CustomTriangle_1",
"target":"Task_1",
"waypoints":[
// ...
]
}
];

customModeler.setCustomElements(customElements);
customModeler.addCustomElements(customElements);


// get them after modeling
Expand All @@ -35,6 +49,7 @@ customModeler.getCustomElements(); // all currently existing custom elements
The modeler ships with a [module](https://github.com/bpmn-io/bpmn-js-examples/blob/master/custom-elements/app/custom-modeler/custom/index.js) that provides the following [bpmn-js](https://github.com/bpmn-io/bpmn-js) extensions:

* [`CustomPalette`](https://github.com/bpmn-io/bpmn-js-examples/blob/master/custom-elements/app/custom-modeler/custom/CustomPalette.js): A custom palette that allows you to create custom elements
* [`CustomContextPadProvider`](app/custom-modeler/custom/CustomContextPadProvider.js): A custom context pad that allows you to connect custom elements to BPMN elements
* [`CustomElementFactory`](https://github.com/bpmn-io/bpmn-js-examples/blob/master/custom-elements/app/custom-modeler/custom/CustomElementFactory.js): A factory that knows about how to create BPMN and custom shapes
* [`CustomRenderer`](https://github.com/bpmn-io/bpmn-js-examples/blob/master/custom-elements/app/custom-modeler/custom/CustomRenderer.js): A renderer that knows how to draw custom elements
* [`CustomRules`](https://github.com/bpmn-io/bpmn-js-examples/blob/master/custom-elements/app/custom-modeler/custom/CustomRules.js): A rule provider that defines the allowed interaction with custom elements
Expand All @@ -47,10 +62,19 @@ Fetch dependencies:

```
npm install
```

Build example:
```
grunt auto-build
```

Run tests:

```
grunt auto-test
```

## License

MIT
4 changes: 2 additions & 2 deletions custom-elements/app/app.js
Expand Up @@ -21,9 +21,9 @@ modeler.importXML(pizzaDiagram, function(err) {

modeler.get('canvas').zoom('fit-viewport');

modeler.setCustomElements(customElements);
modeler.addCustomElements(customElements);
});


// expose bpmnjs to window for debugging purposes
window.bpmnjs = modeler;
window.bpmnjs = modeler;
117 changes: 107 additions & 10 deletions custom-elements/app/custom-elements.json
@@ -1,12 +1,109 @@
[
{
"type": "custom:triangle",
"x": 300,
"y": 300
},
{
"type": "custom:circle",
"x": 500,
"y": 400
}
{
"type":"custom:circle",
"id":"CustomCircle_1",
"x":806,
"y":210
},
{
"type":"custom:triangle",
"id":"CustomTriangle_1",
"x":300,
"y":300
},
{
"type":"custom:connection",
"id":"CustomConnection_2",
"waypoints":[
{
"original":{
"x":320,
"y":320
},
"x":330,
"y":320
},
{
"x":469,
"y":320
},
{
"x":469,
"y":225
},
{
"original":{
"x":559,
"y":200
},
"x":517,
"y":212
}
],
"source":"CustomTriangle_1",
"target":"Task_2"
},
{
"type":"custom:connection",
"id":"CustomConnection_1",
"source":"CustomTriangle_1",
"target":"Task_1",
"waypoints":[
{
"original":{
"x":319,
"y":302
},
"x":319,
"y":302
},
{
"x":319,
"y":200
},
{
"x":309,
"y":200
},
{
"original":{
"x":309,
"y":145
},
"x":309,
"y":145
}
]
},
{
"type":"custom:connection",
"waypoints":[
{
"original":{
"x":876,
"y":280
},
"x":946,
"y":280
},
{
"x":1028,
"y":280
},
{
"x":1028,
"y":111
},
{
"original":{
"x":876,
"y":111
},
"x":917,
"y":111
}
],
"source":"CustomCircle_1",
"target":"Task_3"
}
]
@@ -0,0 +1,61 @@
'use strict';

var inherits = require('inherits');

var ContextPadProvider = require('bpmn-js/lib/features/context-pad/ContextPadProvider');

var isAny = require('bpmn-js/lib/features/modeling/util/ModelingUtil').isAny;

var assign = require('lodash/object/assign'),
bind = require('lodash/function/bind');

function CustomContextPadProvider(contextPad, modeling, elementFactory, connect,
create, popupMenu, canvas, rules, translate) {

ContextPadProvider.call(this, contextPad, modeling, elementFactory, connect, create,
popupMenu, canvas, rules, translate);

var cached = bind(this.getContextPadEntries, this);

this.getContextPadEntries = function(element) {
var actions = cached(element);

var businessObject = element.businessObject;

function startConnect(event, element, autoActivate) {
connect.start(event, element, autoActivate);
}

if (isAny(businessObject, [ 'custom:triangle', 'custom:circle'])) {
assign(actions, {
'connect': {
group: 'connect',
className: 'bpmn-icon-connection-multi',
title: translate('Connect using custom connection'),
action: {
click: startConnect,
dragstart: startConnect
}
}
});
}

return actions;
};
}

inherits(CustomContextPadProvider, ContextPadProvider);

CustomContextPadProvider.$inject = [
'contextPad',
'modeling',
'elementFactory',
'connect',
'create',
'popupMenu',
'canvas',
'rules',
'translate'
];

module.exports = CustomContextPadProvider;
19 changes: 14 additions & 5 deletions custom-elements/app/custom-modeler/custom/CustomElementFactory.js
Expand Up @@ -24,23 +24,32 @@ function CustomElementFactory(bpmnFactory, moddle) {
* @return {djs.model.Base}
*/
this.create = function(elementType, attrs) {
var type = attrs.type,
size;
var type = attrs.type;

if (elementType === 'label') {
return self.baseCreate(elementType, assign({ type: 'label' }, LabelUtil.DEFAULT_LABEL_SIZE, attrs));
}

// add type to businessObject if custom
if (/^custom\:/.test(type)) {
if (!attrs.businessObject) {
attrs.businessObject = {
type: type
type: type,
};

if(attrs.id) {
assign(attrs.businessObject, {
id: attrs.id
});
}
}

size = self._getCustomElementSize(type);
// add width and height if shape
if (!/\:connection$/.test(type)) {
assign(attrs, self._getCustomElementSize(type));
}

return self.baseCreate(elementType, assign(attrs, size));
return self.baseCreate(elementType, attrs);
}

return self.createBpmnElement(elementType, attrs);
Expand Down

0 comments on commit 9fc57b4

Please sign in to comment.