Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/bpmn-support.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ The default rendering uses `white` as fill color and `black` as stroke color.
|Stroke fits the BPMN specification but no icon

|Message Intermediate Catch Event
|
|Stroke fits the BPMN specification but no icon
|icon:check-circle-o[]
|The stroke & icon width may be adjusted
|===

[cols="1,1,4", options="header"]
Expand Down
113 changes: 43 additions & 70 deletions src/component/mxgraph/shape/event-shapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,63 @@ const mxUtils: typeof mxgraph.mxUtils = MxGraphFactoryService.getMxGraphProperty
const mxConstants: typeof mxgraph.mxConstants = MxGraphFactoryService.getMxGraphProperty('mxConstants');

abstract class EventShape extends mxEllipse {
// when all/more event types will be supported, we could move to a Record/MappedType
private iconPainters: Map<ShapeBpmnEventKind, (c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number) => void> = new Map([
[ShapeBpmnEventKind.MESSAGE, (c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number) => this.paintMessageIcon(c, x, y, w, h)],
[ShapeBpmnEventKind.TERMINATE, (c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number) => this.paintTerminateIcon(c, x, y, w, h)],
]);
protected isUsingThrowIcons = false;

protected constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth: number) {
super(bounds, fill, stroke, strokewidth);
}

public paintVertexShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
this.markNonFullyRenderedEvents(c);
this.paintOuterShape(c, x, y, w, h);
this.paintInnerShape(c, x, y, w, h);
}

// This will be removed when managing the render of all events
private markNonFullyRenderedEvents(c: mxgraph.mxXmlCanvas2D): void {
const eventKind = this.getBpmnEventKind();
// will be removed when managing the timer rendering
if (eventKind == ShapeBpmnEventKind.TIMER) {
c.setFillColor('green');
c.setFillAlpha(0.3);
} // eslint-disable-next-line @typescript-eslint/no-use-before-define
else if (eventKind == ShapeBpmnEventKind.MESSAGE && (this instanceof StartEventShape || this instanceof EndEventShape)) {
c.setFillColor('yellow');
c.setFillAlpha(0.3);
}

this.paintOuterShape(c, x, y, w, h);
this.paintInnerShape(c, x, y, w, h);
}

protected paintOuterShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
super.paintVertexShape(c, x, y, w, h);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected paintInnerShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
// do nothing by default
const iconPainter =
this.iconPainters.get(this.getBpmnEventKind()) || ((c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number) => this.paintEmptyIcon(c, x, y, w, h));
iconPainter(c, x, y, w, h);
Comment thread
tbouffard marked this conversation as resolved.
Outdated
}

protected getBpmnEventKind(): ShapeBpmnEventKind {
private getBpmnEventKind(): ShapeBpmnEventKind {
return mxUtils.getValue(this.style, StyleConstant.BPMN_STYLE_EVENT_KIND, ShapeBpmnEventKind.NONE);
}

// TODO: will be removed when managing the message rendering
protected paintOuterMessageShape(c: mxgraph.mxXmlCanvas2D): void {
c.setFillColor('yellow');
c.setFillAlpha(0.3);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private paintEmptyIcon(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
// empty by nature
}

// this implementation is adapted from the draw.io BPMN 'message' symbol
// https://github.com/jgraph/drawio/blob/0e19be6b42755790a749af30450c78c0d83be765/src/main/webapp/shapes/bpmn/mxBpmnShape2.js#L465
private paintMessageIcon(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number, isInverse = false): void {
private paintMessageIcon(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (!(this instanceof IntermediateEventShape)) {
return;
}
const isInverse = this.isUsingThrowIcons;
// Change the coordinate referential
c.translate(x + w * 0.24, y + h * 0.34);
w = w * 0.52;
Expand Down Expand Up @@ -111,41 +130,8 @@ abstract class EventShape extends mxEllipse {
c.stroke();
}

protected paintThrowMessageIcon(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
this.paintMessageIcon(c, x, y, w, h, true);
}
}

export class StartEventShape extends EventShape {
public constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth: number = StyleConstant.STROKE_WIDTH_THIN) {
super(bounds, fill, stroke, strokewidth);
}

// TODO: will be removed when managing the message rendering
protected paintOuterShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
const eventKind = this.getBpmnEventKind();
if (eventKind == ShapeBpmnEventKind.MESSAGE) {
this.paintOuterMessageShape(c);
}

super.paintOuterShape(c, x, y, w, h);
}
}

export class EndEventShape extends EventShape {
public constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth: number = StyleConstant.STROKE_WIDTH_THICK) {
super(bounds, fill, stroke, strokewidth);
}

protected paintInnerShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
const eventKind = this.getBpmnEventKind();
if (eventKind == ShapeBpmnEventKind.TERMINATE) {
this.paintTerminateEventIcon(c, x, y, w, h);
}
}

