I am working on Camunda modeler to add a diagram type based on the open source project. I would like to add a brand-new diagram type beyond the default diagrams like BPMN DMN and CMMN. I can call it Activity Diagram. Adding a new diagram means Camunda engine should be able to recognize and show it on the UI. For example, I should be able to run the following code without any issue in the base project.
const element = event.element;
if (is(element, 'bpmn:ManualTask')) {
// Code to open and render the new Activity Diagram
const activityDiagramXML = `<activityDiagram xmlns="http://example.com/schema/activityDiagram">
<metrics id="metrics1"/>
<operation id="operation1"/>
</activityDiagram>`;
BPMNJS.importXML(activityDiagramXML, (err) => {
if (!err) {
console.log('Activity Diagram rendered');
} else {
console.error('Failed to render Activity Diagram', err);
}
});
}
Unfortunately, the default importXML function only support default xml tags like bpmn:Definitions.
Here is the piece of code of the function.
BaseViewer.prototype.importXML = async function importXML(xml, bpmnDiagram) {
const self = this;
function ParseCompleteEvent(data) {
return self.get('eventBus').createEvent(data);
}
let aggregatedWarnings = [];
try {
// hook in pre-parse listeners +
// allow xml manipulation
/**
* A `import.parse.start` event.
*
* @event BaseViewer#ImportParseStartEvent
* @type {ImportParseStartEvent}
*/
xml = this._emit('import.parse.start', { xml: xml }) || xml;
let parseResult;
try {
parseResult = await this._moddle.fromXML(xml, 'bpmn:Definitions');
} catch (error) {
this._emit('import.parse.complete', {
error
});
throw error;
}
let definitions = parseResult.rootElement;
const references = parseResult.references;
const parseWarnings = parseResult.warnings;
const elementsById = parseResult.elementsById;
aggregatedWarnings = aggregatedWarnings.concat(parseWarnings);
// hook in post parse listeners +
// allow definitions manipulation
/**
* A `import.parse.complete` event.
*
* @event BaseViewer#ImportParseCompleteEvent
* @type {ImportParseCompleteEvent}
*/
definitions = this._emit('import.parse.complete', ParseCompleteEvent({
error: null,
definitions: definitions,
elementsById: elementsById,
references: references,
warnings: aggregatedWarnings
})) || definitions;
const importResult = await this.importDefinitions(definitions, bpmnDiagram);
aggregatedWarnings = aggregatedWarnings.concat(importResult.warnings);
/**
* A `import.parse.complete` event.
*
* @event BaseViewer#ImportDoneEvent
* @type {ImportDoneEvent}
*/
this._emit('import.done', { error: null, warnings: aggregatedWarnings });
return { warnings: aggregatedWarnings };
} catch (err) {
let error = err;
aggregatedWarnings = aggregatedWarnings.concat(error.warnings || []);
addWarningsToError(error, aggregatedWarnings);
error = checkValidationError(error);
this._emit('import.done', { error, warnings: error.warnings });
throw error;
}
};
How can I add a new diagram type to Camunda modeler and make the use of xml tags like <activityDiagram> and <operation> possible?
I wrote 2 files, namely, activityDiagramModdle.js and ActivityDiagramRendered.js to support the new diagram type and registered them properly to make Camunda model recognize them. But when I try to render the new diagram type, it causes the following error: failed to parse document as <bpmn:Definitions>. Any help or suggestion would be welcome.
Here are my files. activityDiagramModdle.js
export default {
name: 'ActivityDiagram',
prefix: 'ad',
uri: 'http://example.com/schema/activityDiagram',
xml: {
tagAlias: 'lowerCase'
},
types: [
{
name: 'ActivityDiagram',
superClass: ['Element'],
properties: [
{
name: 'metrics',
isMany: true,
type: 'ad:Metrics'
},
{
name: 'operation',
isMany: true,
type: 'ad:Operation'
}
]
},
{
name: 'Metrics',
superClass: ['Element'],
properties: [
{
name: 'id',
isAttr: true,
type: 'String'
}
]
},
{
name: 'Operation',
superClass: ['Element'],
properties: [
{
name: 'id',
isAttr: true,
type: 'String'
}
]
}
]
};
ActivityDiagramRendered.js
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
export default class ActivityDiagramRenderer extends BaseRenderer {
constructor(eventBus, bpmnRenderer) {
super(eventBus, 2000);
this.bpmnRenderer = bpmnRenderer;
}
canRender(element) {
return /^ad:/.test(element.type);
}
drawShape(parentNode, element) {
const shape = this.bpmnRenderer.drawShape(parentNode, element);
if (element.type === 'ad:Metrics') {
shape.attr({
fill: 'green'
});
} else if (element.type === 'ad:Operation') {
shape.attr({
fill: 'blue'
});
}
return shape;
}
}
ActivityDiagramRenderer.$inject = ['eventBus', 'bpmnRenderer'];