diagram-js
Version:
A toolbox for displaying and modifying diagrams on the web
179 lines (132 loc) • 4.07 kB
JavaScript
import {
forEach,
filter
} from 'min-dash';
import inherits from 'inherits-browser';
var LOW_PRIORITY = 250,
HIGH_PRIORITY = 1400;
import {
add as collectionAdd,
indexOf as collectionIdx
} from '../../util/Collections';
import { saveClear } from '../../util/Removal';
import CommandInterceptor from '../../command/CommandInterceptor';
/**
* @typedef {import('../../model/Types').Element} Element
*
* @typedef {import('didi').Injector} Injector
*
* @typedef {import('../../core/EventBus').default} EventBus
* @typedef {import('../modeling/Modeling').default} Modeling
*/
/**
* A handler that makes sure labels are properly moved with
* their label targets.
*
* @param {Injector} injector
* @param {EventBus} eventBus
* @param {Modeling} modeling
*/
export default function LabelSupport(injector, eventBus, modeling) {
CommandInterceptor.call(this, eventBus);
var movePreview = injector.get('movePreview', false);
// remove labels from the collection that are being
// moved with other elements anyway
eventBus.on('shape.move.start', HIGH_PRIORITY, function(e) {
var context = e.context,
shapes = context.shapes,
validatedShapes = context.validatedShapes;
context.shapes = removeLabels(shapes);
context.validatedShapes = removeLabels(validatedShapes);
});
// add labels to visual's group
movePreview && eventBus.on('shape.move.start', LOW_PRIORITY, function(e) {
var context = e.context,
shapes = context.shapes;
var labels = [];
forEach(shapes, function(element) {
forEach(element.labels, function(label) {
if (!label.hidden && context.shapes.indexOf(label) === -1) {
labels.push(label);
}
if (element.labelTarget) {
labels.push(element);
}
});
});
forEach(labels, function(label) {
movePreview.makeDraggable(context, label, true);
});
});
// add all labels to move closure
this.preExecuted('elements.move', HIGH_PRIORITY, function(e) {
var context = e.context,
closure = context.closure,
enclosedElements = closure.enclosedElements;
var enclosedLabels = [];
// find labels that are not part of
// move closure yet and add them
forEach(enclosedElements, function(element) {
forEach(element.labels, function(label) {
if (!enclosedElements[label.id]) {
enclosedLabels.push(label);
}
});
});
closure.addAll(enclosedLabels);
});
this.preExecute([
'connection.delete',
'shape.delete'
], function(e) {
var context = e.context,
element = context.connection || context.shape;
saveClear(element.labels, function(label) {
modeling.removeShape(label, { nested: true });
});
});
this.execute('shape.delete', function(e) {
var context = e.context,
shape = context.shape,
labelTarget = shape.labelTarget;
// unset labelTarget
if (labelTarget) {
context.labelTargetIndex = collectionIdx(labelTarget.labels, shape);
context.labelTarget = labelTarget;
shape.labelTarget = null;
}
});
this.revert('shape.delete', function(e) {
var context = e.context,
shape = context.shape,
labelTarget = context.labelTarget,
labelTargetIndex = context.labelTargetIndex;
// restore labelTarget
if (labelTarget) {
collectionAdd(labelTarget.labels, shape, labelTargetIndex);
shape.labelTarget = labelTarget;
}
});
}
inherits(LabelSupport, CommandInterceptor);
LabelSupport.$inject = [
'injector',
'eventBus',
'modeling'
];
/**
* Return a filtered list of elements that do not
* contain attached elements with hosts being part
* of the selection.
*
* @param {Element[]} elements
*
* @return {Element[]} filtered
*/
function removeLabels(elements) {
return filter(elements, function(element) {
// filter out labels that are move together
// with their label targets
return elements.indexOf(element.labelTarget) === -1;
});
}