chore: switch to truely multi-root aware mode

Use new `Canvas#*RootElement` APIs.
This commit is contained in:
Nico Rehwaldt 2021-12-10 00:31:00 +01:00 committed by fake-join[bot]
parent 56eb34cc82
commit 4e08a1c703
8 changed files with 197 additions and 95 deletions

View File

@ -1,24 +1,27 @@
/**
* Move collapsed subprocesses into view when drilling down. Zoom and scroll
* are saved in a session.
* Move collapsed subprocesses into view when drilling down.
*
* Zoom and scroll are saved in a session.
*
* @param {eventBus} eventBus
* @param {canvas} canvas
*/
export default function DrilldownCentering(eventBus, canvas) {
var currentPlane = 'base';
var positionMap = {};
eventBus.on('plane.set', function(event) {
var currentRoot = null;
var positionMap = new Map();
eventBus.on('root.set', function(event) {
var currentViewbox = canvas.viewbox();
positionMap[currentPlane] = {
positionMap.set(currentRoot, {
x: currentViewbox.x,
y: currentViewbox.y,
zoom: currentViewbox.scale
};
});
var planeId = event.plane.name;
var storedViewbox = positionMap[planeId] || { x: 0, y: 0, zoom: 1 };
var newRoot = event.element;
var storedViewbox = positionMap.get(newRoot) || { x: 0, y: 0, zoom: 1 };
var dx = (currentViewbox.x - storedViewbox.x) * currentViewbox.scale,
dy = (currentViewbox.y - storedViewbox.y) * currentViewbox.scale;
@ -34,8 +37,78 @@ export default function DrilldownCentering(eventBus, canvas) {
canvas.zoom(storedViewbox.zoom, { x: 0, y: 0 });
}
currentPlane = planeId;
currentRoot = newRoot;
});
eventBus.on('root.remove', function(event) {
positionMap.remove(event.element);
});
eventBus.on('diagram.clear', function() {
positionMap.clear();
currentRoot = null;
});
}
DrilldownCentering.$inject = [ 'eventBus', 'canvas' ];
/**
* ES5 Map implementation. Works.
*/
function Map() {
this._entries = [];
this.set = function(key, value) {
var found = false;
for (var k in this._entries) {
if (this._entries[k][0] === key) {
this._entries[k][1] = value;
found = true;
break;
}
}
if (!found) {
this._entries.push([ key, value ]);
}
};
this.get = function(key) {
for (var k in this._entries) {
if (this._entries[k][0] === key) {
return this._entries[k][1];
}
}
return null;
};
this.clear = function() {
this._entries.length = 0;
};
this.remove = function(key) {
var idx = -1;
for (var k in this._entries) {
if (this._entries[k][0] === key) {
idx = k;
break;
}
}
if (idx !== -1) {
this._entries.splice(idx, 1);
}
};
}

View File

@ -21,21 +21,15 @@ export default function DrilldownOverlays(eventBus, elementRegistry, overlays, c
var containerClasses = classes(container);
container.appendChild(breadcrumbs);
function updateBreadcrumbs(plane) {
var subProcess = elementRegistry.get(plane.name);
var parents = getParentChain(subProcess);
function updateBreadcrumbs(element) {
var parents = getParentChain(element);
var path = parents.map(function(el) {
var title = escapeHTML(el.name || el.id);
var link = domify('<li><span class="bjs-crumb"><a title="' + title + '">' + title + '</a></span></li>');
link.addEventListener('click', function() {
if (canvas.getPlane(el.id)) {
canvas.setActivePlane(el.id);
} else {
var plane = canvas.findPlane(el.id);
canvas.setActivePlane(plane);
}
canvas.setRootElement(canvas.findRoot(planeId(el)) || canvas.findRoot(el.id));
});
return link;
@ -52,17 +46,15 @@ export default function DrilldownOverlays(eventBus, elementRegistry, overlays, c
});
}
eventBus.on('plane.set', function(event) {
var plane = event.plane;
updateBreadcrumbs(plane);
eventBus.on('root.set', function(event) {
updateBreadcrumbs(event.element);
});
var createOverlay = function(element) {
var html = domify('<button class="bjs-drilldown">' + ARROW_DOWN_SVG + '</button>');
html.addEventListener('click', function() {
canvas.setActivePlane(element.id);
canvas.setRootElement(canvas.findRoot(planeId(element)));
});
overlays.add(element, {
@ -76,9 +68,10 @@ export default function DrilldownOverlays(eventBus, elementRegistry, overlays, c
var addOverlays = function(elements) {
elements.forEach(function(element) {
if (is(element, 'bpmn:SubProcess')
&& element.collapsed
&& canvas.getPlane(element.id)) {
&& canvas.findRoot(planeId(element))) {
createOverlay(element);
}
});
@ -95,7 +88,7 @@ DrilldownOverlays.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas
// helpers
var getParentChain = function(child) {
function getParentChain(child) {
var bo = getBusinessObject(child);
var parents = [];
@ -107,4 +100,13 @@ var getParentChain = function(child) {
}
return parents.reverse();
};
}
function planeId(element) {
if (is(element, 'bpmn:SubProcess')) {
return element.id + '_plane';
}
return element.id;
}

View File

@ -29,7 +29,8 @@ UpdateCanvasRootHandler.prototype.execute = function(context) {
diPlane = getDi(oldRoot);
// (1) replace process old <> new root
canvas.setRootElement(newRoot, true);
canvas.setRootElement(newRoot);
canvas.removeRootElement(oldRoot);
// (2) update root elements
collectionAdd(bpmnDefinitions.rootElements, newRootBusinessObject);
@ -63,7 +64,8 @@ UpdateCanvasRootHandler.prototype.revert = function(context) {
diPlane = getDi(newRoot);
// (1) replace process old <> new root
canvas.setRootElement(oldRoot, true);
canvas.setRootElement(oldRoot);
canvas.removeRootElement(newRoot);
// (2) update root elements
collectionRemove(bpmnDefinitions.rootElements, newRootBusinessObject);

View File

@ -113,7 +113,7 @@ BpmnImporter.prototype.add = function(semantic, di, parentElement) {
element.id = element.id + '_plane';
}
this._canvas.createPlane(semantic.id, element);
this._canvas.addRootElement(element);
}
// SHAPE
@ -149,7 +149,7 @@ BpmnImporter.prototype.add = function(semantic, di, parentElement) {
// check whether data store is inside our outside of its semantic parent
if (!isPointInsideBBox(parentElement, getMid(bounds))) {
parentElement = this._canvas.findPlane(parentElement).rootElement;
parentElement = this._canvas.findRoot(parentElement);
}
}
@ -177,7 +177,7 @@ BpmnImporter.prototype.add = function(semantic, di, parentElement) {
// are rendered correctly across different "hacks" people
// love to model such as cross participant / sub process
// associations
parentElement = this._canvas.findPlane(parentElement).rootElement;
parentElement = this._canvas.findRoot(parentElement);
}
// insert sequence flows behind other flow nodes (cf. #727)

View File

@ -76,7 +76,12 @@ export function importBpmnDiagram(diagram, definitions, bpmnDiagram) {
}
var mainDiagram = bpmnDiagram || definitions.diagrams[0];
canvas.setActivePlane(mainDiagram.plane.bpmnElement.id);
canvas.setRootElement(
canvas.findRoot(
mainDiagram.plane.bpmnElement.id
)
);
}
return new Promise(function(resolve, reject) {

View File

@ -4,9 +4,10 @@ import {
import coreModule from 'lib/core';
import DrilldownModule from 'lib/features/drilldown';
import { bootstrapViewer } from '../../../helper';
import { bootstrapViewer, getBpmnJS } from '../../../helper';
import { classes } from 'min-dom';
describe('features - drilldown', function() {
var testModules = [
@ -53,8 +54,9 @@ describe('features - drilldown', function() {
overlay.html.click();
// then
var plane = canvas.getActivePlane();
expect(plane.name).to.eql('collapsedProcess');
var collapsedRoot = canvas.getRootElement();
expect(collapsedRoot.businessObject).to.equal(collapsedProcess.businessObject);
}));
});
@ -78,7 +80,7 @@ describe('features - drilldown', function() {
var container = canvas.getContainer();
// when
canvas.setActivePlane('collapsedProcess');
canvas.setRootElement(canvas.findRoot('collapsedProcess_plane'));
// then
expect(classes(container).contains('bjs-breadcrumbs-shown')).to.be.true;
@ -87,42 +89,48 @@ describe('features - drilldown', function() {
it('should show execution tree', inject(function(canvas) {
// given
var breadcrumbs = canvas.getContainer().querySelector('.bjs-breadcrumbs');
// when
canvas.setActivePlane('collapsedProcess_2');
canvas.setRootElement(canvas.findRoot('collapsedProcess_2_plane'));
// then
expectBreadcrumbs(breadcrumbs, ['Root', 'Collapsed Process', 'Expanded Process', 'Collapsed Process 2']);
expectBreadcrumbs([
'Root',
'Collapsed Process',
'Expanded Process',
'Collapsed Process 2'
]);
}));
it('should switch to process plane on click', inject(function(canvas) {
// given
var breadcrumbs = canvas.getContainer().querySelector('.bjs-breadcrumbs');
canvas.setActivePlane('collapsedProcess_2');
canvas.setRootElement(canvas.findRoot('collapsedProcess_2_plane'));
// when
breadcrumbs.children[1].click();
clickBreadcrumb(1);
// then
expectBreadcrumbs(breadcrumbs, ['Root', 'Collapsed Process']);
expectBreadcrumbs([
'Root',
'Collapsed Process'
]);
}));
it('should switch to containing process plane on embedded click', inject(function(canvas) {
// given
var breadcrumbs = canvas.getContainer().querySelector('.bjs-breadcrumbs');
canvas.setActivePlane('collapsedProcess_2');
canvas.setRootElement(canvas.findRoot('collapsedProcess_2_plane'));
// when
breadcrumbs.children[2].click();
clickBreadcrumb(2);
// then
expectBreadcrumbs(breadcrumbs, ['Root', 'Collapsed Process']);
expectBreadcrumbs([
'Root',
'Collapsed Process'
]);
}));
});
@ -137,7 +145,7 @@ describe('features - drilldown', function() {
canvas.zoom(0.5);
// when
canvas.setActivePlane('collapsedProcess');
canvas.setRootElement(canvas.findRoot('collapsedProcess_plane'));
// then
var viewbox = canvas.viewbox();
@ -155,8 +163,8 @@ describe('features - drilldown', function() {
var zoomedAndScrolledViewbox = canvas.viewbox();
// when
canvas.setActivePlane('collapsedProcess');
canvas.setActivePlane('rootProcess');
canvas.setRootElement(canvas.findRoot('collapsedProcess_plane'));
canvas.setRootElement(canvas.findRoot('rootProcess'));
// then
var newViewbox = canvas.viewbox();
@ -175,8 +183,8 @@ describe('features - drilldown', function() {
it('should import collapsed subprocess', inject(function(canvas) {
// when
var inlineProcess1 = canvas.getPlane('inlineSubprocess');
var inlineProcess2 = canvas.getPlane('inlineSubprocess_2');
var inlineProcess1 = canvas.findRoot('inlineSubprocess_plane');
var inlineProcess2 = canvas.findRoot('inlineSubprocess_2_plane');
// then
expect(inlineProcess1).to.exist;
@ -202,10 +210,22 @@ describe('features - drilldown', function() {
// helpers
function expectBreadcrumbs(breadcrumbs, expected) {
function getBreadcrumbs() {
return getBpmnJS().invoke(function(canvas) {
return canvas.getContainer().querySelector('.bjs-breadcrumbs');
});
}
function expectBreadcrumbs(expected) {
var breadcrumbs = getBreadcrumbs();
var crumbs = Array.from(breadcrumbs.children).map(function(element) {
return element.innerText;
});
expect(crumbs).to.eql(expected);
}
function clickBreadcrumb(index) {
getBreadcrumbs().children[index].click();
}

View File

@ -742,13 +742,13 @@ describe('import - Importer', function() {
taskA = elementRegistry.get('Task_A'),
taskB = elementRegistry.get('Task_B');
var activePlane = canvas.getActivePlane(),
planeA = canvas.findPlane(taskA),
planeB = canvas.findPlane(taskB);
var activeRoot = canvas.getRootElement(),
rootA = canvas.findRoot(taskA),
rootB = canvas.findRoot(taskB);
// then
expect(activePlane).to.eql(planeA);
expect(planeA).to.not.eql(planeB);
expect(activeRoot).to.equal(rootA);
expect(rootA).not.to.equal(rootB);
});
});

View File

@ -15,74 +15,74 @@ describe('import - collapsed container', function() {
it('should import collapsed subProcess', inject(function(elementRegistry, canvas) {
var collapsedShape = elementRegistry.get('SubProcess_1');
var collapsedPlane = canvas.findPlane(collapsedShape);
var subProcessRoot = canvas.findRoot(collapsedShape);
var childShape = elementRegistry.get('IntermediateCatchEvent_1');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(collapsedShape.collapsed).to.be.true;
expect(collapsedPlane).to.not.eql(childPlane);
expect(subProcessRoot).not.to.equal(childRoot);
}));
it('should import collapsed transaction', inject(function(elementRegistry, canvas) {
var collapsedShape = elementRegistry.get('Transaction_1');
var collapsedPlane = canvas.findPlane(collapsedShape);
var subProcessRoot = canvas.findRoot(collapsedShape);
var childShape = elementRegistry.get('UserTask_1');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(collapsedShape.collapsed).to.be.true;
expect(collapsedPlane).to.not.eql(childPlane);
expect(subProcessRoot).not.to.eql(childRoot);
}));
it('should import collapsed adhocSubProcess', inject(function(elementRegistry, canvas) {
var collapsedShape = elementRegistry.get('AdHocSubProcess_1');
var collapsedPlane = canvas.findPlane(collapsedShape);
var subProcessRoot = canvas.findRoot(collapsedShape);
var childShape = elementRegistry.get('StartEvent_1');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(collapsedShape.collapsed).to.be.true;
expect(collapsedPlane).to.not.eql(childPlane);
expect(subProcessRoot).not.to.eql(childRoot);
}));
it('should import collapsed with nested elements', inject(function(elementRegistry, canvas) {
var collapsedShape = elementRegistry.get('SubProcess_4');
var collapsedPlane = canvas.findPlane(collapsedShape);
var subProcessRoot = canvas.findRoot(collapsedShape);
var childShape = elementRegistry.get('SubProcess_5');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
var nestedChildShape = elementRegistry.get('Task_3');
var nestedChildPlane = canvas.findPlane(nestedChildShape);
var nestedChildRoot = canvas.findRoot(nestedChildShape);
expect(collapsedShape.collapsed).to.be.true;
expect(childPlane).to.not.eql(collapsedPlane);
expect(nestedChildPlane).to.not.eql(collapsedPlane);
expect(childRoot).not.to.eql(subProcessRoot);
expect(nestedChildRoot).not.to.eql(subProcessRoot);
}));
it('should import collapsed with nested hidden labels', inject(function(elementRegistry, canvas) {
var collapsedShape = elementRegistry.get('SubProcess_2');
var collapsedPlane = canvas.findPlane(collapsedShape);
var subProcessRoot = canvas.findRoot(collapsedShape);
var hiddenEventShape = elementRegistry.get('StartEvent_2');
var hiddenEventPlane = canvas.findPlane(hiddenEventShape);
expect(hiddenEventPlane).to.not.eql(collapsedPlane);
var hiddenEventRoot = canvas.findRoot(hiddenEventShape);
expect(hiddenEventRoot).not.to.eql(subProcessRoot);
var hiddenDataShape = elementRegistry.get('DataObjectReference_1');
var hiddenDataPlane = canvas.findPlane(hiddenDataShape);
expect(hiddenDataPlane).to.not.eql(collapsedPlane);
var hiddenDataRoot = canvas.findRoot(hiddenDataShape);
expect(hiddenDataRoot).not.to.eql(subProcessRoot);
}));
it('should import expanded subProcess', inject(function(elementRegistry, canvas) {
var expandedShape = elementRegistry.get('SubProcess_3');
var expandedPlane = canvas.findPlane(expandedShape);
var expandedRoot = canvas.findRoot(expandedShape);
var childShape = elementRegistry.get('Task_2');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(expandedShape.collapsed).to.be.false;
expect(childShape.hidden).to.be.false;
expect(expandedPlane).to.eql(childPlane);
expect(expandedRoot).to.eql(childRoot);
}));
});
@ -97,47 +97,47 @@ describe('import - collapsed container', function() {
it('should import collapsed subProcess in pool', inject(function(elementRegistry, canvas) {
var collapsedShape = elementRegistry.get('SubProcess_1');
var collapsedPlane = canvas.findPlane(collapsedShape);
var subProcessRoot = canvas.findRoot(collapsedShape);
var childShape = elementRegistry.get('Task_1');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(collapsedShape.collapsed).to.be.true;
expect(collapsedPlane).to.not.eql(childPlane);
expect(subProcessRoot).not.to.eql(childRoot);
}));
it('should import expanded subProcess in pool', inject(function(elementRegistry, canvas) {
var expandedShape = elementRegistry.get('SubProcess_2');
var expandedPlane = canvas.findPlane(expandedShape);
var expandedRoot = canvas.findRoot(expandedShape);
var childShape = elementRegistry.get('StartEvent_1');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(expandedShape.collapsed).to.be.false;
expect(childShape.hidden).to.be.false;
expect(expandedPlane).to.eql(childPlane);
expect(expandedRoot).to.eql(childRoot);
}));
it('should import collapsed subProcess in lane', inject(function(elementRegistry, canvas) {
var expandedShape = elementRegistry.get('SubProcess_4');
var collapsedPlane = canvas.findPlane(expandedShape);
var subProcessRoot = canvas.findRoot(expandedShape);
var childShape = elementRegistry.get('Task_2');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(expandedShape.collapsed).to.be.true;
expect(collapsedPlane).to.not.eql(childPlane);
expect(subProcessRoot).not.to.eql(childRoot);
}));
it('should import expanded subProcess in lane', inject(function(elementRegistry, canvas) {
var expandedShape = elementRegistry.get('SubProcess_3');
var expandedPlane = canvas.findPlane(expandedShape);
var expandedRoot = canvas.findRoot(expandedShape);
var childShape = elementRegistry.get('StartEvent_2');
var childPlane = canvas.findPlane(childShape);
var childRoot = canvas.findRoot(childShape);
expect(expandedShape.collapsed).to.be.false;
expect(childShape.hidden).to.be.false;
expect(expandedPlane).to.eql(childPlane);
expect(expandedRoot).to.eql(childRoot);
}));
});