-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Overlays to navigate collapsed subprocesses
This introduces a new css file to bpmn-js. To upgrade, please include `dist/assets/bpmn-js.css` in your application. closes #1483
- Loading branch information
Showing
13 changed files
with
1,068 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
.bpmnjs-breadcrumbs { | ||
position: absolute; | ||
top: 10px; | ||
left: 100px; | ||
font-family: Arial, sans-serif; | ||
} | ||
|
||
.bpmnjs-breadcrumbs li { | ||
display: inline-block; | ||
color: var(--blue-base-65); | ||
cursor: pointer; | ||
} | ||
|
||
.bpmnjs-breadcrumbs li:last-of-type { | ||
color: inherit; | ||
cursor: default; | ||
} | ||
|
||
.bpmnjs-breadcrumbs li:not(:first-child)::before { | ||
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" /><path d="M0 0h24v24H0z" fill="none" /></svg>'); | ||
padding: 0 8px; | ||
color: black; | ||
} | ||
|
||
.bpmnjs-breadcrumbs .bpmnjs-crumb { | ||
display: inline-block; | ||
max-width: 200px; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
} | ||
|
||
.bpmnjs-drilldown { | ||
width: 20px; | ||
height: 20px; | ||
|
||
padding: 0px; | ||
margin-left: -20px; | ||
|
||
cursor: pointer; | ||
border: none; | ||
border-radius: 2px; | ||
outline: none; | ||
|
||
fill: white; | ||
background-color: var(--blue-base-65); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
export default function CollapsedProcessesNavigation(eventBus, canvas) { | ||
var currentPlane = 'base'; | ||
var positionMap = {}; | ||
|
||
eventBus.on('plane.set', function(event) { | ||
|
||
var currentViewbox = canvas.viewbox(); | ||
positionMap[currentPlane] = { | ||
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 dx = (currentViewbox.x - storedViewbox.x) * currentViewbox.scale, | ||
dy = (currentViewbox.y - storedViewbox.y) * currentViewbox.scale; | ||
|
||
if (dx !== 0 || dy !== 0) { | ||
canvas.scroll({ | ||
dx: dx, | ||
dy: dy | ||
}); | ||
} | ||
|
||
if (storedViewbox.zoom !== currentViewbox.scale) { | ||
canvas.zoom(storedViewbox.zoom, { x: 0, y: 0 }); | ||
} | ||
|
||
currentPlane = planeId; | ||
}); | ||
} | ||
|
||
CollapsedProcessesNavigation.$inject = [ 'eventBus', 'canvas' ]; |
135 changes: 135 additions & 0 deletions
135
lib/features/subprocess-navigation/SubprocessCompatibility.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
|
||
import { selfAndAllChildren } from 'diagram-js/lib/util/Elements'; | ||
import { isExpanded } from '../../util/DiUtil'; | ||
import { getBusinessObject, getDi, is } from '../../util/ModelUtil'; | ||
|
||
export default function CollapsedProcessesCompatibility(eventBus, elementRegistry, canvas, moddle, elementFactory, bpmnjs) { | ||
this._eventBus = eventBus; | ||
this._elementRegistry = elementRegistry; | ||
this._canvas = canvas; | ||
this._bpmnjs = bpmnjs; | ||
this._moddle = moddle; | ||
this._elementFactory = elementFactory; | ||
|
||
var self = this; | ||
|
||
eventBus.on('import.done', 1500, function() { | ||
self._handleImport(); | ||
}); | ||
} | ||
|
||
CollapsedProcessesCompatibility.prototype._handleImport = function() { | ||
var elementRegistry = this._elementRegistry; | ||
var canvas = this._canvas; | ||
var elementFactory = this._elementFactory; | ||
var self = this; | ||
var legacyProcesses = elementRegistry.filter(function(element) { | ||
return is(element, 'bpmn:SubProcess') && !isExpanded(element) && element.children && element.children.length; | ||
}); | ||
|
||
legacyProcesses.forEach(function(oldParent) { | ||
var bo = getBusinessObject(oldParent); | ||
|
||
var newDiagram = self.createDiagram(bo); | ||
|
||
var newParent = elementFactory.createRoot( | ||
{ | ||
id: bo.id, | ||
type: bo.$type, | ||
businessObject: bo, | ||
di: newDiagram.plane | ||
} | ||
); | ||
|
||
newParent.id = newParent.id + '_plane'; | ||
|
||
canvas.createPlane(bo.id, newParent); | ||
|
||
var elementsToChange = selfAndAllChildren(oldParent).filter(function(el) { | ||
return el !== oldParent; | ||
}); | ||
|
||
self.moveElementsToRoot(elementsToChange); | ||
|
||
elementsToChange.forEach(function(el) { | ||
if (el.parent === oldParent) { | ||
el.parent = newParent; | ||
} | ||
el.hidden = el.parent && (el.parent.hidden || el.parent.collapsed); | ||
|
||
self.moveToDiPlane(el, newDiagram.plane); | ||
|
||
self._eventBus.fire('elements.changed', { elements: [el] }); | ||
}); | ||
}); | ||
|
||
}; | ||
|
||
CollapsedProcessesCompatibility.prototype.moveToDiPlane = function(element, newPlane) { | ||
var di = getDi(element); | ||
var containingDiagram = findRootDiagram(di); | ||
|
||
// Remove DI from old Plane and add it to the new one | ||
var parentPlaneElement = containingDiagram.plane.get('planeElement'); | ||
parentPlaneElement.splice(parentPlaneElement.indexOf(di), 1); | ||
newPlane.get('planeElement').push(di); | ||
}; | ||
|
||
CollapsedProcessesCompatibility.prototype.moveElementsToRoot = function(elements) { | ||
var defaultPosition = { x: 180, y: 160 }, | ||
minX = Infinity, | ||
minY = Infinity; | ||
|
||
elements.forEach(function(el) { | ||
minX = Math.min(minX, el.x || Infinity); | ||
minY = Math.min(minY, el.y || Infinity); | ||
}); | ||
|
||
var xOffset = defaultPosition.x - minX; | ||
var yOffset = defaultPosition.y - minY; | ||
|
||
elements.forEach(function(el) { | ||
if (el.waypoints) { | ||
el.waypoints.forEach(function(waypoint) { | ||
waypoint.x = waypoint.x + xOffset; | ||
waypoint.y = waypoint.y + yOffset; | ||
}); | ||
} else { | ||
el.x = el.x + xOffset; | ||
el.y = el.y + yOffset; | ||
} | ||
}); | ||
}; | ||
|
||
CollapsedProcessesCompatibility.prototype.getDefinitions = function() { | ||
return this._bpmnjs._definitions || []; | ||
}; | ||
|
||
CollapsedProcessesCompatibility.prototype.getDiagrams = function() { | ||
return this.getDefinitions().diagrams || []; | ||
}; | ||
|
||
CollapsedProcessesCompatibility.prototype.createDiagram = function(bo) { | ||
var plane = this._moddle.create('bpmndi:BPMNPlane', { bpmnElement: bo }); | ||
var diagram = this._moddle.create('bpmndi:BPMNDiagram', { | ||
plane: plane | ||
}); | ||
plane.$parent = diagram; | ||
plane.bpmnElement = bo; | ||
diagram.$parent = this.getDefinitions(); | ||
this.getDiagrams().push(diagram); | ||
return diagram; | ||
}; | ||
|
||
CollapsedProcessesCompatibility.$inject = [ 'eventBus', 'elementRegistry', 'canvas', 'moddle', 'elementFactory', 'bpmnjs' ]; | ||
|
||
|
||
// Util | ||
|
||
var findRootDiagram = function(element) { | ||
if (is(element, 'bpmndi:BPMNDiagram')) { | ||
return element; | ||
} else { | ||
return findRootDiagram(element.$parent); | ||
} | ||
}; |
100 changes: 100 additions & 0 deletions
100
lib/features/subprocess-navigation/SubprocessOverlays.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { domify } 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>'; | ||
|
||
export default function CollapsedSubprocessOverlays(eventBus, elementRegistry, overlays, canvas) { | ||
var breadcrumbs = domify('<ul class="bpmnjs-breadcrumbs djs-element-hidden"></ul>'); | ||
var container = canvas.getContainer(); | ||
container.appendChild(breadcrumbs); | ||
|
||
function updateBreadcrumbs(plane) { | ||
var subProcess = elementRegistry.get(plane.name); | ||
var parents = getParentChain(subProcess); | ||
|
||
var path = parents.map(function(el) { | ||
var title = escapeHTML(el.name) || el.id; | ||
var link = domify('<li><span class="bpmnjs-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); | ||
} | ||
}); | ||
|
||
return link; | ||
}); | ||
|
||
breadcrumbs.innerHTML = ''; | ||
|
||
if (path.length > 1) { | ||
breadcrumbs.classList.remove('djs-element-hidden'); | ||
} else { | ||
breadcrumbs.classList.add('djs-element-hidden'); | ||
} | ||
|
||
path.forEach(function(el) { | ||
breadcrumbs.appendChild(el); | ||
}); | ||
} | ||
|
||
eventBus.on('plane.set', function(event) { | ||
var plane = event.plane; | ||
|
||
updateBreadcrumbs(plane); | ||
}); | ||
|
||
var createOverlay = function(element) { | ||
var html = domify('<button class="bpmnjs-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'); | ||
})); | ||
}); | ||
} | ||
|
||
CollapsedSubprocessOverlays.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ]; | ||
|
||
|
||
var getParentChain = function(child) { | ||
var bo = getBusinessObject(child); | ||
|
||
var parents = []; | ||
|
||
for (var element = bo; element; element = element.$parent) { | ||
if (is(element, 'bpmn:SubProcess') || is(element, 'bpmn:Process')) { | ||
parents.push(element); | ||
} | ||
} | ||
|
||
return parents.reverse(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import OverlaysModule from 'diagram-js/lib/features/overlays'; | ||
import ChangeSupportModule from 'diagram-js/lib/features/change-support'; | ||
|
||
import SubprocessCentering from './SubprocessCentering'; | ||
import SubprocessCompatibility from './SubprocessCompatibility'; | ||
import SubprocessOverlays from './SubprocessOverlays'; | ||
|
||
export default { | ||
__depends__: [ OverlaysModule, ChangeSupportModule ], | ||
__init__: [ 'subprocessOverlays', 'subprocessCompatibility', 'subprocessCentering' ], | ||
subprocessOverlays: [ 'type', SubprocessOverlays ], | ||
subprocessCompatibility: [ 'type', SubprocessCompatibility ], | ||
subprocessCentering: [ 'type', SubprocessCentering ] | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.