Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - feat(modeling): create new planes when collapsed subprocesses are created #1538

Closed
wants to merge 9 commits into from
8 changes: 8 additions & 0 deletions assets/bpmn-js.css
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,11 @@
fill: var(--drilldown-fill-color);
background-color: var(--drilldown-background-color);
}

.bjs-drilldown-empty {
display: none;
}

.selected .bjs-drilldown-empty {
display: inherit;
}
6 changes: 6 additions & 0 deletions lib/features/auto-resize/BpmnAutoResizeProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ BpmnAutoResizeProvider.$inject = [
*/
BpmnAutoResizeProvider.prototype.canResize = function(elements, target) {

// do not resize plane elements:
// root elements, collapsed sub-processes
if (is(target.di, 'bpmndi:BPMNPlane')) {
return false;
}

if (!is(target, 'bpmn:Participant') && !is(target, 'bpmn:Lane') && !(is(target, 'bpmn:SubProcess'))) {
return false;
}
Expand Down
32 changes: 19 additions & 13 deletions lib/features/di-ordering/BpmnDiOrdering.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getDi } from '../../util/ModelUtil';

import {
filter,
forEach,
map
} from 'min-dash';

Expand All @@ -10,27 +11,32 @@ import { selfAndAllChildren } from 'diagram-js/lib/util/Elements';

var HIGH_PRIORITY = 2000;

export default function BpmnDiOrdering(eventBus, canvas) {
export default function BpmnDiOrdering(eventBus, elementRegistry) {

eventBus.on('saveXML.start', HIGH_PRIORITY, orderDi);

function orderDi() {
var root = canvas.getRootElement(),
rootDi = getDi(root),
elements,
diElements;
var rootElements = elementRegistry.filter(function(element) {
return !element.parent;
});

elements = selfAndAllChildren([ root ], false);
forEach(rootElements, function(root) {
var rootDi = getDi(root),
elements,
diElements;

// only bpmndi:Shape and bpmndi:Edge can be direct children of bpmndi:Plane
elements = filter(elements, function(element) {
return element !== root && !element.labelTarget;
});
elements = selfAndAllChildren([ root ], false);

diElements = map(elements, getDi);
// only bpmndi:Shape and bpmndi:Edge can be direct children of bpmndi:Plane
elements = filter(elements, function(element) {
return element !== root && !element.labelTarget;
});

rootDi.set('planeElement', diElements);
diElements = map(elements, getDi);

rootDi.set('planeElement', diElements);
});
}
}

BpmnDiOrdering.$inject = [ 'eventBus', 'canvas' ];
BpmnDiOrdering.$inject = [ 'eventBus', 'elementRegistry' ];
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { domify, classes } from 'min-dom';
import { escapeHTML } from 'diagram-js/lib/util/EscapeUtil';
import { getBusinessObject, is } from '../../util/ModelUtil';

var ARROW_DOWN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4.81801948,3.50735931 L10.4996894,9.1896894 L10.5,4 L12,4 L12,12 L4,12 L4,10.5 L9.6896894,10.4996894 L3.75735931,4.56801948 C3.46446609,4.27512627 3.46446609,3.80025253 3.75735931,3.50735931 C4.05025253,3.21446609 4.52512627,3.21446609 4.81801948,3.50735931 Z"/></svg>';

var OPEN_CLASS = 'bjs-breadcrumbs-shown';

/**
Expand All @@ -15,7 +13,7 @@ var OPEN_CLASS = 'bjs-breadcrumbs-shown';
* @param {overlays} overlays
* @param {canvas} canvas
*/
export default function DrilldownOverlays(eventBus, elementRegistry, overlays, canvas) {
export default function DrilldownBreadcrumbs(eventBus, elementRegistry, overlays, canvas) {
var breadcrumbs = domify('<ul class="bjs-breadcrumbs"></ul>');
var container = canvas.getContainer();
var containerClasses = classes(container);
Expand Down Expand Up @@ -58,40 +56,9 @@ export default function DrilldownOverlays(eventBus, elementRegistry, overlays, c
updateBreadcrumbs(plane);
});

var createOverlay = function(element) {
var html = domify('<button class="bjs-drilldown">' + ARROW_DOWN_SVG + '</button>');

html.addEventListener('click', function() {
canvas.setActivePlane(element.id);
});

overlays.add(element, {
position: {
bottom: -7,
right: -8
},
html: html
});
};

var addOverlays = function(elements) {
elements.forEach(function(element) {
if (is(element, 'bpmn:SubProcess')
&& element.collapsed
&& canvas.getPlane(element.id)) {
createOverlay(element);
}
});
};

eventBus.on('import.done', function() {
addOverlays(elementRegistry.filter(function(el) {
return is(el, 'bpmn:SubProcess');
}));
});
}

DrilldownOverlays.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ];
DrilldownBreadcrumbs.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ];


// helpers
Expand Down
132 changes: 132 additions & 0 deletions lib/features/drilldown/DrilldownOverlayBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import inherits from 'inherits';

import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import { is } from '../../util/ModelUtil';
import { classes, domify } from 'min-dom';

var LOW_PRIORITY = 250;
var ARROW_DOWN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4.81801948,3.50735931 L10.4996894,9.1896894 L10.5,4 L12,4 L12,12 L4,12 L4,10.5 L9.6896894,10.4996894 L3.75735931,4.56801948 C3.46446609,4.27512627 3.46446609,3.80025253 3.75735931,3.50735931 C4.05025253,3.21446609 4.52512627,3.21446609 4.81801948,3.50735931 Z"/></svg>';

