-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(properties-panel-extension): add example
Closes #19
- Loading branch information
Showing
14 changed files
with
1,016 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules/ | ||
dist/ | ||
tmp/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
module.exports = function(grunt) { | ||
|
||
require('load-grunt-tasks')(grunt); | ||
|
||
var path = require('path'); | ||
|
||
/** | ||
* Resolve external project resource as file path | ||
*/ | ||
function resolvePath(project, file) { | ||
return path.join(path.dirname(require.resolve(project)), file); | ||
} | ||
|
||
// project configuration | ||
grunt.initConfig({ | ||
pkg: grunt.file.readJSON('package.json'), | ||
|
||
config: { | ||
sources: 'app', | ||
dist: 'dist' | ||
}, | ||
|
||
jshint: { | ||
src: [ | ||
['<%=config.sources %>'] | ||
], | ||
options: { | ||
jshintrc: true | ||
} | ||
}, | ||
|
||
browserify: { | ||
options: { | ||
browserifyOptions: { | ||
debug: true, | ||
insertGlobalVars: [] | ||
}, | ||
transform: [ 'brfs' ] | ||
}, | ||
watch: { | ||
options: { | ||
watch: true | ||
}, | ||
files: { | ||
'<%= config.dist %>/index.js': [ '<%= config.sources %>/**/*.js' ] | ||
} | ||
}, | ||
app: { | ||
files: { | ||
'<%= config.dist %>/index.js': [ '<%= config.sources %>/**/*.js' ] | ||
} | ||
} | ||
}, | ||
|
||
copy: { | ||
diagram_js: { | ||
files: [ | ||
{ | ||
src: resolvePath('diagram-js', 'assets/diagram-js.css'), | ||
dest: '<%= config.dist %>/css/diagram-js.css' | ||
} | ||
] | ||
}, | ||
bpmn_js: { | ||
files: [ | ||
{ | ||
expand: true, | ||
cwd: resolvePath('bpmn-js', 'assets'), | ||
src: ['**/*.*', '!**/*.js'], | ||
dest: '<%= config.dist %>/vendor' | ||
} | ||
] | ||
}, | ||
app: { | ||
files: [ | ||
{ | ||
expand: true, | ||
cwd: '<%= config.sources %>/', | ||
src: ['**/*.*', '!**/*.js'], | ||
dest: '<%= config.dist %>' | ||
} | ||
] | ||
} | ||
}, | ||
|
||
less: { | ||
options: { | ||
dumpLineNumbers: 'comments', | ||
paths: [ | ||
'node_modules' | ||
] | ||
}, | ||
|
||
styles: { | ||
files: { | ||
'dist/css/app.css': 'styles/app.less' | ||
} | ||
} | ||
}, | ||
|
||
watch: { | ||
samples: { | ||
files: [ '<%= config.sources %>/**/*.*' ], | ||
tasks: [ 'copy:app' ] | ||
}, | ||
|
||
less: { | ||
files: [ | ||
'styles/**/*.less', | ||
'node_modules/bpmn-js-properties-panel/styles/**/*.less' | ||
], | ||
tasks: [ | ||
'less' | ||
] | ||
}, | ||
|
||
connect: { | ||
options: { | ||
livereload: 9014 | ||
}, | ||
files: [ | ||
'<%= config.dist %>/**/*.css' | ||
], | ||
tasks: [] | ||
} | ||
}, | ||
|
||
connect: { | ||
options: { | ||
appName: 'Firefox', // name of the app that opens, ie: open, start, xdg-open | ||
port: 9013, | ||
livereload: 9014, | ||
hostname: 'localhost' | ||
}, | ||
livereload: { | ||
options: { | ||
open: { appName: 'google-chrome' }, | ||
base: [ | ||
'<%= config.dist %>' | ||
] | ||
} | ||
} | ||
} | ||
}); | ||
|
||
// tasks | ||
|
||
grunt.registerTask('build', [ 'copy', 'less', 'browserify:app' ]); | ||
|
||
grunt.registerTask('auto-build', [ | ||
'copy', | ||
'less', | ||
'browserify:watch', | ||
'connect:livereload', | ||
'watch' | ||
]); | ||
|
||
grunt.registerTask('default', [ 'jshint', 'build' ]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
# Properties Panel Extension Example | ||
|
||
This example shows how to extend the [bpmn-js-properties-panel](https://github.com/bpmn-io/bpmn-js-properties-panel) with custom properties. | ||
|
||
![properties panel extension screenshot](https://raw.githubusercontent.com/bpmn-io/bpmn-js-examples/master/properties-panel-extension/docs/screenshot.png "Screenshot of the properties panel extension example") | ||
|
||
|
||
## About | ||
|
||
> If you need more information about setting up take look at the [basic properties example](https://github.com/bpmn-io/bpmn-js-examples/tree/master/properties-panel) first. | ||
In this example we extend the properties panel to allow editing a `magic:spell` property on all start events. To achieve that we will walk through the following steps: | ||
|
||
* Add a tab called "Magic" to contain the property | ||
* Add a group called "Black Magic" to this tab | ||
* Add a "spell" text input field to this group | ||
* Create a new moddle extension | ||
|
||
The property `magic:spell` will be persisted as an extension as part of the BPMN 2.0 document: | ||
|
||
```xml | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<bpmn2:definitions ... xmlns:magic="http://magic" id="sample-diagram"> | ||
<bpmn2:process id="Process_1"> | ||
<bpmn2:startEvent id="StartEvent_1" magic:spell="WOOO ZAAAA" /> | ||
</bpmn2:process> | ||
... | ||
</bpmn2:definitions> | ||
``` | ||
|
||
|
||
Let us look into all the necessary steps in detail. | ||
|
||
|
||
### Create a Properties Provider | ||
|
||
The first step to a custom property is to create your own `PropertiesProvider`. | ||
The provider defines which properties are available and how they are organized in the panel using tabs, groups and input elements. | ||
|
||
We created the [`MagicPropertiesProvider`](app/provider/magic/MagicPropertiesProvider.js) which exposes all basic BPMN properties (via a "general" tab) as well as the "magic" tab. | ||
|
||
```javascript | ||
function MagicPropertiesProvider(eventBus, bpmnFactory, elementRegistry) { | ||
|
||
... | ||
|
||
this.getTabs = function(element) { | ||
|
||
... | ||
|
||
// The "magic" tab | ||
var magicTab = { | ||
id: 'magic', | ||
label: 'Magic', | ||
groups: createMagicTabGroups(element, elementRegistry) | ||
}; | ||
|
||
// All avaliable tabs | ||
return [ | ||
generalTab, | ||
magicTab | ||
]; | ||
}; | ||
} | ||
``` | ||
|
||
|
||
### Define a Group | ||
|
||
As part of the properties provider we define the groups for the magic tab, too: | ||
|
||
```javascript | ||
// Require your custom property entries. | ||
// The entry is a text input field with logic attached to create, | ||
// update and delete the "spell" property. | ||
var spellProps = require('./parts/SpellProps'); | ||
|
||
// Create the custom magic tab | ||
function createMagicTabGroups(element, elementRegistry) { | ||
|
||
// Create a group called "Black Magic". | ||
var blackMagicGroup = { | ||
id: 'black-magic', | ||
label: 'Black Magic', | ||
entries: [ | ||
spellProps(blackMagicGroup, element); | ||
] | ||
}; | ||
|
||
// Add the spell props to the black magic group. | ||
spellProps(blackMagicGroup, element); | ||
|
||
return [ | ||
blackMagicGroup | ||
]; | ||
} | ||
``` | ||
|
||
|
||
### Define an Entry | ||
|
||
The "spell" entry is defined in [`SpellProps`](app/provider/magic/parts/SpellProps.js). We reuse [`EntryFactory#textField`](https://github.com/bpmn-io/bpmn-js-properties-panel/blob/master/lib/factory/EntryFactory.js#L79) to create a text field for the property. Note that we make sure that the entry is shown if a start event is selected: | ||
|
||
```javascript | ||
var entryFactory = require('bpmn-js-properties-panel/lib/factory/EntryFactory'); | ||
|
||
var is = require('bpmn-js/lib/util/ModelUtil').is; | ||
|
||
module.exports = function(group, element) { | ||
// only return an entry, if the currently selected element is a start event | ||
if (is(element, 'bpmn:StartEvent')) { | ||
group.entries.push(entryFactory.textField({ | ||
id : 'spell', | ||
description : 'Apply a black magic spell', | ||
label : 'Spell', | ||
modelProperty : 'spell' | ||
})); | ||
} | ||
}; | ||
``` | ||
|
||
You can look into the [`EntryFactory`](https://github.com/bpmn-io/bpmn-js-properties-panel/blob/master/lib/factory/EntryFactory.js) to find many other useful reusable form input components. You can also go further and define what happens if you enter text in an input field and what is shown in it if the element is selected. To do so you can override `entry#set` and `entry#get` methods. A good example for this is [`DocumentationProps`](https://github.com/bpmn-io/bpmn-js-properties-panel/blob/master/lib/provider/bpmn/parts/DocumentationProps.js). | ||
|
||
To get a better understand of the lifecycle of updating elements and the properties panel [this forum post](https://forum.bpmn.io/t/integrating-bpmn-js-properties-panel-with-the-bpmn-js-modeler/261/20) may be helpful. | ||
|
||
|
||
### Create a Moddle Extension | ||
|
||
The second step to create a custom property is to create a moddle extension so that moddle is aware of our new property "spell". This is important for moddle to write and read BPMN XML containing custom properties. The extension is basically a json descriptor file [magic.json](app/descriptors/magic.json) containing a definition of `bpmn:StartEvent#spell`: | ||
|
||
```javascript | ||
{ | ||
"name": "Magic", | ||
"prefix": "magic", | ||
"uri": "http://magic", | ||
"xml": { | ||
"tagAlias": "lowerCase" | ||
}, | ||
"associations": [], | ||
"types": [ | ||
{ | ||
"name": "BewitchedStartEvent", | ||
"extends": [ | ||
"bpmn:StartEvent" | ||
], | ||
"properties": [ | ||
{ | ||
"name": "spell", | ||
"isAttr": true, | ||
"type": "String" | ||
}, | ||
] | ||
}, | ||
] | ||
} | ||
``` | ||
|
||
In this file we define the new type `BewitchesStartEvent` which extends the type `bpmn:StartEvent` and adds the "spell" property as an attribute to it. | ||
|
||
|
||
### Plugging Everything together | ||
|
||
To ship our custom extension with the properties panel we have to wire both the moddle extension and the properties provider when creating the modeler. | ||
|
||
```javascript | ||
var propertiesPanelModule = require('bpmn-js-properties-panel'), | ||
propertiesProviderModule = require('./provider/magic'), | ||
magicModdleDescriptor = require('./descriptors/magic'); | ||
|
||
var canvas = $('#js-canvas'); | ||
|
||
var bpmnModeler = new BpmnModeler({ | ||
container: canvas, | ||
propertiesPanel: { | ||
parent: '#js-properties-panel' | ||
}, | ||
additionalModules: [ | ||
propertiesPanelModule, | ||
propertiesProviderModule | ||
], | ||
moddleExtensions: { | ||
magic: magicModdleDescriptor | ||
} | ||
}); | ||
``` | ||
|
||
|
||
## Running the Example | ||
|
||
Install all required dependencies: | ||
|
||
``` | ||
npm install | ||
npm install -g grunt-cli | ||
``` | ||
|
||
Build and run the project | ||
|
||
``` | ||
grunt auto-build | ||
``` | ||
|
||
|
||
## License | ||
|
||
MIT |
Oops, something went wrong.