feat(contextPad): add color picker

Related to #1491
This commit is contained in:
Beatriz Mendes 2022-05-19 13:32:13 +02:00
parent 9b1096abbc
commit 3fc2cba013
6 changed files with 318 additions and 15 deletions

View File

@ -8,7 +8,7 @@
--color-grey-225-10-80: hsl(225, 10%, 80%);
--color-grey-225-10-85: hsl(225, 10%, 85%);
--color-grey-225-10-90: hsl(225, 10%, 90%);
--color-grey-225-10-95: hsl(225, 10%, 95%);
--color-grey-225-10-95: hsl(225, 10%, 95%);
--color-grey-225-10-97: hsl(225, 10%, 97%);
--color-blue-205-100-45: hsl(205, 100%, 45%);
@ -24,8 +24,8 @@
--color-red-360-100-97: hsl(360, 100%, 97%);
--color-white: hsl(0, 0%, 100%);
--color-black: hsl(0, 0%, 0%);
--color-black-opacity-05: hsla(0, 0%, 0%, 5%);
--color-black: hsl(0, 0%, 0%);
--color-black-opacity-05: hsla(0, 0%, 0%, 5%);
--color-black-opacity-10: hsla(0, 0%, 0%, 10%);
--breadcrumbs-font-family: var(--bjs-font-family);
@ -113,4 +113,61 @@
.selected .bjs-drilldown-empty {
display: inherit;
}
/* COLOR PICKER */
.bpmn-color-picker .djs-popup-header {
display: flex;
width: 66px;
flex-wrap: wrap;
padding: 1px;
}
/* override popup menu to have color grid */
[class^="color-icon-"],
[class*=" color-icon-"] {
padding: 0 !important;
margin: 1px !important;
}
[class^="color-icon-"]:before,
[class*=" color-icon-"]:before {
display: flex;
content: '';
width: 16px;
height: 16px;
border-radius: 0.1em;
border: 0.03em solid black;
padding: 0;
}
.color-icon-default:before {
background-color: white;
border-color: black;
}
.color-icon-blue:before {
background-color: rgb(187, 222, 251);
border-color: rgb(30, 136, 229);
}
.color-icon-orange:before {
background-color: rgb(255, 224, 178);
border-color: rgb(251, 140, 0);
}
.color-icon-green:before {
background-color: rgb(200, 230, 201);
border-color: rgb(67, 160, 71);
}
.color-icon-red:before {
background-color: rgb(255, 205, 210);
border-color: rgb(229, 57, 53);
}
.color-icon-purple:before {
background-color: rgb(225, 190, 231);
border-color: rgb(142, 36, 170);
}

View File

@ -428,6 +428,33 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
});
}
// color picker
var colorImageSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048">' +
'<path d="M 1647.63 97.188 L 1248.282 497.465 L 1139.624 606.124 L 940.879 549.473 L 734.703 491.893 L 730.989 495.608 L 564.748 661.847 L 962.238 1059.336 L 1359.728 1456.827 L 1525.969 1290.587 L 1525.969 1289.658 L 1529.684 1286.873 L 1472.104 1080.698 L 1420.096 897.74 L 1534.327 783.509 L 1933.674 384.162 L 1647.63 97.188 Z M 446.802 781.652 L 76.244 1151.28 L 473.734 1548.77 L 871.224 1947.188 L 1241.783 1576.632 L 844.292 1179.142 L 446.802 781.652 Z"/>' +
'</svg>';
var colorImageUrl = 'data:image/svg+xml;utf8,' + encodeURIComponent(colorImageSvg);
assign(actions, {
'set-color': {
group: 'edit',
title: translate('Set color'),
imageUrl: colorImageUrl,
action: {
click: function(event, element) {
popupMenu.open(
element,
'bpmn-color-picker',
assign(getReplaceMenuPosition(element), {
cursor: { x: event.x, y: event.y }
})
);
}
}
} });
// delete element entry, only show if allowed by rules
var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] });

View File

@ -0,0 +1,63 @@
var COLORS = [{
title: 'Default',
fill: undefined,
stroke: undefined
}, {
title: 'Blue',
fill: 'rgb(187, 222, 251)',
stroke: 'rgb(30, 136, 229)'
}, {
title: 'Orange',
fill: 'rgb(255, 224, 178)',
stroke: 'rgb(251, 140, 0)'
}, {
title: 'Green',
fill: 'rgb(200, 230, 201)',
stroke: 'rgb(67, 160, 71)'
}, {
title: 'Red',
fill: 'rgb(255, 205, 210)',
stroke: 'rgb(229, 57, 53)'
}, {
title: 'Purple',
fill: 'rgb(225, 190, 231)',
stroke: 'rgb(142, 36, 170)'
}];
export default function ColorMenuProvider(popupMenu, modeling, translate) {
this._popupMenu = popupMenu;
this._modeling = modeling;
this._translate = translate;
this.register();
}
ColorMenuProvider.prototype.register = function() {
this._popupMenu.registerProvider('bpmn-color-picker', this);
};
ColorMenuProvider.prototype.getEntries = function() {
return [];
};
ColorMenuProvider.prototype.getHeaderEntries = function(elements) {
var modeling = this._modeling;
var translate = this._translate;
return COLORS.map(function(color) {
return {
id: color.title.toLowerCase() + '-color',
title: translate(color.title),
className: 'color-icon-' + color.title.toLowerCase(),
action: function() {
modeling.setColor(elements, {
fill: color.fill,
stroke: color.stroke
});
}
};
});
};
ColorMenuProvider.$inject = ['popupMenu', 'modeling', 'translate'];

View File

