Skip to content

Commit

Permalink
feat: handle resizing of vertical lanes
Browse files Browse the repository at this point in the history
Closes #2062

---------

Co-authored-by: Maciej Barelkowski <[email protected]>
  • Loading branch information
sombrek and barmac authored Jan 22, 2024
1 parent b9d0ce3 commit 0a706be
Show file tree
Hide file tree
Showing 12 changed files with 2,177 additions and 1,080 deletions.
472 changes: 262 additions & 210 deletions lib/features/modeling/behavior/ResizeBehavior.js

Large diffs are not rendered by default.

346 changes: 207 additions & 139 deletions lib/features/modeling/behavior/SpaceToolBehavior.js
Original file line number Diff line number Diff line change
@@ -1,139 +1,207 @@
import { forEach } from 'min-dash';

import { is } from '../../../util/ModelUtil';

import { isExpanded } from '../../../util/DiUtil';

import {
GROUP_MIN_DIMENSIONS,
LANE_MIN_DIMENSIONS,
PARTICIPANT_MIN_DIMENSIONS,
SUB_PROCESS_MIN_DIMENSIONS,
TEXT_ANNOTATION_MIN_DIMENSIONS
} from './ResizeBehavior';

import { getChildLanes } from '../util/LaneUtil';

/**
* @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
*
* @typedef {import('../../../model/Types').Shape} Shape
*/

var max = Math.max;

