Skip to content

Commit

Permalink
feat(copy-paste): refactor
Browse files Browse the repository at this point in the history
* add <element.copyProperties> and <element.propertyCopied> event
* copy category value if group is copied
* add camunda-bpmn-moddle for integration tests

BREAKING CHANGES

* BpmnRules: removed <elements.paste> rule in favor of <elements.create> rule
* BpmnRules: removed <element.paste> rule
* removed CopyPasteBehavior in favor of CreateBehavior
* use attrs.di property for fill and stroke when creating element through ElementFactory#createBpmnElement
* CopyModelElementUtil: rename <property.clone> event to <element.copyProperty>
  • Loading branch information
philippfromme committed Aug 6, 2019
1 parent 4233bd0 commit 6c14110
Show file tree
Hide file tree
Showing 34 changed files with 3,930 additions and 3,600 deletions.
192 changes: 98 additions & 94 deletions lib/features/copy-paste/BpmnCopyPaste.js
Original file line number Diff line number Diff line change
@@ -1,145 +1,146 @@
import CopyModelElementUtil from '../../util/CopyModelElementUtil';

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

import ModelCloneHelper from '../../util/model/ModelCloneHelper';

import {
getProperties,
IGNORED_PROPERTIES
} from '../../util/model/ModelCloneUtils';

import {
filter,
forEach
forEach,
isArray,
isUndefined,
omit,
reduce
} from 'min-dash';

