320 lines
7.7 KiB
JavaScript
320 lines
7.7 KiB
JavaScript
'use strict';
|
|
|
|
var assign = require('lodash/object/assign');
|
|
|
|
var UpdateLabelHandler = require('./cmd/UpdateLabelHandler');
|
|
|
|
var LabelUtil = require('./LabelUtil');
|
|
|
|
var is = require('../../util/ModelUtil').is,
|
|
isAny = require('../modeling/util/ModelingUtil').isAny,
|
|
isExpanded = require('../../util/DiUtil').isExpanded;
|
|
|
|
var SMALL_FONT_SIZE = 11,
|
|
SMALL_LINE_HEIGHT = 13,
|
|
MEDIUM_FONT_SIZE = 12,
|
|
MEDIUM_LINE_HEIGHT = 14;
|
|
|
|
function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, resizeHandles) {
|
|
|
|
this._canvas = canvas;
|
|
this._commandStack = commandStack;
|
|
|
|
directEditing.registerProvider(this);
|
|
|
|
commandStack.registerHandler('element.updateLabel', UpdateLabelHandler);
|
|
|
|
// listen to dblclick on non-root elements
|
|
eventBus.on('element.dblclick', function(event) {
|
|
activateDirectEdit(event.element, true);
|
|
});
|
|
|
|
// complete on followup canvas operation
|
|
eventBus.on([ 'element.mousedown', 'drag.init', 'canvas.viewbox.changing' ], function(event) {
|
|
directEditing.complete();
|
|
});
|
|
|
|
// cancel on command stack changes
|
|
eventBus.on([ 'commandStack.changed' ], function(e) {
|
|
directEditing.cancel();
|
|
});
|
|
|
|
|
|
eventBus.on('directEditing.activate', function(event) {
|
|
resizeHandles.removeResizers();
|
|
});
|
|
|
|
eventBus.on('create.end', 500, function(event) {
|
|
|
|
var element = event.shape,
|
|
canExecute = event.context.canExecute,
|
|
isTouch = event.isTouch;
|
|
|
|
// TODO(nikku): we need to find a way to support the direct editing
|
|
// on mobile devices; right now this will break for desworkflowediting on mobile devices
|
|
// as it breaks the user interaction workflow
|
|
|
|
// TODO(nre): we should temporarily focus the edited element here
|
|
// and release the focused viewport after the direct edit operation is finished
|
|
if (isTouch) {
|
|
return;
|
|
}
|
|
|
|
if (!canExecute) {
|
|
return;
|
|
}
|
|
|
|
activateDirectEdit(element);
|
|
});
|
|
|
|
|
|
function activateDirectEdit(element, force) {
|
|
if (force ||
|
|
isAny(element, [ 'bpmn:Task', 'bpmn:TextAnnotation' ]) ||
|
|
isCollapsedSubProcess(element)) {
|
|
|
|
directEditing.activate(element);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
LabelEditingProvider.$inject = [ 'eventBus', 'canvas', 'directEditing', 'commandStack', 'resizeHandles' ];
|
|
|
|
module.exports = LabelEditingProvider;
|
|
|
|
|
|
/**
|
|
* Activate direct editing for activities and text annotations.
|
|
*
|
|
* @param {djs.model.Base} element
|
|
*
|
|
* @return {Object} an object with properties bounds (position and size), text and options
|
|
*/
|
|
LabelEditingProvider.prototype.activate = function(element) {
|
|
|
|
// text
|
|
var text = LabelUtil.getLabel(element);
|
|
|
|
if (text === undefined) {
|
|
return;
|
|
}
|
|
|
|
var context = {
|
|
text: text
|
|
};
|
|
|
|
// bounds
|
|
var bounds = this.getEditingBBox(element);
|
|
|
|
assign(context, bounds);
|
|
|
|
// options
|
|
var target = element.label || element;
|
|
|
|
var options = {};
|
|
|
|
// tasks
|
|
if (isAny(element, [ 'bpmn:Task', 'bpmn:Participant', 'bpmn:Lane', 'bpmn:CallActivity' ]) ||
|
|
isCollapsedSubProcess(element)) {
|
|
assign(options, {
|
|
centerVertically: true
|
|
});
|
|
}
|
|
|
|
// external labels
|
|
if (target.labelTarget) {
|
|
assign(options, {
|
|
autoResize: true
|
|
});
|
|
}
|
|
|
|
// text annotations
|
|
if (is(element, 'bpmn:TextAnnotation')) {
|
|
assign(options, {
|
|
resizable: true,
|
|
autoResize: true
|
|
});
|
|
}
|
|
|
|
assign(context, {
|
|
options: options
|
|
});
|
|
|
|
return context;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the editing bounding box based on the element's size and position
|
|
*
|
|
* @param {djs.model.Base} element
|
|
*
|
|
* @return {Object} an object containing information about position and size (fixed or minimum and/or maximum)
|
|
*/
|
|
LabelEditingProvider.prototype.getEditingBBox = function(element) {
|
|
var canvas = this._canvas;
|
|
|
|
var target = element.label || element;
|
|
|
|
var bbox = canvas.getAbsoluteBBox(target);
|
|
|
|
var mid = {
|
|
x: bbox.x + bbox.width / 2,
|
|
y: bbox.y + bbox.height / 2
|
|
};
|
|
|
|
// default position
|
|
var bounds = { x: bbox.x, y: bbox.y };
|
|
|
|
var zoom = canvas.zoom();
|
|
|
|
// take zoom into account
|
|
var smallFontSize = SMALL_FONT_SIZE * zoom,
|
|
smallLineHeight = SMALL_LINE_HEIGHT * zoom,
|
|
mediumFontSize = MEDIUM_FONT_SIZE * zoom,
|
|
mediumLineHeight = MEDIUM_LINE_HEIGHT * zoom;
|
|
|
|
var style = {};
|
|
|
|
// adjust for expanded pools AND lanes
|
|
if (is(element, 'bpmn:Lane') || isExpandedPool(element)) {
|
|
|
|
assign(bounds, {
|
|
width: bbox.height,
|
|
height: 30 * zoom,
|
|
x: bbox.x - bbox.height / 2 + (15 * zoom),
|
|
y: mid.y - (30 * zoom) / 2
|
|
});
|
|
|
|
assign(style, {
|
|
fontSize: mediumFontSize + 'px',
|
|
lineHeight: mediumLineHeight + 'px',
|
|
paddingTop: (7 * zoom) + 'px',
|
|
paddingBottom: (7 * zoom) + 'px',
|
|
paddingLeft: (5 * zoom) + 'px',
|
|
paddingRight: (5 * zoom) + 'px',
|
|
transform: 'rotate(-90deg)'
|
|
});
|
|
}
|
|
|
|
|
|
// internal labels for tasks and collapsed call activities, sub processes and participants
|
|
if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity']) ||
|
|
isCollapsedPool(element) ||
|
|
isCollapsedSubProcess(element)) {
|
|
|
|
assign(bounds, {
|
|
width: bbox.width,
|
|
height: bbox.height
|
|
});
|
|
|
|
assign(style, {
|
|
fontSize: mediumFontSize + 'px',
|
|
lineHeight: mediumLineHeight + 'px',
|
|
paddingTop: (7 * zoom) + 'px',
|
|
paddingBottom: (7 * zoom) + 'px',
|
|
paddingLeft: (5 * zoom) + 'px',
|
|
paddingRight: (5 * zoom) + 'px'
|
|
});
|
|
}
|
|
|
|
|
|
// internal labels for expanded sub processes
|
|
if (isExpandedSubProcess(element)) {
|
|
assign(bounds, {
|
|
width: bbox.width,
|
|
x: bbox.x
|
|
});
|
|
|
|
assign(style, {
|
|
fontSize: mediumFontSize + 'px',
|
|
lineHeight: mediumLineHeight + 'px',
|
|
paddingTop: (7 * zoom) + 'px',
|
|
paddingBottom: (7 * zoom) + 'px',
|
|
paddingLeft: (5 * zoom) + 'px',
|
|
paddingRight: (5 * zoom) + 'px'
|
|
});
|
|
}
|
|
|
|
|
|
// external labels for events, data elements, gateways and connections
|
|
if (target.labelTarget) {
|
|
var width = 90 * zoom,
|
|
paddingTop = 7 * zoom,
|
|
paddingBottom = 4 * zoom;
|
|
|
|
assign(bounds, {
|
|
width: width,
|
|
height: bbox.height + paddingTop + paddingBottom,
|
|
x: mid.x - width / 2,
|
|
y: bbox.y - paddingTop
|
|
});
|
|
|
|
assign(style, {
|
|
fontSize: smallFontSize + 'px',
|
|
lineHeight: smallLineHeight + 'px',
|
|
paddingTop: paddingTop + 'px',
|
|
paddingBottom: paddingBottom + 'px'
|
|
});
|
|
}
|
|
|
|
|
|
// text annotations
|
|
if (is(element, 'bpmn:TextAnnotation')) {
|
|
assign(bounds, {
|
|
width: bbox.width,
|
|
height: bbox.height,
|
|
minWidth: 30 * zoom,
|
|
minHeight: 10 * zoom
|
|
});
|
|
|
|
assign(style, {
|
|
textAlign: 'left',
|
|
paddingTop: (7 * zoom) + 'px',
|
|
paddingBottom: (7 * zoom) + 'px',
|
|
paddingLeft: (5 * zoom) + 'px',
|
|
paddingRight: (5 * zoom) + 'px',
|
|
fontSize: mediumFontSize + 'px',
|
|
lineHeight: mediumLineHeight + 'px'
|
|
});
|
|
}
|
|
|
|
return { bounds: bounds, style: style };
|
|
};
|
|
|
|
|
|
LabelEditingProvider.prototype.update = function(element, newLabel, activeContextText, bounds) {
|
|
var absoluteElementBBox = this._canvas.getAbsoluteBBox(element);
|
|
|
|
this._commandStack.execute('element.updateLabel', {
|
|
element: element,
|
|
newLabel: newLabel,
|
|
bounds: {
|
|
x: element.x,
|
|
y: element.y,
|
|
width: element.width / absoluteElementBBox.width * bounds.width,
|
|
height: element.height / absoluteElementBBox.height * bounds.height
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
|
|
////////// helpers ////////////////////////////////
|
|
|
|
function isCollapsedSubProcess(element) {
|
|
return is(element, 'bpmn:SubProcess') && !isExpanded(element);
|
|
}
|
|
|
|
function isExpandedSubProcess(element) {
|
|
return is(element, 'bpmn:SubProcess') && isExpanded(element);
|
|
}
|
|
|
|
function isCollapsedPool(element) {
|
|
return is(element, 'bpmn:Participant') && !isExpanded(element);
|
|
}
|
|
|
|
function isExpandedPool(element) {
|
|
return is(element, 'bpmn:Participant') && isExpanded(element);
|
|
} |