/**
* @param {EventBus} eventBus
*/
export default function SpaceToolBehavior(eventBus) {
eventBus.on('spaceTool.getMinDimensions', function(context) {
var shapes = context.shapes,
axis = context.axis,
start = context.start,
minDimensions = {};

forEach(shapes, function(shape) {
var id = shape.id;

if (is(shape, 'bpmn:Participant')) {

if (isHorizontal(axis)) {
minDimensions[ id ] = PARTICIPANT_MIN_DIMENSIONS;
} else {
minDimensions[ id ] = {
width: PARTICIPANT_MIN_DIMENSIONS.width,
height: getParticipantMinHeight(shape, start)
};
}

}

if (is(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
minDimensions[ id ] = SUB_PROCESS_MIN_DIMENSIONS;
}

if (is(shape, 'bpmn:TextAnnotation')) {
minDimensions[ id ] = TEXT_ANNOTATION_MIN_DIMENSIONS;
}

if (is(shape, 'bpmn:Group')) {
minDimensions[ id ] = GROUP_MIN_DIMENSIONS;
}
});

return minDimensions;
});
}

SpaceToolBehavior.$inject = [ 'eventBus' ];


// helpers //////////
function isHorizontal(axis) {
return axis === 'x';
}

/**
* Get minimum height for participant taking lanes into account.
*
* @param {Shape} participant
* @param {number} start
*
* @return {number}
*/
function getParticipantMinHeight(participant, start) {
var lanesMinHeight;

if (!hasChildLanes(participant)) {
return PARTICIPANT_MIN_DIMENSIONS.height;
}

lanesMinHeight = getLanesMinHeight(participant, start);

return max(PARTICIPANT_MIN_DIMENSIONS.height, lanesMinHeight);
}

function hasChildLanes(element) {
return !!getChildLanes(element).length;
}

function getLanesMinHeight(participant, resizeStart) {
var lanes = getChildLanes(participant),
resizedLane;

// find the nested lane which is currently resized
resizedLane = findResizedLane(lanes, resizeStart);

// resized lane cannot shrink below the minimum height
// but remaining lanes' dimensions are kept intact
return participant.height - resizedLane.height + LANE_MIN_DIMENSIONS.height;
}

/**
* Find nested lane which is currently resized.
*
* @param {Shape[]} lanes
* @param {number} resizeStart
*
* @return {Shape}
*/
function findResizedLane(lanes, resizeStart) {
var i, lane, childLanes;

for (i = 0; i < lanes.length; i++) {
lane = lanes[i];

// resizing current lane or a lane nested
if (resizeStart >= lane.y && resizeStart <= lane.y + lane.height) {
childLanes = getChildLanes(lane);

// a nested lane is resized
if (childLanes.length) {
return findResizedLane(childLanes, resizeStart);
}

// current lane is the resized one
return lane;
}
}
}
import { forEach } from 'min-dash';

import { is } from '../../../util/ModelUtil';

import {
isExpanded,
isHorizontal
} from '../../../util/DiUtil';

import {
GROUP_MIN_DIMENSIONS,
LANE_MIN_DIMENSIONS,
VERTICAL_LANE_MIN_DIMENSIONS,
PARTICIPANT_MIN_DIMENSIONS,
VERTICAL_PARTICIPANT_MIN_DIMENSIONS,
SUB_PROCESS_MIN_DIMENSIONS,
TEXT_ANNOTATION_MIN_DIMENSIONS
} from './ResizeBehavior';

import { getChildLanes } from '../util/LaneUtil';

/**
* @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
*
* @typedef {import('../../../model/Types').Shape} Shape
*/

var max = Math.max;

/**
* @param {EventBus} eventBus
*/
export default function SpaceToolBehavior(eventBus) {
eventBus.on('spaceTool.getMinDimensions', function(context) {
var shapes = context.shapes,
axis = context.axis,
start = context.start,
minDimensions = {};

forEach(shapes, function(shape) {
var id = shape.id;

if (is(shape, 'bpmn:Participant')) {
minDimensions[ id ] = getParticipantMinDimensions(shape, axis, start);
}

if (is(shape, 'bpmn:Lane')) {
minDimensions[ id ] = isHorizontal(shape) ? LANE_MIN_DIMENSIONS : VERTICAL_LANE_MIN_DIMENSIONS;
}

if (is(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
minDimensions[ id ] = SUB_PROCESS_MIN_DIMENSIONS;
}

if (is(shape, 'bpmn:TextAnnotation')) {
minDimensions[ id ] = TEXT_ANNOTATION_MIN_DIMENSIONS;
}

if (is(shape, 'bpmn:Group')) {
minDimensions[ id ] = GROUP_MIN_DIMENSIONS;
}
});

return minDimensions;
});
}

SpaceToolBehavior.$inject = [ 'eventBus' ];


// helpers //////////
function isHorizontalAxis(axis) {
return axis === 'x';
}

/**
* Get minimum dimensions for participant taking lanes into account.
*
* @param {Shape} participant
* @param {Axis} axis
* @param {number} start
*
* @return {number}
*/
function getParticipantMinDimensions(participant, axis, start) {
var isHorizontalLane = isHorizontal(participant);

if (!hasChildLanes(participant)) {
return isHorizontalLane ? PARTICIPANT_MIN_DIMENSIONS : VERTICAL_PARTICIPANT_MIN_DIMENSIONS;
}

var isHorizontalResize = isHorizontalAxis(axis);
var minDimensions = {};

if (isHorizontalResize) {
if (isHorizontalLane) {
minDimensions = PARTICIPANT_MIN_DIMENSIONS;
} else {
minDimensions = {
width: getParticipantMinWidth(participant, start, isHorizontalResize),
height: VERTICAL_PARTICIPANT_MIN_DIMENSIONS.height
};
}

} else {
if (isHorizontalLane) {
minDimensions = {
width: PARTICIPANT_MIN_DIMENSIONS.width,
height: getParticipantMinHeight(participant, start, isHorizontalResize)
};
} else {
minDimensions = VERTICAL_PARTICIPANT_MIN_DIMENSIONS;
}
}

return minDimensions;
}

/**
* Get minimum height for participant taking lanes into account.
*
* @param {Shape} participant
* @param {number} start
* @param {boolean} isHorizontalResize
*
* @return {number}
*/
function getParticipantMinHeight(participant, start, isHorizontalResize) {
var lanesMinHeight;
lanesMinHeight = getLanesMinHeight(participant, start, isHorizontalResize);
return max(PARTICIPANT_MIN_DIMENSIONS.height, lanesMinHeight);
}

/**
* Get minimum width for participant taking lanes into account.
*
* @param {Shape} participant
* @param {number} start
* @param {boolean} isHorizontalResize
*
* @return {number}
*/
function getParticipantMinWidth(participant, start, isHorizontalResize) {
var lanesMinWidth;
lanesMinWidth = getLanesMinWidth(participant, start, isHorizontalResize);
return max(VERTICAL_PARTICIPANT_MIN_DIMENSIONS.width, lanesMinWidth);
}

function hasChildLanes(element) {
return !!getChildLanes(element).length;
}

function getLanesMinHeight(participant, resizeStart, isHorizontalResize) {
var lanes = getChildLanes(participant),
resizedLane;

// find the nested lane which is currently resized
resizedLane = findResizedLane(lanes, resizeStart, isHorizontalResize);

// resized lane cannot shrink below the minimum height
// but remaining lanes' dimensions are kept intact
return participant.height - resizedLane.height + LANE_MIN_DIMENSIONS.height;
}

function getLanesMinWidth(participant, resizeStart, isHorizontalResize) {
var lanes = getChildLanes(participant),
resizedLane;

// find the nested lane which is currently resized
resizedLane = findResizedLane(lanes, resizeStart, isHorizontalResize);

// resized lane cannot shrink below the minimum width
// but remaining lanes' dimensions are kept intact
return participant.width - resizedLane.width + VERTICAL_LANE_MIN_DIMENSIONS.width;
}

/**
* Find nested lane which is currently resized.
*
* @param {Shape[]} lanes
* @param {number} resizeStart
* @param {boolean} isHorizontalResize
*
* @return {Shape}
*/
function findResizedLane(lanes, resizeStart, isHorizontalResize) {
var i, lane, childLanes;

for (i = 0; i < lanes.length; i++) {
lane = lanes[i];

// resizing current lane or a lane nested
if (!isHorizontalResize && resizeStart >= lane.y && resizeStart <= lane.y + lane.height ||
isHorizontalResize && resizeStart >= lane.x && resizeStart <= lane.x + lane.width) {

childLanes = getChildLanes(lane);

// a nested lane is resized
if (childLanes.length) {
return findResizedLane(childLanes, resizeStart, isHorizontalResize);
}

// current lane is the resized one
return lane;
}
}
}
Loading

0 comments on commit 0a706be

Please sign in to comment.