Hello,
I want to make the background as the default x,y coordinates of any element nested within it whenever I create and move them inside of it. Like in the picture, I want the custom:electricityResource x and y become zero when I create or move it at the corner of the background. Also, I want to restrict creating and moving custom elements outside of the background. How can I achieve that?
Thanks…
This is my exported diagram. The custom:electricityResource x and y are not 0.
<?xml version="1.0" encoding="UTF-8"?>
<custom:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:custom="http://custom.com" xmlns:customdi="http://customdi.com" xmlns:dc="http://dc.com" id="Definitions_1">
<custom:modeler id="Modeler_1" isExecutable="false">
<custom:background id="Background_1">
<custom:electricityResource id="ElectricityResource_16jn66p" serviceStatus="true" enabled="true" />
</custom:background>
</custom:modeler>
<customdi:CUSTOMDiagram id="CUSTOMDiagram_1">
<customdi:CUSTOMPlane id="CUSTOMPlane_1" customElement="Modeler_1">
<customdi:CUSTOMShape id="_CUSTOMShape_Background_2" customElement="Background_1">
<dc:Bounds x="135" y="13" width="1100" height="750" />
</customdi:CUSTOMShape>
<customdi:CUSTOMShape id="ElectricityResource_16jn66p_di" customElement="ElectricityResource_16jn66p">
<dc:Bounds x="138" y="16" width="36" height="36" />
</customdi:CUSTOMShape>
</customdi:CUSTOMPlane>
</customdi:CUSTOMDiagram>
</custom:definitions>
This is my CustomUpdater.js file.
import {
assign,
forEach
} from 'min-dash';
import inherits from 'inherits';
import {
remove as collectionRemove,
} from 'diagram-js/lib/util/Collections';
import {
Label
} from 'diagram-js/lib/model';
import {
is
} from '../../util/ModelUtil';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
/**
* A handler responsible for updating the underlying CUSTOM XML + DI
* once changes on the diagram happen
*/
export default function CustomUpdater(
eventBus, customFactory, translate) {
CommandInterceptor.call(this, eventBus);
this._customFactory = customFactory;
this._translate = translate;
var self = this;
// CUSTOM + DI update //////////////////////
// update parent
function updateParent(e) {
var context = e.context;
self.updateParent(context.shape || context.oldParent);
}
function reverseUpdateParent(e) {
var context = e.context;
var element = context.shape;
// oldParent is the (old) new parent, because we are undoing
oldParent = context.parent || context.newParent;
self.updateParent(element, oldParent);
}
this.executed([
'shape.move',
'shape.create',
'shape.delete',
], ifBpmn(updateParent));
this.reverted([
'shape.move',
'shape.create',
'shape.delete',
], ifBpmn(reverseUpdateParent));
/*
* ## Updating Parent
*/
function updateRoot(event) {
var context = event.context,
oldRoot = context.oldRoot,
children = oldRoot.children;
forEach(children, function(child) {
if (is(child, 'custom:BaseElement')) {
self.updateParent(child);
}
});
}
this.executed([ 'canvas.updateRoot' ], updateRoot);
this.reverted([ 'canvas.updateRoot' ], updateRoot);
// update bounds
function updateBounds(e) {
var shape = e.context.shape;
if (!is(shape, 'custom:BaseElement')) {
return;
}
self.updateBounds(shape);
}
this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
// exclude labels because they're handled separately during shape.changed
if (event.context.shape.type === 'label') {
return;
}
updateBounds(event);
}));
this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
// exclude labels because they're handled separately during shape.changed
if (event.context.shape.type === 'label') {
return;
}
updateBounds(event);
}));
// Handle labels separately. This is necessary, because the label bounds have to be updated
// every time its shape changes, not only on move, create and resize.
eventBus.on('shape.changed', function(event) {
if (event.element.type === 'label') {
updateBounds({ context: { shape: event.element } });
}
});
}
inherits(CustomUpdater, CommandInterceptor);
CustomUpdater.$inject = [
'eventBus',
'customFactory',
'translate'
];
// implementation //////////////////////
CustomUpdater.prototype.updateParent = function(element, oldParent) {
// do not update label parent
if (element instanceof Label) {
return;
}
var parentShape = element.parent;
var businessObject = element.businessObject,
parentBusinessObject = parentShape && parentShape.businessObject,
parentDi = parentBusinessObject && parentBusinessObject.di;
this.updateSemanticParent(businessObject, parentBusinessObject);
this.updateDiParent(businessObject.di, parentDi);
};
CustomUpdater.prototype.updateBounds = function(shape) {
var di = shape.businessObject.di;
var target = (shape instanceof Label) ? this._getLabel(di) : di;
var bounds = target.bounds;
if (!bounds) {
bounds = this._customFactory.createDiBounds();
target.set('bounds', bounds);
}
assign(bounds, {
x: shape.x,
y: shape.y,
width: shape.width,
height: shape.height
});
};
CustomUpdater.prototype.updateDiParent = function(di, parentDi) {
if (parentDi && !is(parentDi, 'customdi:CUSTOMPlane')) {
parentDi = parentDi.$parent;
}
if (di.$parent === parentDi) {
return;
}
var planeElements = (parentDi || di.$parent).get('planeElement');
if (parentDi) {
planeElements.push(di);
di.$parent = parentDi;
} else {
collectionRemove(planeElements, di);
di.$parent = null;
}
};
CustomUpdater.prototype.updateSemanticParent = function(businessObject, newParent) {
var containment,
translate = this._translate;
if (businessObject.$parent === newParent) {
return;
}
if (is(businessObject, 'custom:Modeler')) {
if (newParent) {
if (is(newParent, 'custom:Lane')) {
do {
// unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
newParent = newParent.$parent.$parent;
} while (is(newParent, 'custom:Lane'));
}
}
containment = 'modelerShapes';
}
if (!containment) {
throw new Error(translate(
'no parent for {element} in {parent}',
{
element: businessObject.id,
parent: newParent.id
}
));
}
var children;
if (businessObject.$parent) {
// remove from old parent
children = businessObject.$parent.get(containment);
collectionRemove(children, businessObject);
}
if (!newParent) {
businessObject.$parent = null;
} else {
// add to new parent
children = newParent.get(containment);
children.push(businessObject);
businessObject.$parent = newParent;
}
};
// helpers //////////////////////
CustomUpdater.prototype._getLabel = function(di) {
if (!di.label) {
di.label = this._customFactory.createDiLabel();
}
return di.label;
};
/**
* Make sure the event listener is only called
* if the touched element is a CUSTOM element.
*
* @param {Function} fn
* @return {Function} guarded function
*/
function ifBpmn(fn) {
return function(event) {
var context = event.context,
element = context.shape;
if (is(element, 'custom:BaseElement')) {
fn(event);
}
};
}