From b8ed73b7f8e4c47d9b2bf10a565bd8fd7f2bc953 Mon Sep 17 00:00:00 2001 From: Martin Stamm Date: Wed, 15 Dec 2021 17:31:10 +0100 Subject: [PATCH] feat(modeling): support toggle collapse with drilldown closes #1539 --- .../behavior/SubProcessPlaneBehavior.js | 155 ++++++++- lib/features/ordering/BpmnOrderingProvider.js | 2 +- .../grid-snapping/BpmnGridSnappingSpec.js | 2 +- .../ToggleElementCollapseBehaviour.bpmn | 301 +++++++++--------- .../ToggleElementCollapseBehaviourSpec.js | 29 +- test/spec/features/replace/BpmnReplaceSpec.js | 2 +- 6 files changed, 305 insertions(+), 186 deletions(-) diff --git a/lib/features/modeling/behavior/SubProcessPlaneBehavior.js b/lib/features/modeling/behavior/SubProcessPlaneBehavior.js index 83756f1f..a610e060 100644 --- a/lib/features/modeling/behavior/SubProcessPlaneBehavior.js +++ b/lib/features/modeling/behavior/SubProcessPlaneBehavior.js @@ -10,6 +10,15 @@ import { getBBox } from 'diagram-js/lib/util/Elements'; import { asPlaneId, planeId } from '../../../util/DrilldownUtil'; +var LOW_PRIORITY = 400; +var HIGH_PRIORITY = 600; + +var DEFAULT_POSITION = { + x: 180, + y: 160 +}; + + /** * Creates diPlanes and canvas planes when collapsed subprocesses are created. * @@ -140,27 +149,149 @@ export default function SubProcessPlaneBehavior( elementRegistry.updateId(planeElement, asPlaneId(oldId)); }, true); + + + // create/remove plane for the subprocess + this.executed('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + if (!isExpanded(shape)) { + createRoot(context); + self._showRecursively(shape.children); + } else { + removeRoot(context); + } + + }, true); + + + // create/remove plane for the subprocess + this.reverted('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + if (!isExpanded(shape)) { + createRoot(context); + self._showRecursively(shape.children); + } else { + removeRoot(context); + } + + }, true); + + // move elements between planes + this.postExecuted('shape.toggleCollapse', HIGH_PRIORITY, function(context) { + var shape = context.shape; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + var rootElement = context.newRootElement; + + if (!rootElement) { + return; + } + + if (!isExpanded(shape)) { + + // collapsed + self._moveChildrenToShape(shape, rootElement); + + } else { + self._moveChildrenToShape(rootElement, shape); + } + }, true); + } inherits(SubProcessPlaneBehavior, CommandInterceptor); - /** -* Adds a given diagram to the definitions and returns a . -* -* @param {Object} planeElement -*/ -SubProcessPlaneBehavior.prototype._addDiagram = function(planeElement) { - var bpmnjs = this._bpmnjs; - var diagrams = bpmnjs.getDefinitions().diagrams; + * Moves the child elements from source to target. + * + * If the target is a plane, the children are moved to the top left corner. + * Otherwise, the center of the target is used. + * + * @param {Object|djs.model.Base} source + * @param {Object|djs.model.Base} target + */ +SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target) { + var modeling = this._modeling; - if (!planeElement.businessObject) { - planeElement = this._createNewDiagram(planeElement); + var children = source.children; + var offset; + + if (!children) { + return; } - diagrams.push(planeElement.di.$parent); + // Only change plane if there are no visible children, but don't move them + var visibleChildren = children.filter(function(child) { + return !child.hidden; + }); - return planeElement; + if (!visibleChildren.length) { + modeling.moveElements(children, { x: 0, y: 0 }, target, { autoResize: false }); + return; + } + + // target is a plane + if (!target.x) { + offset = { + x: DEFAULT_POSITION.x - source.x , + y: DEFAULT_POSITION.y - source.y + }; + } + + // source is a plane + else { + + // move relative to the center of the shape + var targetMid = getMid(target); + var childrenBounds = getBBox(visibleChildren); + var childrenMid = getMid(childrenBounds); + + offset = { + x: targetMid.x - childrenMid.x, + y: targetMid.y - childrenMid.y + }; + } + + modeling.moveElements(children, offset, target, { autoResize: false }); +}; + +/** + * Sets `hidden` property on all children of the given shape. + * + * @param {Array} elements + * @param {Boolean} [hidden] + * @returns {Array} all child elements + */ +SubProcessPlaneBehavior.prototype._showRecursively = function(elements, hidden) { + var self = this; + + var result = []; + elements.forEach(function(element) { + element.hidden = !!hidden; + + result = result.concat(element); + + if (element.children) { + result = result.concat( + self._showRecursively(element.children, element.collapsed || hidden) + ); + } + }); + + return result; }; /** diff --git a/lib/features/ordering/BpmnOrderingProvider.js b/lib/features/ordering/BpmnOrderingProvider.js index 175d4370..9b795e9b 100644 --- a/lib/features/ordering/BpmnOrderingProvider.js +++ b/lib/features/ordering/BpmnOrderingProvider.js @@ -131,7 +131,7 @@ export default function BpmnOrderingProvider(eventBus, canvas, translate) { // render labels always on top if (element.labelTarget) { return { - parent: canvas.getRootElement(), + parent: canvas.findRoot(element.labelTarget) || canvas.getRootElement(), index: -1 }; } diff --git a/test/spec/features/grid-snapping/BpmnGridSnappingSpec.js b/test/spec/features/grid-snapping/BpmnGridSnappingSpec.js index 2976c866..44cc54d6 100644 --- a/test/spec/features/grid-snapping/BpmnGridSnappingSpec.js +++ b/test/spec/features/grid-snapping/BpmnGridSnappingSpec.js @@ -343,7 +343,7 @@ describe('features/grid-snapping', function() { // then expect(expandedSubProcess).to.include({ - x: 140, + x: 150, y: 120, width: 360, height: 210 diff --git a/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviour.bpmn b/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviour.bpmn index 617db520..db4ad5ad 100644 --- a/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviour.bpmn +++ b/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviour.bpmn @@ -77,137 +77,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - + + + + + + + + + + @@ -219,55 +171,118 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviourSpec.js b/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviourSpec.js index 84ad4ce7..0455bb02 100644 --- a/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviourSpec.js +++ b/test/spec/features/modeling/behavior/ToggleElementCollapseBehaviourSpec.js @@ -229,7 +229,6 @@ describe('features/modeling - collapse and expand elements', function() { describe('undo', function() { - it('collapsed-marker is placed', inject(function(elementRegistry, bpmnReplace, commandStack) { @@ -339,32 +338,6 @@ describe('features/modeling - collapse and expand elements', function() { ); - it('hide all children', - inject(function(elementRegistry, bpmnReplace) { - - // given - var expandedSubProcess = elementRegistry.get('SubProcess_2'); - var originalChildren = expandedSubProcess.children.slice(); - - // when - var collapsedSubProcess = bpmnReplace.replaceElement(expandedSubProcess, - { - type: 'bpmn:SubProcess', - isExpanded: false - } - ); - - // then keep children - originalChildren.forEach(function(c) { - expect(collapsedSubProcess.children).to.include(c); - }); - - // and hide them - expect(collapsedSubProcess.children).to.satisfy(allHidden()); - }) - ); - - it('keep ad-hoc and multiInstance-marker', inject(function(elementRegistry, bpmnReplace) { @@ -640,7 +613,7 @@ function childrenHidden(hidden) { return child.hidden; } else { - return child.hidden == hidden; + return !!child.hidden == hidden; } }); }; diff --git a/test/spec/features/replace/BpmnReplaceSpec.js b/test/spec/features/replace/BpmnReplaceSpec.js index f26e9b1b..b9f7d345 100644 --- a/test/spec/features/replace/BpmnReplaceSpec.js +++ b/test/spec/features/replace/BpmnReplaceSpec.js @@ -324,7 +324,7 @@ describe('features/replace - bpmn replace', function() { beforeEach(inject(function(canvas) { - canvas.setActivePlane('SubProcess_Collapsed'); + canvas.setRootElement(canvas.findRoot('SubProcess_Collapsed_plane')); }));