| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- import {
- assign
- } from 'min-dash';
- import {
- getLabel
- } from '../../util/LabelUtil';
- import {
- is
- } from '../../util/ModelUtil';
- import { isAny } from '../modeling/util/ModelingUtil';
- import {
- isExpanded,
- isHorizontal
- } from '../../util/DiUtil';
- import {
- getExternalLabelMid,
- isLabelExternal,
- hasExternalLabel,
- isLabel
- } from '../../util/LabelUtil';
- /**
- * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
- * @typedef {import('../modeling/BpmnFactory').default} BpmnFactory
- * @typedef {import('diagram-js/lib/core/Canvas').default} Canvas
- * @typedef {import('diagram-js-direct-editing/lib/DirectEditing').default} DirectEditing
- * @typedef {import('../modeling/Modeling').default} Modeling
- * @typedef {import('diagram-js/lib/features/resize/ResizeHandles').default} ResizeHandles
- * @typedef {import('../../draw/TextRenderer').default} TextRenderer
- *
- * @typedef {import('../../model/Types').Element} Element
- *
- * @typedef { {
- * bounds: {
- * x: number;
- * y: number;
- * width: number;
- * height: number;
- * minWidth?: number;
- * minHeight?: number;
- * };
- * style: Object;
- * } } DirectEditingContext
- */
- var HIGH_PRIORITY = 2000;
- /**
- * @param {EventBus} eventBus
- * @param {BpmnFactory} bpmnFactory
- * @param {Canvas} canvas
- * @param {DirectEditing} directEditing
- * @param {Modeling} modeling
- * @param {ResizeHandles} resizeHandles
- * @param {TextRenderer} textRenderer
- */
- export default function LabelEditingProvider(
- eventBus, bpmnFactory, canvas, directEditing,
- modeling, resizeHandles, textRenderer) {
- this._bpmnFactory = bpmnFactory;
- this._canvas = canvas;
- this._modeling = modeling;
- this._textRenderer = textRenderer;
- directEditing.registerProvider(this);
- // listen to dblclick on non-root elements
- eventBus.on('element.dblclick', function(event) {
- activateDirectEdit(event.element, true);
- });
- // complete on followup canvas operation
- eventBus.on([
- 'autoPlace.start',
- 'canvas.viewbox.changing',
- 'drag.init',
- 'element.mousedown',
- 'popupMenu.open',
- 'root.set',
- 'selection.changed'
- ], function() {
- if (directEditing.isActive()) {
- directEditing.complete();
- }
- });
- eventBus.on([
- 'shape.remove',
- 'connection.remove'
- ], HIGH_PRIORITY, function(event) {
- if (directEditing.isActive(event.element)) {
- directEditing.cancel();
- }
- });
- // cancel on command stack changes
- eventBus.on([ 'commandStack.changed' ], function(e) {
- if (directEditing.isActive()) {
- directEditing.cancel();
- }
- });
- eventBus.on('directEditing.activate', function(event) {
- resizeHandles.removeResizers();
- });
- eventBus.on('create.end', 500, function(event) {
- var context = event.context,
- element = context.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(nikku): 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;
- }
- if (context.hints && context.hints.createElementsBehavior === false) {
- return;
- }
- activateDirectEdit(element);
- });
- eventBus.on('autoPlace.end', 500, function(event) {
- activateDirectEdit(event.shape);
- });
- function activateDirectEdit(element, force) {
- if (force ||
- isAny(element, [ 'bpmn:Task', 'bpmn:TextAnnotation', 'bpmn:Participant' ]) ||
- isCollapsedSubProcess(element)) {
- directEditing.activate(element);
- }
- }
- }
- LabelEditingProvider.$inject = [
- 'eventBus',
- 'bpmnFactory',
- 'canvas',
- 'directEditing',
- 'modeling',
- 'resizeHandles',
- 'textRenderer'
- ];
- /**
- * Activate direct editing for activities and text annotations.
- *
- * @param {Element} element
- *
- * @return { {
- * text: string;
- * options?: {
- * autoResize?: boolean;
- * centerVertically?: boolean;
- * resizable?: boolean;
- * }
- * } & DirectEditingContext }
- */
- LabelEditingProvider.prototype.activate = function(element) {
- // text
- var text = getLabel(element);
- if (text === undefined) {
- return;
- }
- var context = {
- text: text
- };
- // bounds
- var bounds = this.getEditingBBox(element);
- assign(context, bounds);
- var options = {};
- // tasks
- if (
- isAny(element, [
- 'bpmn:Task',
- 'bpmn:Participant',
- 'bpmn:Lane',
- 'bpmn:CallActivity'
- ]) ||
- isCollapsedSubProcess(element)
- ) {
- assign(options, {
- centerVertically: true
- });
- }
- // external labels
- if (isLabelExternal(element)) {
- 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 {Element} element
- *
- * @return {DirectEditingContext}
- */
- 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();
- var defaultStyle = this._textRenderer.getDefaultStyle(),
- externalStyle = this._textRenderer.getExternalStyle();
- // take zoom into account
- var externalFontSize = externalStyle.fontSize * zoom,
- externalLineHeight = externalStyle.lineHeight,
- defaultFontSize = defaultStyle.fontSize * zoom,
- defaultLineHeight = defaultStyle.lineHeight;
- var style = {
- fontFamily: this._textRenderer.getDefaultStyle().fontFamily,
- fontWeight: this._textRenderer.getDefaultStyle().fontWeight
- };
- // adjust for expanded pools AND lanes
- if (is(element, 'bpmn:Lane') || isExpandedPool(element)) {
- var isHorizontalLane = isHorizontal(element);
- var laneBounds = isHorizontalLane ? {
- width: bbox.height,
- height: 30 * zoom,
- x: bbox.x - bbox.height / 2 + (15 * zoom),
- y: mid.y - (30 * zoom) / 2
- } : {
- width: bbox.width,
- height: 30 * zoom
- };
- assign(bounds, laneBounds);
- assign(style, {
- fontSize: defaultFontSize + 'px',
- lineHeight: defaultLineHeight,
- paddingTop: (7 * zoom) + 'px',
- paddingBottom: (7 * zoom) + 'px',
- paddingLeft: (5 * zoom) + 'px',
- paddingRight: (5 * zoom) + 'px',
- transform: isHorizontalLane ? 'rotate(-90deg)' : null
- });
- }
- // internal labels for collapsed participants
- if (isCollapsedPool(element)) {
- var isHorizontalPool = isHorizontal(element);
- var poolBounds = isHorizontalPool ? {
- width: bbox.width,
- height: bbox.height
- } : {
- width: bbox.height,
- height: bbox.width,
- x: mid.x - bbox.height / 2,
- y: mid.y - bbox.width / 2
- };
- assign(bounds, poolBounds);
- assign(style, {
- fontSize: defaultFontSize + 'px',
- lineHeight: defaultLineHeight,
- paddingTop: (7 * zoom) + 'px',
- paddingBottom: (7 * zoom) + 'px',
- paddingLeft: (5 * zoom) + 'px',
- paddingRight: (5 * zoom) + 'px',
- transform: isHorizontalPool ? null : 'rotate(-90deg)'
- });
- }
- // internal labels for tasks and collapsed call activities
- // and sub processes
- if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity' ]) ||
- isCollapsedSubProcess(element)) {
- assign(bounds, {
- width: bbox.width,
- height: bbox.height
- });
- assign(style, {
- fontSize: defaultFontSize + 'px',
- lineHeight: defaultLineHeight,
- 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: defaultFontSize + 'px',
- lineHeight: defaultLineHeight,
- paddingTop: (7 * zoom) + 'px',
- paddingBottom: (7 * zoom) + 'px',
- paddingLeft: (5 * zoom) + 'px',
- paddingRight: (5 * zoom) + 'px'
- });
- }
- var width = 90 * zoom,
- paddingTop = 7 * zoom,
- paddingBottom = 4 * zoom;
- // external labels for events, data elements, gateways, groups and connections
- if (target.labelTarget) {
- assign(bounds, {
- width: width,
- height: bbox.height + paddingTop + paddingBottom,
- x: mid.x - width / 2,
- y: bbox.y - paddingTop
- });
- assign(style, {
- fontSize: externalFontSize + 'px',
- lineHeight: externalLineHeight,
- paddingTop: paddingTop + 'px',
- paddingBottom: paddingBottom + 'px'
- });
- }
- // external label not yet created
- if (isLabelExternal(target)
- && !hasExternalLabel(target)
- && !isLabel(target)) {
- var externalLabelMid = getExternalLabelMid(element);
- var absoluteBBox = canvas.getAbsoluteBBox({
- x: externalLabelMid.x,
- y: externalLabelMid.y,
- width: 0,
- height: 0
- });
- var height = externalFontSize + paddingTop + paddingBottom;
- assign(bounds, {
- width: width,
- height: height,
- x: absoluteBBox.x - width / 2,
- y: absoluteBBox.y - height / 2
- });
- assign(style, {
- fontSize: externalFontSize + 'px',
- lineHeight: externalLineHeight,
- 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: (5 * zoom) + 'px',
- paddingBottom: (7 * zoom) + 'px',
- paddingLeft: (7 * zoom) + 'px',
- paddingRight: (5 * zoom) + 'px',
- fontSize: defaultFontSize + 'px',
- lineHeight: defaultLineHeight
- });
- }
- return { bounds: bounds, style: style };
- };
- LabelEditingProvider.prototype.update = function(
- element, newLabel,
- activeContextText, bounds) {
- var newBounds,
- bbox;
- if (is(element, 'bpmn:TextAnnotation')) {
- bbox = this._canvas.getAbsoluteBBox(element);
- newBounds = {
- x: element.x,
- y: element.y,
- width: element.width / bbox.width * bounds.width,
- height: element.height / bbox.height * bounds.height
- };
- }
- if (isEmptyText(newLabel)) {
- newLabel = null;
- }
- this._modeling.updateLabel(element, newLabel, newBounds);
- };
- // 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);
- }
- function isEmptyText(label) {
- return !label || !label.trim();
- }
|