@ -1,6 +1,6 @@
import PopupMenuModule from 'diagram-js/lib/features/popup-menu';
import ReplaceModule from '../replace';
import ColorMenuProvider from './ColorMenuProvider';
import ReplaceMenuProvider from './ReplaceMenuProvider';
@ -9,6 +9,11 @@ export default {
PopupMenuModule,
ReplaceModule
],
__init__: [ 'replaceMenuProvider' ],
replaceMenuProvider: [ 'type', ReplaceMenuProvider ]
__init__: [
'replaceMenuProvider',
'colorMenuProvider'
],
replaceMenuProvider: [ 'type', ReplaceMenuProvider ],
colorMenuProvider: [ 'type', ColorMenuProvider ]
};

View File

@ -207,7 +207,8 @@ describe('features - context-pad', function() {
'append.gateway',
'append.append-task',
'append.intermediate-event',
'append.text-annotation'
'append.text-annotation',
'set-color'
]);
}));
@ -223,7 +224,8 @@ describe('features - context-pad', function() {
'append.condition-intermediate-event',
'append.signal-intermediate-event',
'append.text-annotation',
'!append.task'
'!append.task',
'set-color'
]);
}));
@ -233,7 +235,8 @@ describe('features - context-pad', function() {
expectContextPadEntries('EndEvent_1', [
'connect',
'replace',
'!append.task'
'!append.task',
'set-color'
]);
}));
@ -244,7 +247,8 @@ describe('features - context-pad', function() {
'connect',
'replace',
'!append.end-event',
'append.text-annotation'
'append.text-annotation',
'set-color'
]);
}));
@ -255,7 +259,8 @@ describe('features - context-pad', function() {
'connect',
'replace',
'append.compensation-activity',
'!append.end-event'
'!append.end-event',
'set-color'
]);
}));
@ -266,7 +271,8 @@ describe('features - context-pad', function() {
'connect',
'append.text-annotation',
'replace',
'!append.end-event'
'!append.end-event',
'set-color'
]);
}));
@ -277,7 +283,8 @@ describe('features - context-pad', function() {
'connect',
'append.text-annotation',
'replace',
'!append.end-event'
'!append.end-event',
'set-color'
]);
}));
@ -287,7 +294,8 @@ describe('features - context-pad', function() {
expectContextPadEntries('Group_1', [
'append.text-annotation',
'delete',
'!replace'
'!replace',
'set-color'
]);
}));
@ -298,7 +306,8 @@ describe('features - context-pad', function() {
'connect',
'delete',
'!replace',
'!append.text-annotation'
'!append.text-annotation',
'set-color'
]);
}));
@ -620,6 +629,45 @@ describe('features - context-pad', function() {
});
describe('set-color', function() {
var diagramXML = require('../../../fixtures/bpmn/simple.bpmn');
beforeEach(bootstrapModeler(diagramXML, {
modules: testModules
}));
var container;
beforeEach(function() {
container = TestContainer.get(this);
});
it('should show popup menu in the correct position', inject(function(elementRegistry, contextPad) {
// given
var element = elementRegistry.get('StartEvent_1'),
padding = 5,
padMenuRect,
colorMenuRect;
contextPad.open(element);
// when
contextPad.trigger('click', padEvent('set-color'));
padMenuRect = contextPad.getPad(element).html.getBoundingClientRect();
colorMenuRect = domQuery('.bpmn-color-picker', container).getBoundingClientRect();
// then
expect(colorMenuRect.left).to.be.at.most(padMenuRect.left);
expect(colorMenuRect.top).to.be.at.most(padMenuRect.bottom + padding);
}));
});
});

View File

@ -0,0 +1,103 @@
import {
bootstrapModeler,
getBpmnJS,
inject
} from 'test/TestHelper';
import {
createEvent as globalEvent
} from '../../../util/MockEvents';
import coreModule from 'lib/core';
import modelingModule from 'lib/features/modeling';
import colorMenuProvider from 'lib/features/popup-menu';
import {
query as domQuery,
queryAll as domQueryAll
} from 'min-dom';
describe('features/popup-menu - replace menu provider', function() {
var simpleXML= require('../../../fixtures/bpmn/simple.bpmn');
var testModules = [
coreModule,
colorMenuProvider,
modelingModule
];
beforeEach(bootstrapModeler(simpleXML, { modules: testModules }));
it('should show all color options', inject(function(elementRegistry) {
var task = elementRegistry.get('Task_1');
openPopup(task);
var entries = queryEntries();
expect(entries).to.have.lengthOf(6);
}));
it('should call setColor', inject(function(modeling, elementRegistry) {
var spy = sinon.spy(modeling, 'setColor');
var task = elementRegistry.get('Task_1');
openPopup(task);
triggerAction('blue-color');
expect(spy).to.have.been.calledWith(
task,
{
fill: 'rgb(187, 222, 251)',
stroke: 'rgb(30, 136, 229)'
}
);
}));
});
// helpers ////////////
function openPopup(element, offset) {
offset = offset || 100;
getBpmnJS().invoke(function(popupMenu) {
popupMenu.open(element, 'bpmn-color-picker', {
x: element.x + offset, y: element.y + offset
});
});
}
function queryEntry(id) {
var container = getBpmnJS().get('canvas').getContainer();
return domQuery('.djs-popup [data-id="' + id + '"]', container);
}
function queryEntries() {
var container = getBpmnJS().get('canvas').getContainer();
return domQueryAll('.djs-popup .entry', container);
}
function triggerAction(id) {
var entry = queryEntry(id);
if (!entry) {
throw new Error('entry "'+ id +'" not found in replace menu');
}
var popupMenu = getBpmnJS().get('popupMenu');
return popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
}