├── .eslintignore
├── .npmrc
├── .gitignore
├── .babelrc
├── docs
└── screenshot.png
├── app
├── css
│ └── app.css
├── custom
│ ├── index.js
│ ├── CustomPalette.js
│ └── CustomContextPad.js
├── index.html
└── app.js
├── webpack.config.js
├── package.json
├── README.md
└── resources
└── diagram.bpmn
/.eslintignore:
--------------------------------------------------------------------------------
1 | public/
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | public/
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [ "env" ]
3 | }
--------------------------------------------------------------------------------
/docs/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bpmn-io/bpmn-js-example-custom-controls/HEAD/docs/screenshot.png
--------------------------------------------------------------------------------
/app/css/app.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | margin: 0;
4 | outline: none;
5 | padding: 0;
6 | }
7 |
8 | html, body, #container {
9 | height: 100%;
10 | }
--------------------------------------------------------------------------------
/app/custom/index.js:
--------------------------------------------------------------------------------
1 | import CustomContextPad from './CustomContextPad';
2 | import CustomPalette from './CustomPalette';
3 |
4 | export default {
5 | __init__: [ 'customContextPad', 'customPalette' ],
6 | customContextPad: [ 'type', CustomContextPad ],
7 | customPalette: [ 'type', CustomPalette ]
8 | };
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | bpmn-js-example-model-extension
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | import BpmnModeler from 'bpmn-js/lib/Modeler';
2 |
3 | import customControlsModule from './custom';
4 |
5 | import diagramXML from '../resources/diagram.bpmn';
6 |
7 | const containerEl = document.getElementById('container');
8 |
9 | // create modeler
10 | const bpmnModeler = new BpmnModeler({
11 | container: containerEl,
12 | additionalModules: [
13 | customControlsModule
14 | ]
15 | });
16 |
17 | // import XML
18 | bpmnModeler.importXML(diagramXML, (err) => {
19 | if (err) {
20 | console.error(err);
21 | }
22 | });
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const CopyWebpackPlugin = require('copy-webpack-plugin');
2 |
3 | module.exports = {
4 | entry: {
5 | bundle: ['./app/app.js']
6 | },
7 | output: {
8 | path: __dirname + '/public',
9 | filename: 'app.js'
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.bpmn$/,
15 | use: 'raw-loader'
16 | }
17 | ]
18 | },
19 | plugins: [
20 | new CopyWebpackPlugin([
21 | { from: 'assets/**', to: 'vendor/bpmn-js', context: 'node_modules/bpmn-js/dist/' },
22 | { from: '**/*.{html,css}', context: 'app/' }
23 | ])
24 | ],
25 | mode: 'development',
26 | devtool: 'source-map'
27 | };
--------------------------------------------------------------------------------
/app/custom/CustomPalette.js:
--------------------------------------------------------------------------------
1 | export default class CustomPalette {
2 | constructor(create, elementFactory, palette, translate) {
3 | this.create = create;
4 | this.elementFactory = elementFactory;
5 | this.translate = translate;
6 |
7 | palette.registerProvider(this);
8 | }
9 |
10 | getPaletteEntries(element) {
11 | const {
12 | create,
13 | elementFactory,
14 | translate
15 | } = this;
16 |
17 | function createServiceTask(event) {
18 | const shape = elementFactory.createShape({ type: 'bpmn:ServiceTask' });
19 |
20 | create.start(event, shape);
21 | }
22 |
23 | return {
24 | 'create.service-task': {
25 | group: 'activity',
26 | className: 'bpmn-icon-service-task',
27 | title: translate('Create ServiceTask'),
28 | action: {
29 | dragstart: createServiceTask,
30 | click: createServiceTask
31 | }
32 | },
33 | }
34 | }
35 | }
36 |
37 | CustomPalette.$inject = [
38 | 'create',
39 | 'elementFactory',
40 | 'palette',
41 | 'translate'
42 | ];
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bpmn-js-example-custom-rendering",
3 | "version": "0.0.0",
4 | "description": "An example of creating custom rendering for bpmn-js",
5 | "scripts": {
6 | "all": "run-s lint build",
7 | "build": "webpack --mode production",
8 | "dev": "webpack-dev-server --content-base=public --open",
9 | "lint": "eslint .",
10 | "start": "run-s dev"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/bpmn-io/bpmn-js-example-model-extension"
15 | },
16 | "keywords": [
17 | "bpmnjs-example"
18 | ],
19 | "author": {
20 | "name": "Philipp Fromme",
21 | "url": "https://github.com/philippfromme"
22 | },
23 | "contributors": [
24 | {
25 | "name": "bpmn.io contributors",
26 | "url": "https://github.com/bpmn-io"
27 | }
28 | ],
29 | "license": "MIT",
30 | "devDependencies": {
31 | "copy-webpack-plugin": "^4.6.0",
32 | "eslint": "^5.0.1",
33 | "eslint-plugin-bpmn-io": "^0.5.3",
34 | "npm-run-all": "^4.1.3",
35 | "raw-loader": "^0.5.1",
36 | "webpack": "^4.15.1",
37 | "webpack-cli": "^3.0.8",
38 | "webpack-dev-server": "^3.1.14"
39 | },
40 | "dependencies": {
41 | "bpmn-js": "^3.2.2",
42 | "diagram-js": "^3.1.3",
43 | "tiny-svg": "^2.2.1"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/custom/CustomContextPad.js:
--------------------------------------------------------------------------------
1 | export default class CustomContextPad {
2 | constructor(config, contextPad, create, elementFactory, injector, translate) {
3 | this.create = create;
4 | this.elementFactory = elementFactory;
5 | this.translate = translate;
6 |
7 | if (config.autoPlace !== false) {
8 | this.autoPlace = injector.get('autoPlace', false);
9 | }
10 |
11 | contextPad.registerProvider(this);
12 | }
13 |
14 | getContextPadEntries(element) {
15 | const {
16 | autoPlace,
17 | create,
18 | elementFactory,
19 | translate
20 | } = this;
21 |
22 | function appendServiceTask(event, element) {
23 | if (autoPlace) {
24 | const shape = elementFactory.createShape({ type: 'bpmn:ServiceTask' });
25 |
26 | autoPlace.append(element, shape);
27 | } else {
28 | appendServiceTaskStart(event, element);
29 | }
30 | }
31 |
32 | function appendServiceTaskStart(event) {
33 | const shape = elementFactory.createShape({ type: 'bpmn:ServiceTask' });
34 |
35 | create.start(event, shape, element);
36 | }
37 |
38 | return {
39 | 'append.service-task': {
40 | group: 'model',
41 | className: 'bpmn-icon-service-task',
42 | title: translate('Append ServiceTask'),
43 | action: {
44 | click: appendServiceTask,
45 | dragstart: appendServiceTaskStart
46 | }
47 | }
48 | };
49 | }
50 | }
51 |
52 | CustomContextPad.$inject = [
53 | 'config',
54 | 'contextPad',
55 | 'create',
56 | 'elementFactory',
57 | 'injector',
58 | 'translate'
59 | ];
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > This example is part of our [:notebook: custom elements guide](https://github.com/bpmn-io/bpmn-js-examples/tree/master/custom-elements). Checkout the final result [here](https://github.com/bpmn-io/bpmn-js-example-custom-elements).
2 |
3 |
4 | # bpmn-js Example: Custom Controls
5 |
6 | An example of creating custom editing controls for [bpmn-js](https://github.com/bpmn-io/bpmn-js). Use this blueprint to suite the BPMN modeler to your specific needs.
7 |
8 |
9 | ## About
10 |
11 | This example adds controls that allow you to create `bpmn:ServiceTask` elements through the palette and the context pad.
12 |
13 | 
14 |
15 | ### Creating Custom Controls
16 |
17 | First, let's add the ability to create `bpmn:ServiceTask` elements through the [palette](https://github.com/bpmn-io/diagram-js/blob/master/lib/features/palette/Palette.js). We'll create a new module that will register itself with the palette as a provider for entries. We don't need to override the existing provider since we want to keep the existing entries. Our provider implements a `getPaletteEntries` method that returns a single entry:
18 |
19 | ```javascript
20 | getPaletteEntries(element) {
21 | const {
22 | create,
23 | elementFactory,
24 | translate
25 | } = this;
26 |
27 | function createServiceTask(event) {
28 | const shape = elementFactory.createShape({ type: 'bpmn:ServiceTask' });
29 |
30 | create.start(event, shape);
31 | }
32 |
33 | return {
34 | 'create.service-task': {
35 | group: 'activity',
36 | className: 'bpmn-icon-service-task',
37 | title: translate('Create ServiceTask'),
38 | action: {
39 | dragstart: createServiceTask,
40 | click: createServiceTask
41 | }
42 | }
43 | }
44 | }
45 | ```
46 |
47 | You can use the `group` property to group multiple entries. In this case, we're adding our entry to an existing group. Using the `className` property, you can style your entry through CSS. We're using a class that is part of [bpmn-font](https://github.com/bpmn-io/bpmn-font), so there will be a service task icon in the palette. Using the `action` property, we decide what happens when the user clicks or drags the icon.
48 |
49 | See the entire palette [here](app/custom/CustomPalette.js).
50 |
51 | Now, let's also add the ability to create `bpmn:ServiceTask` elements through the [context-pad](https://github.com/bpmn-io/diagram-js/blob/master/lib/features/context-pad/ContextPad.js):
52 |
53 | ```javascript
54 | 'append.service-task': {
55 | group: 'model',
56 | className: 'bpmn-icon-service-task',
57 | title: translate('Append ServiceTask'),
58 | action: {
59 | click: appendServiceTask,
60 | dragstart: appendServiceTaskStart
61 | }
62 | }
63 | ```
64 |
65 | See the entire context pad [here](app/custom/CustomContextPad.js).
66 |
67 | Next, let's add our custom controls to bpmn-js.
68 |
69 | ### Adding the Custom Controls to bpmn-js
70 |
71 | When creating a new instance of bpmn-js we need to add our custom controls using the `additionalModules` property:
72 |
73 | ```javascript
74 | import BpmnModeler from 'bpmn-js/lib/Modeler';
75 |
76 | import customControlsModule from './custom';
77 |
78 | const bpmnModeler = new BpmnModeler({
79 | additionalModules: [
80 | customControlsModule
81 | ]
82 | });
83 | ```
84 |
85 | Our custom controls will now be added to bpmn-js.
86 |
87 | ## Run the Example
88 |
89 | You need a [NodeJS](http://nodejs.org) development stack with [npm](https://npmjs.org) installed to build the project.
90 |
91 | To install all project dependencies execute
92 |
93 | ```sh
94 | npm install
95 | ```
96 |
97 | To start the example execute
98 |
99 | ```sh
100 | npm start
101 | ```
102 |
103 | To build the example into the `public` folder execute
104 |
105 | ```sh
106 | npm run all
107 | ```
108 |
109 |
110 | ## License
111 |
112 | MIT
113 |
--------------------------------------------------------------------------------
/resources/diagram.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_1
6 |
7 |
8 |
9 | SequenceFlow_1
10 | SequenceFlow_2
11 | SequenceFlow_5
12 |
13 |
14 |
15 |
16 | SequenceFlow_2
17 |
18 |
19 |
20 | SequenceFlow_5
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------