function setProperties(descriptor, data, properties) {
function copyProperties(source, target, properties) {
if (!isArray(properties)) {
properties = [ properties ];
}

forEach(properties, function(property) {
if (data[property] !== undefined) {
descriptor[property] = data[property];
if (!isUndefined(source[property])) {
target[property] = source[property];
}
});
}

function removeProperties(element, properties) {
forEach(properties, function(prop) {
if (element[prop]) {
delete element[prop];
if (!isArray(properties)) {
properties = [ properties ];
}

forEach(properties, function(property) {
if (element[property]) {
delete element[property];
}
});
}

export default function BpmnCopyPaste(
bpmnFactory, eventBus, copyPaste,
clipboard, canvas, bpmnRules) {
var LOW_PRIORITY = 750;

var helper = new ModelCloneHelper(eventBus, bpmnFactory);

copyPaste.registerDescriptor(function(element, descriptor) {
var businessObject = descriptor.oldBusinessObject = getBusinessObject(element);
export default function BpmnCopyPaste(bpmnFactory, eventBus, injector) {

var colors = {};
var copyModelElementUtil = injector.instantiate(CopyModelElementUtil);

eventBus.on('element.copy', LOW_PRIORITY, function(context) {
var descriptor = context.descriptor,
element = context.element;

var businessObject = descriptor.oldBusinessObject = getBusinessObject(element);

descriptor.type = element.type;

setProperties(descriptor, businessObject.di, [ 'isExpanded' ]);
descriptor.di = {};

setProperties(colors, businessObject.di, [ 'fill', 'stroke' ]);
// fill and stroke will be set to DI
copyProperties(businessObject.di, descriptor.di, [
'fill',
'stroke'
]);

descriptor.colors = colors;
copyProperties(businessObject.di, descriptor, 'isExpanded');

if (element.type === 'label') {
if (isLabel(descriptor)) {
return descriptor;
}

setProperties(descriptor, businessObject, [
'processRef',
'triggeredByEvent'
]);

// default sequence flow
if (businessObject.default) {
descriptor.default = businessObject.default.id;
}

return descriptor;
});

eventBus.on('element.paste', function(context) {
var descriptor = context.descriptor,
createdElements = context.createdElements,
parent = descriptor.parent,
rootElement = canvas.getRootElement(),
oldBusinessObject = descriptor.oldBusinessObject,
newBusinessObject,
source,
target,
canConnect;

newBusinessObject = bpmnFactory.create(oldBusinessObject.$type);
eventBus.on('element.copyProperty', function(context) {
var parent = context.parent,
property = context.property,
propertyName = context.propertyName;

var properties = getProperties(oldBusinessObject.$descriptor);
if (is(parent, 'bpmn:Participant') &&
is(property, 'bpmn:Process') &&
propertyName === 'processRef') {
return bpmnFactory.create('bpmn:Process');
}
});

properties = filter(properties, function(property) {
return IGNORED_PROPERTIES.indexOf(property.replace(/bpmn:/, '')) === -1;
});
var references;

descriptor.businessObject = helper.clone(oldBusinessObject, newBusinessObject, properties);
function resolveReferences(descriptor) {
var businessObject = getBusinessObject(descriptor);

if (descriptor.type === 'label') {
return;
// default sequence flows
if (descriptor.default) {
references[ descriptor.default ] = {
element: businessObject,
property: 'default'
};
}

if (is(parent, 'bpmn:Process')) {
descriptor.parent = is(rootElement, 'bpmn:Collaboration') ? rootElement : parent;
}
references = omit(references, reduce(references, function(array, reference, key) {
var element = reference.element,
property = reference.property;

if (descriptor.type === 'bpmn:DataOutputAssociation' ||
descriptor.type === 'bpmn:DataInputAssociation' ||
descriptor.type === 'bpmn:MessageFlow') {
descriptor.parent = rootElement;
}
if (key === descriptor.id) {
element[ property ] = businessObject;

if (is(parent, 'bpmn:Lane')) {
descriptor.parent = parent.parent;
}
array.push(descriptor.id);
}

// make sure that the correct type of connection is created
if (descriptor.waypoints) {
source = createdElements[descriptor.source];
target = createdElements[descriptor.target];
return array;
}, []));
}

if (source && target) {
source = source.element;
target = target.element;
}
eventBus.on('elements.paste', function() {
references = {};
});

eventBus.on('element.paste', function(context) {
var cache = context.cache,
descriptor = context.descriptor,
oldBusinessObject = descriptor.oldBusinessObject,
newBusinessObject;

canConnect = bpmnRules.canConnect(source, target);
// do NOT copy business object if external label
if (isLabel(descriptor)) {
descriptor.businessObject = getBusinessObject(cache[ descriptor.labelTarget ]);

if (canConnect) {
descriptor.type = canConnect.type;
}
return;
}

// remove the id or else we cannot paste multiple times
delete newBusinessObject.id;
newBusinessObject = bpmnFactory.create(oldBusinessObject.$type);

// assign an ID
bpmnFactory._ensureId(newBusinessObject);
descriptor.businessObject = copyModelElementUtil.copyElement(
oldBusinessObject,
newBusinessObject
);

if (descriptor.type === 'bpmn:Participant' && descriptor.processRef) {
descriptor.processRef = newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
}
// resolve references references e.g. default sequence flow
resolveReferences(descriptor);

setProperties(newBusinessObject, descriptor, [
'isExpanded',
'triggeredByEvent'
]);
copyProperties(descriptor, newBusinessObject, 'isExpanded');

removeProperties(descriptor, [
'triggeredByEvent'
]);
removeProperties(descriptor, 'oldBusinessObject');
});

}
Expand All @@ -148,8 +149,11 @@ export default function BpmnCopyPaste(
BpmnCopyPaste.$inject = [
'bpmnFactory',
'eventBus',
'copyPaste',
'clipboard',
'canvas',
'bpmnRules'
];
'injector'
];

// helpers //////////

function isLabel(element) {
return !!element.labelTarget;
}
6 changes: 3 additions & 3 deletions lib/features/modeling/ElementFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ ElementFactory.prototype.createBpmnElement = function(elementType, attrs) {
}, attrs);
}

if (attrs.colors) {
assign(businessObject.di, attrs.colors);
if (attrs.di) {
assign(businessObject.di, attrs.di);

delete attrs.colors;
delete attrs.di;
}

applyAttributes(businessObject, attrs, [
Expand Down
71 changes: 0 additions & 71 deletions lib/features/modeling/behavior/CopyPasteBehavior.js

This file was deleted.

27 changes: 27 additions & 0 deletions lib/features/modeling/behavior/CreateBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import inherits from 'inherits';

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

import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';

import { getParent } from '../util/ModelingUtil';


export default function CreateBehavior(injector) {
injector.invoke(CommandInterceptor, this);

this.preExecute('shape.create', 1500, function(event) {
var context = event.context,
parent = context.parent;

if (is(parent, 'bpmn:Lane')) {
context.parent = getParent(parent, 'bpmn:Participant');
}
});

}


CreateBehavior.$inject = [ 'injector' ];

inherits(CreateBehavior, CommandInterceptor);
Loading

0 comments on commit 6c14110

Please sign in to comment.