bpmn-js
Version:
A bpmn 2.0 toolkit and web modeler
163 lines (125 loc) • 4.56 kB
JavaScript
import { domify, classes } from 'min-dom';
import { find } from 'min-dash';
import { escapeHTML } from 'diagram-js/lib/util/EscapeUtil';
import { getBusinessObject, is } from '../../util/ModelUtil';
import {
getPlaneIdFromShape
} from '../../util/DrilldownUtil';
/**
* @typedef {import('diagram-js/lib/core/Canvas').default} Canvas
* @typedef {import('diagram-js/lib/core/ElementRegistry').default} ElementRegistry
* @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
*
* @typedef {import('../../model/Types').Element} Element
* @typedef {import('../../model/Types').Shape} Shape
*/
var OPEN_CLASS = 'bjs-breadcrumbs-shown';
/**
* Adds overlays that allow switching planes on collapsed subprocesses.
*
* @param {EventBus} eventBus
* @param {ElementRegistry} elementRegistry
* @param {Canvas} canvas
*/
export default function DrilldownBreadcrumbs(eventBus, elementRegistry, canvas) {
var breadcrumbs = domify('<ul class="bjs-breadcrumbs"></ul>');
var container = canvas.getContainer();
var containerClasses = classes(container);
container.appendChild(breadcrumbs);
var businessObjectParents = [];
// update breadcrumbs if name or ID of the primary shape changes
eventBus.on('element.changed', function(event) {
var shape = event.element,
businessObject = getBusinessObject(shape);
var isPresent = find(businessObjectParents, function(element) {
return element === businessObject;
});
if (!isPresent) {
return;
}
updateBreadcrumbs();
});
/**
* Updates the displayed breadcrumbs. If no element is provided, only the
* labels are updated.
*
* @param {Element} [element]
*/
function updateBreadcrumbs(element) {
if (element) {
businessObjectParents = getBusinessObjectParentChain(element);
}
var path = businessObjectParents.flatMap(function(parent) {
var parentPlane =
canvas.findRoot(getPlaneIdFromShape(parent)) ||
canvas.findRoot(parent.id);
// when the root is a collaboration, the process does not have a
// corresponding element in the elementRegisty. Instead, we search
// for the corresponding participant
if (!parentPlane && is(parent, 'bpmn:Process')) {
var participant = elementRegistry.find(function(element) {
var businessObject = getBusinessObject(element);
return businessObject && businessObject.get('processRef') === parent;
});
parentPlane = participant && canvas.findRoot(participant.id);
}
if (!parentPlane) {
return [];
}
return [ { parent: parent, parentPlane: parentPlane } ];
});
breadcrumbs.innerHTML = '';
// show breadcrumbs and expose state to .djs-container
var visible = path.length > 1;
containerClasses.toggle(OPEN_CLASS, visible);
path.forEach(function(entry, index) {
var parent = entry.parent,
parentPlane = entry.parentPlane,
title = escapeHTML(parent.name || parent.id);
// the last crumb represents the current plane; render it as
// plain, non-interactive text
var isCurrent = index === path.length - 1;
var crumb;
if (isCurrent) {
crumb = domify(
'<li><span class="bjs-crumb" title="' + title + '">' + title + '</span></li>'
);
} else {
// render navigable crumbs as local links so they are focusable
// and keyboard-operable
var href = '#' + escapeHTML(parent.id);
crumb = domify(
'<li><a class="bjs-crumb" href="' + href + '" title="' + title + '">' + title + '</a></li>'
);
crumb.addEventListener('click', function(event) {
event.preventDefault();
canvas.setRootElement(parentPlane);
});
}
breadcrumbs.appendChild(crumb);
});
}
eventBus.on('root.set', function(event) {
updateBreadcrumbs(event.element);
});
}
DrilldownBreadcrumbs.$inject = [ 'eventBus', 'elementRegistry', 'canvas' ];
// helpers //////////
/**
* Returns the parents for the element using the business object chain,
* starting with the root element.
*
* @param {Shape} child
*
* @return {Shape}
*/
function getBusinessObjectParentChain(child) {
var businessObject = getBusinessObject(child);
var parents = [];
for (var element = businessObject; element; element = element.$parent) {
if (is(element, 'bpmn:SubProcess') || is(element, 'bpmn:Process')) {
parents.push(element);
}
}
return parents.reverse();
}