var EMPTY_MARKER = 'bjs-drilldown-empty';

export default function DrilldownOverlayBehavior(
canvas, eventBus, elementRegistry, overlays
) {
CommandInterceptor.call(this, eventBus);

this._canvas = canvas;
this._eventBus = eventBus;
this._elementRegistry = elementRegistry;
this._overlays = overlays;

var self = this;

this.executed('shape.create', LOW_PRIORITY, function(context) {
var shape = context.shape;

// Add overlay to the collapsed shape
if (self.canDrillDown(shape)) {
self.addOverlay(shape);
}

// with the first element added, we remove the overlay marker
// to make the drilldown button permanent
updateDrilldownOverlay(shape);
}, true);

this.reverted('shape.create', LOW_PRIORITY, function(context) {
updateDrilldownOverlay(context.parent);
}, true);

this.executed('shape.delete', LOW_PRIORITY, function(context) {
updateDrilldownOverlay(context.oldParent);
}, true);

this.reverted('shape.delete', LOW_PRIORITY, function(context) {
updateDrilldownOverlay(context.oldParent);
}, true);

// TODO(marstamm): remove overlays when expanding an element


eventBus.on('import.done', function() {
elementRegistry.filter(function(e) {
return self.canDrillDown(e);
}).map(function(el) {
self.addOverlay(el);
});
});

function updateDrilldownOverlay(shape) {
if (!shape) {
return;
}

var parentPlane = canvas.findPlane(shape);
if (parentPlane) {
self.updateOverlayVisibility(parentPlane.rootElement);
}
}
}

inherits(DrilldownOverlayBehavior, CommandInterceptor);

DrilldownOverlayBehavior.prototype.canDrillDown = function(element) {
return is(element, 'bpmn:SubProcess') && this._canvas.getPlane(element.id);
};

/**
* Updates visibility of the drilldown overlay. If the plane has no elements,
* the drilldown will be only shown when the element is selected.
*
* @param {djs.model.Shape|djs.model.Root} element collapsed shape or root element
*/
DrilldownOverlayBehavior.prototype.updateOverlayVisibility = function(element) {
var overlays = this._overlays;

var bo = element.businessObject;

var overlay = overlays.get({ element: bo.id, type: 'drilldown' })[0];

if (!overlay) {
return;
}

var hasContent = bo && bo.flowElements && bo.flowElements.length;
classes(overlay.html).toggle(EMPTY_MARKER, !hasContent);
};

/**
* Attaches a drilldown button to the given element. We assume that the plane has
* the same id as the element.
*
* @param {djs.model.Shape} element collapsed shape
*/
DrilldownOverlayBehavior.prototype.addOverlay = function(element) {
var canvas = this._canvas;
var overlays = this._overlays;

var button = domify('<button class="bjs-drilldown">' + ARROW_DOWN_SVG + '</button>');

button.addEventListener('click', function() {
canvas.setActivePlane(element.id);
});

overlays.add(element, 'drilldown', {
position: {
bottom: -7,
right: -8
},
html: button
});

this.updateOverlayVisibility(element);
};


DrilldownOverlayBehavior.$inject = [
'canvas',
'eventBus',
'elementRegistry',
'overlays'
];
15 changes: 9 additions & 6 deletions lib/features/drilldown/SubprocessCompatibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ SubprocessCompatibility.prototype.createNewDiagrams = function(plane) {

var newDiagrams = [];

// create new planes for all collapsed subprocesses, even when they are empty
collapsedElements.forEach(function(element) {
if (!self._processToDiagramMap[element.id]) {
var diagram = self.createDiagram(element);
self._processToDiagramMap[element.id] = diagram;
newDiagrams.push(diagram);
}
});

elementsToMove.forEach(function(element) {
var diElement = element.diElement;
var parent = element.parent;
Expand All @@ -98,12 +107,6 @@ SubprocessCompatibility.prototype.createNewDiagrams = function(plane) {
}

var diagram = self._processToDiagramMap[parent.id];
if (!diagram) {
diagram = self.createDiagram(parent);
self._processToDiagramMap[parent.id] = diagram;
newDiagrams.push(diagram);
}

self.moveToDiPlane(diElement, diagram.plane);
});

Expand Down
11 changes: 7 additions & 4 deletions lib/features/drilldown/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import OverlaysModule from 'diagram-js/lib/features/overlays';
import ChangeSupportModule from 'diagram-js/lib/features/change-support';
import PlanesModule from 'diagram-js/lib/features/planes';

import DrilldownOverlays from './DrilldownOverlays';
import DrilldownBreadcrumbs from './DrilldownBreadcrumbs';
import DrilldownCentering from './DrilldownCentering';
import SubprocessCompatibility from './SubprocessCompatibility';
import DrilldownOverlayBehavior from './DrilldownOverlayBehavior';

export default {
__depends__: [ OverlaysModule, ChangeSupportModule ],
__init__: [ 'drilldownOverlays', 'drilldownCentering', 'subprocessCompatibility'],
drilldownOverlays: [ 'type', DrilldownOverlays ],
__depends__: [ OverlaysModule, ChangeSupportModule, PlanesModule ],
__init__: [ 'drilldownBreadcrumbs', 'drilldownOverlayBehavior', 'drilldownCentering', 'subprocessCompatibility'],
drilldownBreadcrumbs: [ 'type', DrilldownBreadcrumbs ],
drilldownCentering: [ 'type', DrilldownCentering ],
drilldownOverlayBehavior: [ 'type', DrilldownOverlayBehavior ],
subprocessCompatibility: [ 'type', SubprocessCompatibility ]
};
Loading