feat: decouple DI from businessObject

In the diagram `di` is now accessed via the diagram element, not the
business object. This has the benefit that elements in multiple diagrams
can easily be represented.

Related to https://github.com/bpmn-io/bpmn-js/issues/1472

BREAKING CHANGE:

* Instead of referencing the `di` from the business object, reference it
  from the diagram element representing it.
This commit is contained in:
Martin Stamm 2021-08-06 10:35:41 +02:00 committed by Nico Rehwaldt
parent d19c4b0027
commit 2b11d871cd
8 changed files with 50 additions and 55 deletions

View File

@ -528,19 +528,6 @@ BaseViewer.prototype.clear = function() {
return;
}
// remove businessObject#di binding
//
// this is necessary, as we establish the bindings
// in the BpmnTreeWalker (and assume none are given
// on reimport)
this.get('elementRegistry').forEach(function(element) {
var bo = element.businessObject;
if (bo && bo.di) {
delete bo.di;
}
});
// remove drawn elements
Diagram.prototype.clear.call(this);
};

View File

@ -43,7 +43,7 @@ export function isCollection(element) {
}
export function getDi(element) {
return element.businessObject.di;
return element.di;
}
export function getSemantic(element) {

View File

@ -26,17 +26,18 @@ import {
} from './Util';
function elementData(semantic, attrs) {
function elementData(semantic, di, attrs) {
return assign({
id: semantic.id,
type: semantic.$type,
businessObject: semantic
businessObject: semantic,
di: di
}, attrs);
}
function getWaypoints(bo, source, target) {
function getWaypoints(di, source, target) {
var waypoints = bo.di.waypoint;
var waypoints = di.waypoint;
if (!waypoints || waypoints.length < 2) {
return [ getMid(source), getMid(target) ];
@ -92,10 +93,8 @@ BpmnImporter.$inject = [
* Add bpmn element (semantic) to the canvas onto the
* specified parent shape.
*/
BpmnImporter.prototype.add = function(semantic, parentElement) {
var di = semantic.di,
element,
BpmnImporter.prototype.add = function(semantic, di, parentElement) {
var element,
translate = this._translate,
hidden;
@ -107,7 +106,7 @@ BpmnImporter.prototype.add = function(semantic, parentElement) {
if (is(di, 'bpmndi:BPMNPlane')) {
// add a virtual element (not being drawn)
element = this._elementFactory.createRoot(elementData(semantic));
element = this._elementFactory.createRoot(elementData(semantic, di));
this._canvas.setRootElement(element);
}
@ -115,13 +114,14 @@ BpmnImporter.prototype.add = function(semantic, parentElement) {
// SHAPE
else if (is(di, 'bpmndi:BPMNShape')) {
var collapsed = !isExpanded(semantic),
var collapsed = !isExpanded(semantic, di),
isFrame = isFrameElement(semantic);
hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
var bounds = semantic.di.bounds;
var bounds = di.bounds;
element = this._elementFactory.createShape(elementData(semantic, {
element = this._elementFactory.createShape(elementData(semantic, di, {
collapsed: collapsed,
hidden: hidden,
x: Math.round(bounds.x),
@ -159,11 +159,11 @@ BpmnImporter.prototype.add = function(semantic, parentElement) {
hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
element = this._elementFactory.createConnection(elementData(semantic, {
element = this._elementFactory.createConnection(elementData(semantic, di, {
hidden: hidden,
source: source,
target: target,
waypoints: getWaypoints(semantic, source, target)
waypoints: getWaypoints(di, source, target)
}));
if (is(semantic, 'bpmn:DataAssociation')) {
@ -190,7 +190,7 @@ BpmnImporter.prototype.add = function(semantic, parentElement) {
// (optional) LABEL
if (isLabelExternal(semantic) && getLabel(element)) {
this.addLabel(semantic, element);
this.addLabel(semantic, di, element);
}
@ -239,12 +239,12 @@ BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElem
/**
* add label for an element
*/
BpmnImporter.prototype.addLabel = function(semantic, element) {
BpmnImporter.prototype.addLabel = function(semantic, di, element) {
var bounds,
text,
label;
bounds = getExternalLabelBounds(semantic, element);
bounds = getExternalLabelBounds(di, element);
text = getLabel(element);
@ -254,7 +254,7 @@ BpmnImporter.prototype.addLabel = function(semantic, element) {
bounds = this._textRenderer.getExternalLabelBounds(bounds, text);
}
label = this._elementFactory.createLabel(elementData(semantic, {
label = this._elementFactory.createLabel(elementData(semantic, di, {
id: semantic.id + '_label',
labelTarget: element,
type: 'label',

View File

@ -4,17 +4,10 @@ import {
forEach
} from 'min-dash';
import Refs from 'object-refs';
import {
elementToString
} from './Util';
var diRefs = new Refs(
{ name: 'bpmnElement', enumerable: true },
{ name: 'di', configurable: true }
);
/**
* Returns true if an element has the given meta-model type
*
@ -48,6 +41,8 @@ export default function BpmnTreeWalker(handler, translate) {
// prerequisites are drawn
var deferred = [];
var diMap = {};
// Helpers //////////////////////
function contextual(fn, ctx) {
@ -76,17 +71,17 @@ export default function BpmnTreeWalker(handler, translate) {
}
// call handler
return handler.element(element, ctx);
return handler.element(element, diMap[element.id], ctx);
}
function visitRoot(element, diagram) {
return handler.root(element, diagram);
return handler.root(element, diMap[element.id], diagram);
}
function visitIfDi(element, ctx) {
try {
var gfx = element.di && visit(element, ctx);
var gfx = diMap[element.id] && visit(element, ctx);
handled(element);
@ -109,7 +104,7 @@ export default function BpmnTreeWalker(handler, translate) {
var bpmnElement = di.bpmnElement;
if (bpmnElement) {
if (bpmnElement.di) {
if (diMap[bpmnElement.id]) {
logError(
translate('multiple DI elements defined for {element}', {
element: elementToString(bpmnElement)
@ -117,8 +112,7 @@ export default function BpmnTreeWalker(handler, translate) {
{ element: bpmnElement }
);
} else {
diRefs.bind(bpmnElement, 'di');
bpmnElement.di = di;
diMap[bpmnElement.id] = di;
}
} else {
logError(
@ -175,6 +169,7 @@ export default function BpmnTreeWalker(handler, translate) {
}
// load DI from selected diagram only
diMap = {};
handleDiagram(diagram);

View File

@ -49,12 +49,12 @@ export function importBpmnDiagram(diagram, definitions, bpmnDiagram) {
var visitor = {
root: function(element) {
return importer.add(element);
root: function(element, di) {
return importer.add(element, di);
},
element: function(element, parentShape) {
return importer.add(element, parentShape);
element: function(element, di, parentShape) {
return importer.add(element, di, parentShape);
},
error: function(message, context) {

View File

@ -1,6 +1,7 @@
import {
is,
getBusinessObject
getBusinessObject,
getDi
} from './ModelUtil';
import {
@ -8,14 +9,16 @@ import {
} from 'min-dash';
export function isExpanded(element) {
export function isExpanded(element, di) {
if (is(element, 'bpmn:CallActivity')) {
return false;
}
if (is(element, 'bpmn:SubProcess')) {
return getBusinessObject(element).di && !!getBusinessObject(element).di.isExpanded;
di = di || getDi(element);
return di && !!di.isExpanded;
}
if (is(element, 'bpmn:Participant')) {

View File

@ -116,15 +116,14 @@ export function getExternalLabelMid(element) {
* Returns the bounds of an elements label, parsed from the elements DI or
* generated from its bounds.
*
* @param {BpmnElement} semantic
* @param {BpmndDi} di
* @param {djs.model.Base} element
*/
export function getExternalLabelBounds(semantic, element) {
export function getExternalLabelBounds(di, element) {
var mid,
size,
bounds,
di = semantic.di,
label = di.label;
if (label && label.bounds) {

View File

@ -22,4 +22,15 @@ export function is(element, type) {
*/
export function getBusinessObject(element) {
return (element && element.businessObject) || element;
}
/**
* Return the di object for a given element.
*
* @param {djs.model.Base} element
*
* @return {ModdleElement}
*/
export function getDi(element) {
return element && element.di;
}