Decrease pool size after lane deletion

Hi,

firstly, thank you for this great software. Also this is my first post, feel free to correct my mistakes :slight_smile:

I am trying to change the behaviour of removing lanes from a pool similar to this:
Before

after

Instead of increasing the size of other lanes i want them to stay the same and the size of the pool to decrease according to the removed lane.

To simplify this we dont have nested lanes/pools. Also all shapes inside a lane get removed by a custom CommandInterceptor, so there is no need to worry about them.
However our pools are at a fixed position. Because of that the deletion should never change the y position in the upper left corner of the pool. This is particularly important if the user deletes the highest pool.

So far i tried to inject the custom deleteLaneBehaviour and overwrite it. As you can see in the linked codesandbox i am unsure how to modify this file, therefor it is just the standard behaviour.

Another idea was to listen for elment.resize event and decrease the size of all lanes accordingly. But i am also unsure how to start with this approach.

I created a sandbox with the important parts, i hope this helps you to understand my problem.

Is there a better way of doing this? And if not can you help me understand how calculateAdjustments and makeSpace work?

Thanks in advance!

1 Like

I found a solution that is working for me so far. I havent testet it yet, but maybe it’s usefull for other people to implement their own custom behaviour.

First stop the normal behaviour,

{deleteLaneBehavior: ['value', null]}

Then implement your custom behaviour

import CommandInterceptor from "diagram-js/lib/command/CommandInterceptor.js";
import { getChildLanes } from "../helpers/helper";

const LOW_PRIOTITY = 100;

//Instead of increasing the size of other lanes decrease the pool size and move the rest of the lanes upwards
export default class DecreasePoolSizeAfterDelete extends CommandInterceptor {
    constructor(eventBus, modeling) {
        super(eventBus);

        this.postExecute("shape.delete", LOW_PRIOTITY,  context => {
        const deletedLane  = context.shape;
        if(deletedLane.type !== 'bpmn:Lane') return
            const parent = context.oldParent;
            const newPoolBounds = new Object(); 
            newPoolBounds.x = parent.x;
            newPoolBounds.y = parent.y;
            newPoolBounds.width = parent.width;
            newPoolBounds.height = parent.height - deletedLane.height;
            //Resize Pool accordingly
            modeling.resizeShape(parent, newPoolBounds);  
            //If a lane gets deleted move all lanes below this one up by the height of the lane
            let lanes = getChildLanes(parent);
            lanes = sortLanesBelow(lanes, deletedLane);
            //If there are lanes below the deleted one move them downwards!
            if(lanes !== undefined) {
                moveLanes(lanes, - deletedLane.height);
            }
        },
        true
        );
        //Move lanes
        function moveLanes(pools, yDifference) {
            pools.forEach(function(pool) {
                modeling.moveShape(pool, {x:0, y: yDifference});
            });
        }
        function sortLanesBelow(lanes, deletedLane) {
            lanes = lanes.filter(lane => {
                return lane.y > deletedLane.y
            });
            return lanes;
        }
    }
}

CommandInterceptor.$inject = ["eventBus", "modeling"];
import CommandInterceptor from "diagram-js/lib/command/CommandInterceptor.js";
import { getChildLanes } from "../helpers/helper";

//LOW -> Later
const LOW_PRIORITY = 100;
let allPools;

export default class FixPoolResize extends CommandInterceptor {
    constructor(eventBus, modeling, elementRegistry) {
      super(eventBus);

      this.postExecuted("shape.resize", LOW_PRIORITY,  context => {
        const pool  = context.shape;
        if(pool.type !== 'bpmn:Participant') return

        const newBounds = context.newBounds;
        const oldBounds = context.oldBounds; 
        //Get all pools below given
        let pools = sortPoolsBelow(pool);
        console.log("shape.resize pool", context);

        //If y position changed reposition it
        if(oldBounds.y !== newBounds.y) {
            const yDifference = oldBounds.y - newBounds.y;
            movePool(pool, 0, yDifference);
            //Returns all pools below given pool sorted by y: 0, 5, 120...  
            movePools(pools, yDifference);
        } else if(oldBounds.height !== newBounds.heigth) {
            const heightDifference = newBounds.height- oldBounds.height;
            movePools(pools, heightDifference);
        } 
        //Fix x positon if resized
        //X should always be at coordinate 100; otherwise move shape accordingly
          if(newBounds.x !== 100) {
                const xDifference = oldBounds.x- newBounds.x;
                movePool(pool, xDifference, 0);
                //resizeChildren(getChildLanes(pool), newBounds);
          }
          //If width changed try to resize all pools
          if(newBounds.width !== oldBounds.width) {
            resizePools(allPools, newBounds);
            //resizeChildren(getChildLanes(pool), newBounds);
          }
        },
        true
        );
        function sortPoolsBelow(pool)  {
            let elements = elementRegistry.getAll();
            elements = elements.filter(element => element.type === 'bpmn:Participant')
            allPools = elements;
            //Remove all pools below given pool
            elements = elements.filter(element => {
                return element.y > pool.y
            });
            return elements;
        }
        //Move pools
        function movePools(pools, yDifference) {
            pools.forEach(function(pool) {
                modeling.moveShape(pool, {x:0, y: yDifference});
            });
        }
        //Move one pool
        function movePool(pool, xDifference, yDifference) {
            modeling.moveShape(pool, {x: xDifference, y: yDifference});
        }
        //Resize all given pools, but keep their x and y value
        function resizePools(pools, newBounds) {
            pools.forEach(function(pool) {
                //only overwrite the width!
                newBounds.x = pool.x;
                newBounds.y = pool.y;
                newBounds.height = pool.height;
                modeling.resizeShape(pool, newBounds);
            });
        }
        //Resize all lanes inside this pool
        function resizeChildren(children, newBounds) {
            children.forEach(function(child) {
                //only overwrite the width!
                console.log("child", child);
                newBounds.x = child.x;
                newBounds.y = child.y;
                newBounds.width -= 30;
                modeling.resizeShape(child, newBounds);
            });
        }
    }
}

CommandInterceptor.$inject = ["eventBus", "modeling", "elementRegistry"];

Thanks for sharing the solution. I am glad you were able to solve the problem on your own.