// highly inspired from mxDoubleEllipse
private paintTerminateEventIcon(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
private paintTerminateIcon(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
c.setFillColor(this.stroke);
c.setStrokeWidth(0);
const inset = mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5)));
Expand All @@ -160,15 +146,17 @@ export class EndEventShape extends EventShape {

c.fillAndStroke();
}
}

// TODO: will be removed when managing the message rendering
protected paintOuterShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
const eventKind = this.getBpmnEventKind();
if (eventKind == ShapeBpmnEventKind.MESSAGE) {
this.paintOuterMessageShape(c);
}
export class StartEventShape extends EventShape {
public constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth: number = StyleConstant.STROKE_WIDTH_THIN) {
super(bounds, fill, stroke, strokewidth);
}
}

super.paintOuterShape(c, x, y, w, h);
export class EndEventShape extends EventShape {
public constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth: number = StyleConstant.STROKE_WIDTH_THICK) {
super(bounds, fill, stroke, strokewidth);
}
}

Expand All @@ -193,26 +181,11 @@ export class CatchIntermediateEventShape extends IntermediateEventShape {
public constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth?: number) {
super(bounds, fill, stroke, strokewidth);
}

// TODO: will be removed when managing the message rendering
protected paintOuterShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
const eventKind = this.getBpmnEventKind();
if (eventKind == ShapeBpmnEventKind.MESSAGE) {
this.paintOuterMessageShape(c);
}

super.paintOuterShape(c, x, y, w, h);
}
}

export class ThrowIntermediateEventShape extends IntermediateEventShape {
public constructor(bounds: mxgraph.mxRectangle, fill: string, stroke: string, strokewidth?: number) {
super(bounds, fill, stroke, strokewidth);
}

protected paintInnerShape(c: mxgraph.mxXmlCanvas2D, x: number, y: number, w: number, h: number): void {
const eventKind = this.getBpmnEventKind();
if (eventKind == ShapeBpmnEventKind.MESSAGE) {
this.paintThrowMessageIcon(c, x, y, w, h);
}
this.isUsingThrowIcons = true;
}
}
7 changes: 7 additions & 0 deletions test/e2e/View.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ describe('BPMN Visualization JS', () => {
<semantic:intermediateThrowEvent name="Throw Message Intermediate Event" id="messageIntermediateThrowEvent">
<semantic:messageEventDefinition />
</semantic:intermediateThrowEvent>
<semantic:intermediateCatchEvent name="Catch Message Intermediate Event" id="messageIntermediateCatchEvent">
<semantic:messageEventDefinition />
</semantic:intermediateCatchEvent>
<semantic:intermediateCatchEvent id="IntermediateCatchEvent_Timer_01" name="Timer Intermediate Catch Event">
<semantic:incoming>Flow_028jkgv</semantic:incoming>
<semantic:timerEventDefinition id="TimerEventDefinition_0t6k83a" />
Expand Down Expand Up @@ -114,6 +117,9 @@ describe('BPMN Visualization JS', () => {
<bpmndi:BPMNShape bpmnElement="messageIntermediateThrowEvent" id="S1373649849862_messageIntermediateThrowEvent">
<dc:Bounds height="32.0" width="32.0" x="698.0" y="335.0" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="messageIntermediateCatchEvent" id="S1373649849862_messageIntermediateCatchEvent">
<dc:Bounds height="32.0" width="32.0" x="98.0" y="335.0" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endEvent_1" id="S1373649849862_endEvent_1">
<dc:Bounds height="32.0" width="32.0" x="648.0" y="335.0"/>
<bpmndi:BPMNLabel labelStyle="LS1373649849858">
Expand Down Expand Up @@ -202,6 +208,7 @@ describe('BPMN Visualization JS', () => {
expectModelContainsBpmnEvent('endEvent_1', ShapeBpmnElementKind.EVENT_END, ShapeBpmnEventKind.TERMINATE);
expectModelContainsBpmnEvent('noneIntermediateThrowEvent', ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW, ShapeBpmnEventKind.NONE);
expectModelContainsBpmnEvent('messageIntermediateThrowEvent', ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW, ShapeBpmnEventKind.MESSAGE);
expectModelContainsBpmnEvent('messageIntermediateCatchEvent', ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH, ShapeBpmnEventKind.MESSAGE);
expectModelContainsBpmnEvent('IntermediateCatchEvent_Timer_01', ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH, ShapeBpmnEventKind.TIMER);
expectModelContainsCell('task_1', ShapeBpmnElementKind.TASK);
expectModelContainsCell('serviceTask_2', ShapeBpmnElementKind.TASK_SERVICE);
Expand Down