├── .github └── workflows │ └── CI.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── colors ├── ColorContextPadProvider.js ├── ColorPopupProvider.js ├── color-picker.css └── index.js ├── eslint.config.mjs ├── example ├── app.js ├── index.html ├── newDiagram.bpmn └── style.css ├── package-lock.json ├── package.json ├── renovate.json ├── resources └── screenshot.png └── webpack.config.js /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [ push, pull_request ] 3 | jobs: 4 | Build: 5 | 6 | strategy: 7 | matrix: 8 | os: [ ubuntu-latest ] 9 | node-version: [ 20 ] 10 | 11 | runs-on: ${{ matrix.os }} 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: Use Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | cache: 'npm' 21 | - name: Install dependencies 22 | run: npm ci 23 | - name: Build 24 | run: npm run all -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to [bpmn-js-color-picker](https://github.com/bpmn-io/bpmn-js-color-picker) are documented here. We use [semantic versioning](http://semver.org/) for releases. 4 | 5 | ## Unreleased 6 | 7 | ___Note:__ Yet to be released changes appear here._ 8 | 9 | ## 0.7.1 10 | 11 | * `FIX`: correct positioning of color picker ([#40](https://github.com/bpmn-io/bpmn-js-color-picker/issues/40)) 12 | 13 | ## 0.7.0 14 | 15 | * `FEAT`: sentence-case labels ([`325dd81`](https://github.com/bpmn-io/bpmn-js-color-picker/commit/325dd813c3fc784df92fa27104bc5290d15aa190)) 16 | * `FEAT`: use `imageHtml` for icon rendering ([`36b39a9`](https://github.com/bpmn-io/bpmn-js-color-picker/commit/36b39a9267c19f61b415b384123a711ca38e3a38)) 17 | * `FEAT`: add `module` export 18 | 19 | ## 0.6.1 20 | 21 | * `CHORE`: add LICENSE 22 | 23 | ## 0.6.0 24 | 25 | * `FEAT`: allow icon hover styling ([#24](https://github.com/bpmn-io/bpmn-js-color-picker/pull/24), [#27](https://github.com/bpmn-io/bpmn-js-color-picker/pull/27)) 26 | * `FEAT`: respect default renderer fill and stoke color ([#26](https://github.com/bpmn-io/bpmn-js-color-picker/pull/26)) 27 | 28 | ## 0.5.0 29 | 30 | * `FEAT`: improve default color contrast ([#9](https://github.com/bpmn-io/bpmn-js-color-picker/pull/9)) 31 | * `FEAT`: integrate with new popup menu ([#10](https://github.com/bpmn-io/bpmn-js-color-picker/issues/10)) 32 | * `FEAT`: integrate with multi-element context pad ([#13](https://github.com/bpmn-io/bpmn-js-color-picker/issues/13)) 33 | * `FEAT`: dynamically generate entry colors ([#12](https://github.com/bpmn-io/bpmn-js-color-picker/issues/12)) 34 | * `FEAT`: be able to provide custom colors ([#11](https://github.com/bpmn-io/bpmn-js-color-picker/issues/11)) 35 | * `DEPS`: update to `bpmn-js@11` 36 | 37 | ## 0.4.0 38 | 39 | * `FEAT`: simplify color picker 40 | * `FEAT`: translate color picker entries 41 | 42 | ## ... 43 | 44 | Check `git log` for earlier history. 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-present Camunda Services GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bpmn-js Color Picker 2 | 3 | [![CI](https://github.com/bpmn-io/bpmn-js-color-picker/actions/workflows/CI.yml/badge.svg)](https://github.com/bpmn-io/bpmn-js-color-picker/actions/workflows/CI.yml) 4 | 5 | This [bpmn-js](https://github.com/bpmn-io/bpmn-js) extension adds a simple color picker to an elements context pad. Colors are serialized to BPMN 2.0 according to the [BPMN in Color proposal](https://github.com/bpmn-miwg/bpmn-in-color). 6 | 7 | ![bpmn-js color picker in action](./resources/screenshot.png) 8 | 9 | 10 | ## Features 11 | 12 | * Add color picker to the context pad 13 | * Color single and multiple elements 14 | * Serialize colors to BPMN 2.0 15 | * Render colors (built-in `bpmn-js@8.7+`) 16 | 17 | 18 | ## Use Extension 19 | 20 | Fetch `bpmn-js-color-picker` as a dependency: 21 | 22 | ``` 23 | npm install bpmn-js-color-picker --save 24 | ``` 25 | 26 | Extend your BPMN modeler with colors: 27 | 28 | ```javascript 29 | import BpmnModeler from 'bpmn-js/lib/Modeler'; 30 | 31 | import BpmnColorPickerModule from 'bpmn-js-color-picker'; 32 | 33 | const modeler = new BpmnModeler({ 34 | additionalModules: [ 35 | BpmnColorPickerModule 36 | ] 37 | }); 38 | ``` 39 | 40 | Add diagram-js, bpmn-font and [color picker](./colors/color-picker.css) stylesheets: 41 | 42 | ```html 43 | 44 | 45 | 46 | ``` 47 | 48 | 49 | ## Build Demo 50 | 51 | To run the live demo in the [`./example` directory](./example) (as shown in the screenshot above) execute: 52 | 53 | ``` 54 | npm start 55 | ``` 56 | 57 | 58 | ## Useful Resources 59 | 60 | * [Introduction to bpmn-js](https://bpmn.io/toolkit/bpmn-js/walkthrough/) 61 | * [Colors are here](https://bpmn.io/blog/posts/2016-colors-bpmn-js.html) 62 | -------------------------------------------------------------------------------- /colors/ColorContextPadProvider.js: -------------------------------------------------------------------------------- 1 | const colorImageSvg = ` 2 | 3 | `; 4 | 5 | 6 | export default function ColorContextPadProvider(contextPad, popupMenu, canvas, translate) { 7 | 8 | this._contextPad = contextPad; 9 | this._popupMenu = popupMenu; 10 | this._canvas = canvas; 11 | this._translate = translate; 12 | 13 | contextPad.registerProvider(this); 14 | } 15 | 16 | 17 | ColorContextPadProvider.$inject = [ 18 | 'contextPad', 19 | 'popupMenu', 20 | 'canvas', 21 | 'translate' 22 | ]; 23 | 24 | 25 | ColorContextPadProvider.prototype.getContextPadEntries = function(element) { 26 | return this._createPopupAction([ element ]); 27 | }; 28 | 29 | 30 | ColorContextPadProvider.prototype.getMultiElementContextPadEntries = function(elements) { 31 | 32 | return this._createPopupAction(elements); 33 | }; 34 | 35 | ColorContextPadProvider.prototype._createPopupAction = function(elements) { 36 | 37 | const translate = this._translate; 38 | const contextPad = this._contextPad; 39 | const popupMenu = this._popupMenu; 40 | 41 | return { 42 | 'set-color': { 43 | group: 'edit', 44 | className: 'bpmn-icon-color', 45 | title: translate('Set color'), 46 | html: `
${colorImageSvg}
`, 47 | action: { 48 | click: (event, element) => { 49 | 50 | // get start popup draw start position 51 | var position = { 52 | ...getStartPosition(contextPad, elements), 53 | cursor: { 54 | x: event.x, 55 | y: event.y 56 | } 57 | }; 58 | 59 | // open new color-picker popup 60 | popupMenu.open(elements, 'color-picker', position); 61 | } 62 | } 63 | } 64 | }; 65 | 66 | }; 67 | 68 | 69 | // helpers ////////////////////// 70 | 71 | function getStartPosition(contextPad, elements) { 72 | 73 | var Y_OFFSET = 5; 74 | 75 | var pad = contextPad.getPad(elements).html; 76 | 77 | var padRect = pad.getBoundingClientRect(); 78 | 79 | var pos = { 80 | x: padRect.left, 81 | y: padRect.bottom + Y_OFFSET 82 | }; 83 | 84 | return pos; 85 | } 86 | -------------------------------------------------------------------------------- /colors/ColorPopupProvider.js: -------------------------------------------------------------------------------- 1 | const COLORS = [ { 2 | label: 'Default', 3 | fill: undefined, 4 | stroke: undefined 5 | }, { 6 | label: 'Blue', 7 | fill: '#BBDEFB', 8 | stroke: '#0D4372' 9 | }, { 10 | label: 'Orange', 11 | fill: '#FFE0B2', 12 | stroke: '#6B3C00' 13 | }, { 14 | label: 'Green', 15 | fill: '#C8E6C9', 16 | stroke: '#205022' 17 | }, { 18 | label: 'Red', 19 | fill: '#FFCDD2', 20 | stroke: '#831311' 21 | }, { 22 | label: 'Purple', 23 | fill: '#E1BEE7', 24 | stroke: '#5B176D' 25 | } ]; 26 | 27 | 28 | export default function ColorPopupProvider(config, bpmnRendererConfig, popupMenu, modeling, translate) { 29 | this._popupMenu = popupMenu; 30 | this._modeling = modeling; 31 | this._translate = translate; 32 | 33 | this._colors = config && config.colors || COLORS; 34 | this._defaultFillColor = bpmnRendererConfig && bpmnRendererConfig.defaultFillColor || 'white'; 35 | this._defaultStrokeColor = bpmnRendererConfig && bpmnRendererConfig.defaultStrokeColor || 'rgb(34, 36, 42)'; 36 | 37 | this._popupMenu.registerProvider('color-picker', this); 38 | } 39 | 40 | 41 | ColorPopupProvider.$inject = [ 42 | 'config.colorPicker', 43 | 'config.bpmnRenderer', 44 | 'popupMenu', 45 | 'modeling', 46 | 'translate' 47 | ]; 48 | 49 | 50 | ColorPopupProvider.prototype.getEntries = function(elements) { 51 | var self = this; 52 | 53 | var colorIconHtml = ` 54 | 55 | 56 | 57 | `; 58 | 59 | var entries = this._colors.map(function(color) { 60 | 61 | var entryColorIconHtml = colorIconHtml.replace('var(--fill-color)', color.fill || self._defaultFillColor) 62 | .replace('var(--stroke-color)', color.stroke || self._defaultStrokeColor); 63 | 64 | return { 65 | title: self._translate(color.label), 66 | id: color.label.toLowerCase() + '-color', 67 | imageHtml: entryColorIconHtml, 68 | action: createAction(self._modeling, elements, color) 69 | }; 70 | }); 71 | 72 | return entries; 73 | }; 74 | 75 | 76 | function createAction(modeling, element, color) { 77 | return function() { 78 | modeling.setColor(element, color); 79 | }; 80 | } -------------------------------------------------------------------------------- /colors/color-picker.css: -------------------------------------------------------------------------------- 1 | /* COLOR PICKER */ 2 | 3 | .djs-popup.color-picker .entry { 4 | margin: 0; 5 | } 6 | 7 | .djs-popup.color-picker .djs-popup-group { 8 | display: grid; 9 | grid: auto-flow / 1fr 1fr 1fr; 10 | } -------------------------------------------------------------------------------- /colors/index.js: -------------------------------------------------------------------------------- 1 | import ColorContextPadProvider from './ColorContextPadProvider'; 2 | import ColorPopupProvider from './ColorPopupProvider'; 3 | 4 | export default { 5 | __init__: [ 6 | 'colorContextPadProvider', 7 | 'colorPopupProvider' 8 | ], 9 | colorContextPadProvider: [ 'type', ColorContextPadProvider ], 10 | colorPopupProvider: [ 'type', ColorPopupProvider ] 11 | }; -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import bpmnIoPlugin from 'eslint-plugin-bpmn-io'; 2 | 3 | const files = { 4 | ignored: [ 'dist' ], 5 | build: [ 6 | '*.js', 7 | '*.mjs' 8 | ] 9 | }; 10 | 11 | export default [ 12 | { 13 | ignores: files.ignored, 14 | }, 15 | ...bpmnIoPlugin.configs.browser.map(config => { 16 | return { 17 | ...config, 18 | ignores: files.build 19 | }; 20 | }), 21 | ...bpmnIoPlugin.configs.node.map(config => { 22 | return { 23 | ...config, 24 | files: files.build 25 | }; 26 | }) 27 | ]; -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | import newDiagramXML from './newDiagram.bpmn'; 2 | 3 | import ColorPickerModule from '..'; 4 | 5 | import BpmnModeler from 'bpmn-js/lib/Modeler'; 6 | 7 | 8 | const canvas = document.querySelector('#canvas'); 9 | 10 | const modeler = new BpmnModeler({ 11 | container: canvas, 12 | additionalModules: [ 13 | ColorPickerModule 14 | ] 15 | }); 16 | 17 | modeler.importXML(newDiagramXML).then(result => { 18 | 19 | const { 20 | warnings = [] 21 | } = result; 22 | 23 | if (warnings.length) { 24 | console.log('imported with warnings', warnings); 25 | } 26 | }).catch(error => { 27 | console.error('import error', error); 28 | }); 29 | 30 | 31 | // hook up with UI elements 32 | document.querySelector('#export-to-console').addEventListener('click', function(e) { 33 | 34 | e.preventDefault(); 35 | 36 | modeler.saveXML({ 37 | format: true 38 | }).then(result => { 39 | console.log(result.xml); 40 | }).catch(err => { 41 | console.error(err); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bpmn-js In Color 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/newDiagram.bpmn: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /example/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | width: 100%; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | .canvas { 9 | position: absolute; 10 | left: 0; 11 | top: 0; 12 | bottom: 0; 13 | right: 0; 14 | } 15 | 16 | /* EXPORT TO CONSOLE */ 17 | .export { 18 | position: absolute; 19 | top: 20px; 20 | right: 50%; 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bpmn-js-color-picker", 3 | "version": "0.7.1", 4 | "description": "A color picker for bpmn-js", 5 | "scripts": { 6 | "dev": "npm start", 7 | "all": "run-s lint test", 8 | "start": "webpack serve --open", 9 | "lint": "eslint .", 10 | "test": "echo 'no tests ;-)'" 11 | }, 12 | "repository": "https://github.com/bpmn-io/bpmn-js-color-picker.git", 13 | "keywords": [ 14 | "bpmn-js-example", 15 | "bpmn-in-color", 16 | "bpmn" 17 | ], 18 | "module": "./colors/index.js", 19 | "engines": { 20 | "node": "*" 21 | }, 22 | "author": { 23 | "name": "Vladimirs Katusenoks", 24 | "url": "https://github.com/vkatushenok" 25 | }, 26 | "license": "MIT", 27 | "devDependencies": { 28 | "bpmn-js": "^18.6.1", 29 | "copy-webpack-plugin": "^13.0.0", 30 | "eslint": "^9.27.0", 31 | "eslint-plugin-bpmn-io": "^2.2.0", 32 | "npm-run-all2": "^8.0.0", 33 | "webpack": "^5.99.8", 34 | "webpack-cli": "^6.0.0", 35 | "webpack-dev-server": "^5.0.0" 36 | }, 37 | "peerDependencies": { 38 | "bpmn-js": ">= 14" 39 | }, 40 | "files": [ 41 | "colors/*", 42 | "index.js" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>bpmn-io/renovate-config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /resources/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/bpmn-js-color-picker/418e829215aa0e34d6457d8cbd05a591a937473c/resources/screenshot.png -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const CopyPlugin = require('copy-webpack-plugin'); 4 | 5 | module.exports = { 6 | mode: 'development', 7 | entry: './example/app.js', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'app.js', 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.css$/, 16 | type: 'asset/source' 17 | }, 18 | { 19 | test: /\.bpmn$/, 20 | type: 'asset/source' 21 | } 22 | ] 23 | }, 24 | plugins: [ 25 | new CopyPlugin({ 26 | patterns: [ 27 | { from: '*.{html,css}', context: 'example', to: '.' }, 28 | { from: 'bpmn-js/dist/assets/**/*', context: 'node_modules', to: './vendor' }, 29 | { from: '*.css', context: 'colors', to: './vendor/bpmn-js-color-picker' } 30 | ], 31 | }), 32 | ], 33 | devtool: 'eval-source-map' 34 | }; --------------------------------------------------------------------------------