mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-04 16:16:41 +02:00
feat(sequence): actor creation and destruction #1838
This commit is contained in:
@@ -156,6 +156,81 @@ context('Sequence diagram', () => {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('should render a sequence diagram with basic actor creation and destruction', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
sequenceDiagram
|
||||||
|
Alice ->> Bob: Hello Bob, how are you ?
|
||||||
|
Bob ->> Alice: Fine, thank you. And you?
|
||||||
|
create participant Polo
|
||||||
|
Alice ->> Polo: Hi Polo!
|
||||||
|
create actor Ola1 as Ola
|
||||||
|
Polo ->> Ola1: Hiii
|
||||||
|
Ola1 ->> Alice: Hi too
|
||||||
|
destroy Ola1
|
||||||
|
Alice --x Ola1: Bye!
|
||||||
|
Alice ->> Bob: And now?
|
||||||
|
create participant Ola2 as Ola
|
||||||
|
Alice ->> Ola2: Hello again
|
||||||
|
destroy Alice
|
||||||
|
Alice --x Ola2: Bye for me!
|
||||||
|
destroy Bob
|
||||||
|
Ola2 --> Bob: The end
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should render a sequence diagram with actor creation and destruction coupled with backgrounds, loops and notes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
sequenceDiagram
|
||||||
|
accTitle: test the accTitle
|
||||||
|
accDescr: Test a description
|
||||||
|
|
||||||
|
participant Alice
|
||||||
|
participant Bob
|
||||||
|
autonumber 10 10
|
||||||
|
rect rgb(200, 220, 100)
|
||||||
|
rect rgb(200, 255, 200)
|
||||||
|
|
||||||
|
Alice ->> Bob: Hello Bob, how are you?
|
||||||
|
create participant John as John<br />Second Line
|
||||||
|
Bob-->>John: How about you John?
|
||||||
|
end
|
||||||
|
|
||||||
|
Bob--x Alice: I am good thanks!
|
||||||
|
Bob-x John: I am good thanks!
|
||||||
|
Note right of John: John thinks a long<br />long time, so long<br />that the text does<br />not fit on a row.
|
||||||
|
|
||||||
|
Bob-->Alice: Checking with John...
|
||||||
|
Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit.
|
||||||
|
Bob-x John: Hey John - we're still waiting to know<br />how you're doing
|
||||||
|
Note over John:nowrap: John's trying hard not to break his train of thought.
|
||||||
|
destroy John
|
||||||
|
Bob-x John: John! Cmon!
|
||||||
|
Note over John: After a few more moments, John<br />finally snaps out of it.
|
||||||
|
end
|
||||||
|
|
||||||
|
autonumber off
|
||||||
|
alt either this
|
||||||
|
create actor Lola
|
||||||
|
Alice->>+Lola: Yes
|
||||||
|
Lola-->>-Alice: OK
|
||||||
|
else or this
|
||||||
|
autonumber
|
||||||
|
Alice->>Lola: No
|
||||||
|
else or this will happen
|
||||||
|
Alice->Lola: Maybe
|
||||||
|
end
|
||||||
|
autonumber 200
|
||||||
|
par this happens in parallel
|
||||||
|
destroy Bob
|
||||||
|
Alice -->> Bob: Parallel message 1
|
||||||
|
and
|
||||||
|
Alice -->> Lola: Parallel message 2
|
||||||
|
end
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
context('font settings', () => {
|
context('font settings', () => {
|
||||||
it('should render different note fonts when configured', () => {
|
it('should render different note fonts when configured', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
|
@@ -38,6 +38,8 @@
|
|||||||
"box" { this.begin('LINE'); return 'box'; }
|
"box" { this.begin('LINE'); return 'box'; }
|
||||||
"participant" { this.begin('ID'); return 'participant'; }
|
"participant" { this.begin('ID'); return 'participant'; }
|
||||||
"actor" { this.begin('ID'); return 'participant_actor'; }
|
"actor" { this.begin('ID'); return 'participant_actor'; }
|
||||||
|
"create" return 'create';
|
||||||
|
"destroy" { this.begin('ID'); return 'destroy'; }
|
||||||
<ID>[^\->:\n,;]+?([\-]*[^\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
<ID>[^\->:\n,;]+?([\-]*[^\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
||||||
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
|
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
|
||||||
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
|
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
|
||||||
@@ -138,6 +140,7 @@ directive
|
|||||||
|
|
||||||
statement
|
statement
|
||||||
: participant_statement
|
: participant_statement
|
||||||
|
| 'create' participant_statement {$2.type='createParticipant'; $$=$2;}
|
||||||
| 'box' restOfLine box_section end
|
| 'box' restOfLine box_section end
|
||||||
{
|
{
|
||||||
$3.unshift({type: 'boxStart', boxData:yy.parseBoxData($2) });
|
$3.unshift({type: 'boxStart', boxData:yy.parseBoxData($2) });
|
||||||
@@ -234,10 +237,11 @@ else_sections
|
|||||||
;
|
;
|
||||||
|
|
||||||
participant_statement
|
participant_statement
|
||||||
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
||||||
| 'participant' actor 'NEWLINE' {$2.type='addParticipant';$$=$2;}
|
| 'participant' actor 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant';$$=$2;}
|
||||||
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.type='addActor';$2.description=yy.parseMessage($4); $$=$2;}
|
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
||||||
| 'participant_actor' actor 'NEWLINE' {$2.type='addActor'; $$=$2;}
|
| 'participant_actor' actor 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant'; $$=$2;}
|
||||||
|
| 'destroy' actor 'NEWLINE' {$2.type='destroyParticipant'; $$=$2;}
|
||||||
;
|
;
|
||||||
|
|
||||||
note_statement
|
note_statement
|
||||||
|
@@ -14,12 +14,16 @@ import {
|
|||||||
|
|
||||||
let prevActor = undefined;
|
let prevActor = undefined;
|
||||||
let actors = {};
|
let actors = {};
|
||||||
|
let createdActors = {};
|
||||||
|
let destroyedActors = {};
|
||||||
let boxes = [];
|
let boxes = [];
|
||||||
let messages = [];
|
let messages = [];
|
||||||
const notes = [];
|
const notes = [];
|
||||||
let sequenceNumbersEnabled = false;
|
let sequenceNumbersEnabled = false;
|
||||||
let wrapEnabled;
|
let wrapEnabled;
|
||||||
let currentBox = undefined;
|
let currentBox = undefined;
|
||||||
|
let lastCreated = undefined;
|
||||||
|
let lastDestroyed = undefined;
|
||||||
|
|
||||||
export const parseDirective = function (statement, context, type) {
|
export const parseDirective = function (statement, context, type) {
|
||||||
mermaidAPI.parseDirective(this, statement, context, type);
|
mermaidAPI.parseDirective(this, statement, context, type);
|
||||||
@@ -165,6 +169,12 @@ export const getBoxes = function () {
|
|||||||
export const getActors = function () {
|
export const getActors = function () {
|
||||||
return actors;
|
return actors;
|
||||||
};
|
};
|
||||||
|
export const getCreatedActors = function () {
|
||||||
|
return createdActors;
|
||||||
|
};
|
||||||
|
export const getDestroyedActors = function () {
|
||||||
|
return destroyedActors;
|
||||||
|
};
|
||||||
export const getActor = function (id) {
|
export const getActor = function (id) {
|
||||||
return actors[id];
|
return actors[id];
|
||||||
};
|
};
|
||||||
@@ -194,6 +204,8 @@ export const autoWrap = () => {
|
|||||||
|
|
||||||
export const clear = function () {
|
export const clear = function () {
|
||||||
actors = {};
|
actors = {};
|
||||||
|
createdActors = {};
|
||||||
|
destroyedActors = {};
|
||||||
boxes = [];
|
boxes = [];
|
||||||
messages = [];
|
messages = [];
|
||||||
sequenceNumbersEnabled = false;
|
sequenceNumbersEnabled = false;
|
||||||
@@ -459,10 +471,21 @@ export const apply = function (param) {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'addParticipant':
|
case 'addParticipant':
|
||||||
addActor(param.actor, param.actor, param.description, 'participant');
|
addActor(param.actor, param.actor, param.description, param.draw);
|
||||||
break;
|
break;
|
||||||
case 'addActor':
|
case 'createParticipant':
|
||||||
addActor(param.actor, param.actor, param.description, 'actor');
|
if (actors[param.actor]) {
|
||||||
|
throw new Error(
|
||||||
|
"It is not possible to have actors with the same id, even if one is destroyed before the next is created. Use 'AS' aliases to simulate the behavior"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
lastCreated = param.actor;
|
||||||
|
addActor(param.actor, param.actor, param.description, param.draw);
|
||||||
|
createdActors[param.actor] = messages.length;
|
||||||
|
break;
|
||||||
|
case 'destroyParticipant':
|
||||||
|
lastDestroyed = param.actor;
|
||||||
|
destroyedActors[param.actor] = messages.length;
|
||||||
break;
|
break;
|
||||||
case 'activeStart':
|
case 'activeStart':
|
||||||
addSignal(param.actor, undefined, undefined, param.signalType);
|
addSignal(param.actor, undefined, undefined, param.signalType);
|
||||||
@@ -486,6 +509,27 @@ export const apply = function (param) {
|
|||||||
addDetails(param.actor, param.text);
|
addDetails(param.actor, param.text);
|
||||||
break;
|
break;
|
||||||
case 'addMessage':
|
case 'addMessage':
|
||||||
|
if (lastCreated) {
|
||||||
|
if (param.to !== lastCreated) {
|
||||||
|
throw new Error(
|
||||||
|
'The created participant ' +
|
||||||
|
lastCreated +
|
||||||
|
' does not have an associated creating message after its declaration. Please check the sequence diagram.'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
lastCreated = undefined;
|
||||||
|
}
|
||||||
|
} else if (lastDestroyed) {
|
||||||
|
if (param.to !== lastDestroyed && param.from !== lastDestroyed) {
|
||||||
|
throw new Error(
|
||||||
|
'The destroyed participant ' +
|
||||||
|
lastDestroyed +
|
||||||
|
' does not have an associated destroying message after its declaration. Please check the sequence diagram.'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
lastDestroyed = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
addSignal(param.from, param.to, param.msg, param.signalType);
|
addSignal(param.from, param.to, param.msg, param.signalType);
|
||||||
break;
|
break;
|
||||||
case 'boxStart':
|
case 'boxStart':
|
||||||
@@ -566,6 +610,8 @@ export default {
|
|||||||
showSequenceNumbers,
|
showSequenceNumbers,
|
||||||
getMessages,
|
getMessages,
|
||||||
getActors,
|
getActors,
|
||||||
|
getCreatedActors,
|
||||||
|
getDestroyedActors,
|
||||||
getActor,
|
getActor,
|
||||||
getActorKeys,
|
getActorKeys,
|
||||||
getActorProperty,
|
getActorProperty,
|
||||||
|
@@ -1404,6 +1404,62 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com
|
|||||||
expect(boxes[0].actorKeys).toEqual(['a', 'b']);
|
expect(boxes[0].actorKeys).toEqual(['a', 'b']);
|
||||||
expect(boxes[0].fill).toEqual('Aqua');
|
expect(boxes[0].fill).toEqual('Aqua');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle simple actor creation', async () => {
|
||||||
|
const str = `
|
||||||
|
sequenceDiagram
|
||||||
|
participant a as Alice
|
||||||
|
a ->>b: Hello Bob?
|
||||||
|
create participant c
|
||||||
|
b-->>c: Hello c!
|
||||||
|
c ->> b: Hello b?
|
||||||
|
create actor d as Donald
|
||||||
|
a ->> d: Hello Donald?
|
||||||
|
`;
|
||||||
|
await mermaidAPI.parse(str);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
const createdActors = diagram.db.getCreatedActors();
|
||||||
|
expect(actors['c'].name).toEqual('c');
|
||||||
|
expect(actors['c'].description).toEqual('c');
|
||||||
|
expect(actors['c'].type).toEqual('participant');
|
||||||
|
expect(createdActors['c']).toEqual(1);
|
||||||
|
expect(actors['d'].name).toEqual('d');
|
||||||
|
expect(actors['d'].description).toEqual('Donald');
|
||||||
|
expect(actors['d'].type).toEqual('actor');
|
||||||
|
expect(createdActors['d']).toEqual(3);
|
||||||
|
});
|
||||||
|
it('should handle simple actor destruction', async () => {
|
||||||
|
const str = `
|
||||||
|
sequenceDiagram
|
||||||
|
participant a as Alice
|
||||||
|
a ->>b: Hello Bob?
|
||||||
|
destroy a
|
||||||
|
b-->>a: Hello Alice!
|
||||||
|
b ->> c: Where is Alice?
|
||||||
|
destroy c
|
||||||
|
b ->> c: Where are you?
|
||||||
|
`;
|
||||||
|
await mermaidAPI.parse(str);
|
||||||
|
const destroyedActors = diagram.db.getDestroyedActors();
|
||||||
|
expect(destroyedActors['a']).toEqual(1);
|
||||||
|
expect(destroyedActors['c']).toEqual(3);
|
||||||
|
});
|
||||||
|
it('should handle the creation and destruction of the same actor', async () => {
|
||||||
|
const str = `
|
||||||
|
sequenceDiagram
|
||||||
|
a ->>b: Hello Bob?
|
||||||
|
create participant c
|
||||||
|
b ->>c: Hello c!
|
||||||
|
c ->> b: Hello b?
|
||||||
|
destroy c
|
||||||
|
b ->> c : Bye c !
|
||||||
|
`;
|
||||||
|
await mermaidAPI.parse(str);
|
||||||
|
const createdActors = diagram.db.getCreatedActors();
|
||||||
|
const destroyedActors = diagram.db.getDestroyedActors();
|
||||||
|
expect(createdActors['c']).toEqual(1);
|
||||||
|
expect(destroyedActors['c']).toEqual(3);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe('when checking the bounds in a sequenceDiagram', function () {
|
describe('when checking the bounds in a sequenceDiagram', function () {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
@@ -1973,7 +2029,9 @@ participant Alice`;
|
|||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
expect(bounds.starty).toBe(0);
|
expect(bounds.starty).toBe(0);
|
||||||
expect(bounds.stopx).toBe(conf.width);
|
expect(bounds.stopx).toBe(conf.width);
|
||||||
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + conf.boxMargin);
|
expect(bounds.stopy).toBe(
|
||||||
|
models.lastActor().stopy + models.lastActor().height + conf.boxMargin
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2025,7 +2083,7 @@ participant Alice
|
|||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
expect(bounds.starty).toBe(0);
|
expect(bounds.starty).toBe(0);
|
||||||
expect(bounds.stopy).toBe(
|
expect(bounds.stopy).toBe(
|
||||||
models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin
|
models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should handle one actor, when logLevel is 3 (dfg0)', async () => {
|
it('should handle one actor, when logLevel is 3 (dfg0)', async () => {
|
||||||
@@ -2045,7 +2103,7 @@ participant Alice
|
|||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
expect(bounds.starty).toBe(0);
|
expect(bounds.starty).toBe(0);
|
||||||
expect(bounds.stopy).toBe(
|
expect(bounds.stopy).toBe(
|
||||||
models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin
|
models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should hide sequence numbers when autonumber is removed when autonumber is enabled', async () => {
|
it('should hide sequence numbers when autonumber is removed when autonumber is enabled', async () => {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
// @ts-nocheck TODO: fix file
|
// @ts-nocheck TODO: fix file
|
||||||
import { select, selectAll } from 'd3';
|
import { select, selectAll } from 'd3';
|
||||||
import svgDraw, { drawText, fixLifeLineHeights } from './svgDraw.js';
|
import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import common from '../common/common.js';
|
import common from '../common/common.js';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
import * as svgDrawCommon from '../common/svgDrawCommon';
|
||||||
@@ -478,29 +478,19 @@ const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Di
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawActors = function (
|
const addActorRenderingData = function (
|
||||||
diagram,
|
diagram,
|
||||||
actors,
|
actors,
|
||||||
|
createdActors,
|
||||||
actorKeys,
|
actorKeys,
|
||||||
verticalPos,
|
verticalPos,
|
||||||
configuration,
|
|
||||||
messages,
|
messages,
|
||||||
isFooter
|
isFooter
|
||||||
) {
|
) {
|
||||||
if (configuration.hideUnusedParticipants === true) {
|
|
||||||
const newActors = new Set();
|
|
||||||
messages.forEach((message) => {
|
|
||||||
newActors.add(message.from);
|
|
||||||
newActors.add(message.to);
|
|
||||||
});
|
|
||||||
actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the actors
|
|
||||||
let prevWidth = 0;
|
let prevWidth = 0;
|
||||||
let prevMargin = 0;
|
let prevMargin = 0;
|
||||||
let maxHeight = 0;
|
|
||||||
let prevBox = undefined;
|
let prevBox = undefined;
|
||||||
|
let maxHeight = 0;
|
||||||
|
|
||||||
for (const actorKey of actorKeys) {
|
for (const actorKey of actorKeys) {
|
||||||
const actor = actors[actorKey];
|
const actor = actors[actorKey];
|
||||||
@@ -528,12 +518,16 @@ export const drawActors = function (
|
|||||||
actor.height = common.getMax(actor.height || conf.height, conf.height);
|
actor.height = common.getMax(actor.height || conf.height, conf.height);
|
||||||
actor.margin = actor.margin || conf.actorMargin;
|
actor.margin = actor.margin || conf.actorMargin;
|
||||||
|
|
||||||
actor.x = prevWidth + prevMargin;
|
maxHeight = common.getMax(maxHeight, actor.height);
|
||||||
actor.y = bounds.getVerticalPos();
|
|
||||||
|
// if the actor is created by a message, widen margin
|
||||||
|
if (createdActors[actor.name]) {
|
||||||
|
prevMargin += actor.width / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
actor.x = prevWidth + prevMargin;
|
||||||
|
actor.starty = bounds.getVerticalPos();
|
||||||
|
|
||||||
// Draw the box with the attached line
|
|
||||||
const height = svgDraw.drawActor(diagram, actor, conf, isFooter);
|
|
||||||
maxHeight = common.getMax(maxHeight, height);
|
|
||||||
bounds.insert(actor.x, verticalPos, actor.x + actor.width, actor.height);
|
bounds.insert(actor.x, verticalPos, actor.x + actor.width, actor.height);
|
||||||
|
|
||||||
prevWidth += actor.width + prevMargin;
|
prevWidth += actor.width + prevMargin;
|
||||||
@@ -554,6 +548,28 @@ export const drawActors = function (
|
|||||||
bounds.bumpVerticalPos(maxHeight);
|
bounds.bumpVerticalPos(maxHeight);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const drawActors = function (diagram, actors, actorKeys, isFooter) {
|
||||||
|
if (!isFooter) {
|
||||||
|
for (const actorKey of actorKeys) {
|
||||||
|
const actor = actors[actorKey];
|
||||||
|
// Draw the box with the attached line
|
||||||
|
svgDraw.drawActor(diagram, actor, conf, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let maxHeight = 0;
|
||||||
|
bounds.bumpVerticalPos(conf.boxMargin * 2);
|
||||||
|
for (const actorKey of actorKeys) {
|
||||||
|
const actor = actors[actorKey];
|
||||||
|
if (!actor.stopy) {
|
||||||
|
actor.stopy = bounds.getVerticalPos();
|
||||||
|
}
|
||||||
|
const height = svgDraw.drawActor(diagram, actor, conf, true);
|
||||||
|
maxHeight = common.getMax(maxHeight, height);
|
||||||
|
}
|
||||||
|
bounds.bumpVerticalPos(maxHeight + conf.boxMargin);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const drawActorsPopup = function (diagram, actors, actorKeys, doc) {
|
export const drawActorsPopup = function (diagram, actors, actorKeys, doc) {
|
||||||
let maxHeight = 0;
|
let maxHeight = 0;
|
||||||
let maxWidth = 0;
|
let maxWidth = 0;
|
||||||
@@ -633,6 +649,95 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop
|
|||||||
bounds.bumpVerticalPos(heightAdjust);
|
bounds.bumpVerticalPos(heightAdjust);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust the msgModel and the actor for the rendering in case the latter is created or destroyed by the msg
|
||||||
|
* @param msg - the potentially creating or destroying message
|
||||||
|
* @param msgModel - the model associated with the message
|
||||||
|
* @param lineStartY - the y position of the message line
|
||||||
|
* @param index - the index of the current actor under consideration
|
||||||
|
* @param actors - the array of all actors
|
||||||
|
* @param createdActors - the array of actors created in the diagram
|
||||||
|
* @param destroyedActors - the array of actors destroyed in the diagram
|
||||||
|
*/
|
||||||
|
function adjustCreatedDestroyedData(
|
||||||
|
msg,
|
||||||
|
msgModel,
|
||||||
|
lineStartY,
|
||||||
|
index,
|
||||||
|
actors,
|
||||||
|
createdActors,
|
||||||
|
destroyedActors
|
||||||
|
) {
|
||||||
|
function receiverAdjustment(actor, adjustment) {
|
||||||
|
if (actor.x < actors[msg.from].x) {
|
||||||
|
bounds.insert(
|
||||||
|
msgModel.stopx - adjustment,
|
||||||
|
msgModel.starty,
|
||||||
|
msgModel.startx,
|
||||||
|
msgModel.stopy + actor.height / 2 + conf.noteMargin
|
||||||
|
);
|
||||||
|
msgModel.stopx = msgModel.stopx + adjustment;
|
||||||
|
} else {
|
||||||
|
bounds.insert(
|
||||||
|
msgModel.startx,
|
||||||
|
msgModel.starty,
|
||||||
|
msgModel.stopx + adjustment,
|
||||||
|
msgModel.stopy + actor.height / 2 + conf.noteMargin
|
||||||
|
);
|
||||||
|
msgModel.stopx = msgModel.stopx - adjustment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function senderAdjustment(actor, adjustment) {
|
||||||
|
if (actor.x < actors[msg.to].x) {
|
||||||
|
bounds.insert(
|
||||||
|
msgModel.startx - adjustment,
|
||||||
|
msgModel.starty,
|
||||||
|
msgModel.stopx,
|
||||||
|
msgModel.stopy + actor.height / 2 + conf.noteMargin
|
||||||
|
);
|
||||||
|
msgModel.startx = msgModel.startx + adjustment;
|
||||||
|
} else {
|
||||||
|
bounds.insert(
|
||||||
|
msgModel.stopx,
|
||||||
|
msgModel.starty,
|
||||||
|
msgModel.startx + adjustment,
|
||||||
|
msgModel.stopy + actor.height / 2 + conf.noteMargin
|
||||||
|
);
|
||||||
|
msgModel.startx = msgModel.startx - adjustment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it is a create message
|
||||||
|
if (createdActors[msg.to] == index) {
|
||||||
|
const actor = actors[msg.to];
|
||||||
|
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
|
||||||
|
receiverAdjustment(actor, adjustment);
|
||||||
|
actor.starty = lineStartY - actor.height / 2;
|
||||||
|
bounds.bumpVerticalPos(actor.height / 2);
|
||||||
|
}
|
||||||
|
// if it is a destroy sender message
|
||||||
|
else if (destroyedActors[msg.from] == index) {
|
||||||
|
const actor = actors[msg.from];
|
||||||
|
if (conf.mirrorActors) {
|
||||||
|
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 : actor.width / 2;
|
||||||
|
senderAdjustment(actor, adjustment);
|
||||||
|
}
|
||||||
|
actor.stopy = lineStartY - actor.height / 2;
|
||||||
|
bounds.bumpVerticalPos(actor.height / 2);
|
||||||
|
}
|
||||||
|
// if it is a destroy receiver message
|
||||||
|
else if (destroyedActors[msg.to] == index) {
|
||||||
|
const actor = actors[msg.to];
|
||||||
|
if (conf.mirrorActors) {
|
||||||
|
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
|
||||||
|
receiverAdjustment(actor, adjustment);
|
||||||
|
}
|
||||||
|
actor.stopy = lineStartY - actor.height / 2;
|
||||||
|
bounds.bumpVerticalPos(actor.height / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a sequenceDiagram in the tag with id: id based on the graph definition in text.
|
* Draws a sequenceDiagram in the tag with id: id based on the graph definition in text.
|
||||||
*
|
*
|
||||||
@@ -666,8 +771,10 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
|
|
||||||
// Fetch data from the parsing
|
// Fetch data from the parsing
|
||||||
const actors = diagObj.db.getActors();
|
const actors = diagObj.db.getActors();
|
||||||
|
const createdActors = diagObj.db.getCreatedActors();
|
||||||
|
const destroyedActors = diagObj.db.getDestroyedActors();
|
||||||
const boxes = diagObj.db.getBoxes();
|
const boxes = diagObj.db.getBoxes();
|
||||||
const actorKeys = diagObj.db.getActorKeys();
|
let actorKeys = diagObj.db.getActorKeys();
|
||||||
const messages = diagObj.db.getMessages();
|
const messages = diagObj.db.getMessages();
|
||||||
const title = diagObj.db.getDiagramTitle();
|
const title = diagObj.db.getDiagramTitle();
|
||||||
const hasBoxes = diagObj.db.hasAtLeastOneBox();
|
const hasBoxes = diagObj.db.hasAtLeastOneBox();
|
||||||
@@ -686,7 +793,16 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawActors(diagram, actors, actorKeys, 0, conf, messages, false);
|
if (conf.hideUnusedParticipants === true) {
|
||||||
|
const newActors = new Set();
|
||||||
|
messages.forEach((message) => {
|
||||||
|
newActors.add(message.from);
|
||||||
|
newActors.add(message.to);
|
||||||
|
});
|
||||||
|
actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false);
|
||||||
const loopWidths = calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj);
|
const loopWidths = calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj);
|
||||||
|
|
||||||
// The arrow head definition is attached to the svg once
|
// The arrow head definition is attached to the svg once
|
||||||
@@ -720,7 +836,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
let sequenceIndex = 1;
|
let sequenceIndex = 1;
|
||||||
let sequenceIndexStep = 1;
|
let sequenceIndexStep = 1;
|
||||||
const messagesToDraw = [];
|
const messagesToDraw = [];
|
||||||
messages.forEach(function (msg) {
|
const backgrounds = [];
|
||||||
|
messages.forEach(function (msg, index) {
|
||||||
let loopModel, noteModel, msgModel;
|
let loopModel, noteModel, msgModel;
|
||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
@@ -757,7 +874,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.RECT_END:
|
case diagObj.db.LINETYPE.RECT_END:
|
||||||
loopModel = bounds.endLoop();
|
loopModel = bounds.endLoop();
|
||||||
svgDraw.drawBackgroundRect(diagram, loopModel);
|
backgrounds.push(loopModel);
|
||||||
bounds.models.addLoop(loopModel);
|
bounds.models.addLoop(loopModel);
|
||||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||||
break;
|
break;
|
||||||
@@ -876,13 +993,20 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
try {
|
try {
|
||||||
// lastMsg = msg
|
|
||||||
bounds.resetVerticalPos();
|
|
||||||
msgModel = msg.msgModel;
|
msgModel = msg.msgModel;
|
||||||
msgModel.starty = bounds.getVerticalPos();
|
msgModel.starty = bounds.getVerticalPos();
|
||||||
msgModel.sequenceIndex = sequenceIndex;
|
msgModel.sequenceIndex = sequenceIndex;
|
||||||
msgModel.sequenceVisible = diagObj.db.showSequenceNumbers();
|
msgModel.sequenceVisible = diagObj.db.showSequenceNumbers();
|
||||||
const lineStartY = boundMessage(diagram, msgModel);
|
const lineStartY = boundMessage(diagram, msgModel);
|
||||||
|
adjustCreatedDestroyedData(
|
||||||
|
msg,
|
||||||
|
msgModel,
|
||||||
|
lineStartY,
|
||||||
|
index,
|
||||||
|
actors,
|
||||||
|
createdActors,
|
||||||
|
destroyedActors
|
||||||
|
);
|
||||||
messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY });
|
messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY });
|
||||||
bounds.models.addMessage(msgModel);
|
bounds.models.addMessage(msgModel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -907,15 +1031,16 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj));
|
log.debug('createdActors', createdActors);
|
||||||
|
log.debug('destroyedActors', destroyedActors);
|
||||||
|
|
||||||
|
drawActors(diagram, actors, actorKeys, false);
|
||||||
|
messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj));
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
// Draw actors below diagram
|
drawActors(diagram, actors, actorKeys, true);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin * 2);
|
|
||||||
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos(), conf, messages, true);
|
|
||||||
bounds.bumpVerticalPos(conf.boxMargin);
|
|
||||||
fixLifeLineHeights(diagram, bounds.getVerticalPos());
|
|
||||||
}
|
}
|
||||||
|
backgrounds.forEach((e) => svgDraw.drawBackgroundRect(diagram, e));
|
||||||
|
fixLifeLineHeights(diagram, actors, actorKeys, conf);
|
||||||
|
|
||||||
bounds.models.boxes.forEach(function (box) {
|
bounds.models.boxes.forEach(function (box) {
|
||||||
box.height = bounds.getVerticalPos() - box.y;
|
box.height = bounds.getVerticalPos() - box.y;
|
||||||
@@ -937,11 +1062,6 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
|
|
||||||
const { bounds: box } = bounds.getBounds();
|
const { bounds: box } = bounds.getBounds();
|
||||||
|
|
||||||
// Adjust line height of actor lines now that the height of the diagram is known
|
|
||||||
log.debug('For line height fix Querying: #' + id + ' .actor-line');
|
|
||||||
const actorLines = selectAll('#' + id + ' .actor-line');
|
|
||||||
actorLines.attr('y2', box.stopy);
|
|
||||||
|
|
||||||
// Make sure the height of the diagram supports long menus.
|
// Make sure the height of the diagram supports long menus.
|
||||||
let boxHeight = box.stopy - box.starty;
|
let boxHeight = box.stopy - box.starty;
|
||||||
if (boxHeight < requiredBoxSize.maxHeight) {
|
if (boxHeight < requiredBoxSize.maxHeight) {
|
||||||
|
@@ -4,6 +4,8 @@ import { addFunction } from '../../interactionDb.js';
|
|||||||
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
|
||||||
|
export const ACTOR_TYPE_WIDTH = 18 * 2;
|
||||||
|
|
||||||
export const drawRect = function (elem, rectData) {
|
export const drawRect = function (elem, rectData) {
|
||||||
return svgDrawCommon.drawRect(elem, rectData);
|
return svgDrawCommon.drawRect(elem, rectData);
|
||||||
};
|
};
|
||||||
@@ -294,14 +296,19 @@ export const drawLabel = function (elem, txtObject) {
|
|||||||
|
|
||||||
let actorCnt = -1;
|
let actorCnt = -1;
|
||||||
|
|
||||||
export const fixLifeLineHeights = (diagram, bounds) => {
|
export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => {
|
||||||
if (!diagram.selectAll) {
|
if (!diagram.select) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
diagram
|
actorKeys.forEach((actorKey) => {
|
||||||
.selectAll('.actor-line')
|
const actor = actors[actorKey];
|
||||||
.attr('class', '200')
|
const actorDOM = diagram.select('#actor' + actor.actorCnt);
|
||||||
.attr('y2', bounds - 55);
|
if (!conf.mirrorActors && actor.stopy) {
|
||||||
|
actorDOM.attr('y2', actor.stopy + actor.height / 2);
|
||||||
|
} else if (conf.mirrorActors) {
|
||||||
|
actorDOM.attr('y2', actor.stopy);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -313,10 +320,11 @@ export const fixLifeLineHeights = (diagram, bounds) => {
|
|||||||
* @param {boolean} isFooter - If the actor is the footer one
|
* @param {boolean} isFooter - If the actor is the footer one
|
||||||
*/
|
*/
|
||||||
const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
const center = actor.x + actor.width / 2;
|
const center = actor.x + actor.width / 2;
|
||||||
const centerY = actor.y + 5;
|
const centerY = actorY + 5;
|
||||||
|
|
||||||
const boxpluslineGroup = elem.append('g');
|
const boxpluslineGroup = elem.append('g').lower();
|
||||||
var g = boxpluslineGroup;
|
var g = boxpluslineGroup;
|
||||||
|
|
||||||
if (!isFooter) {
|
if (!isFooter) {
|
||||||
@@ -328,6 +336,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
|||||||
.attr('x2', center)
|
.attr('x2', center)
|
||||||
.attr('y2', 2000)
|
.attr('y2', 2000)
|
||||||
.attr('class', 'actor-line')
|
.attr('class', 'actor-line')
|
||||||
|
.attr('class', '200')
|
||||||
.attr('stroke-width', '0.5px')
|
.attr('stroke-width', '0.5px')
|
||||||
.attr('stroke', '#999');
|
.attr('stroke', '#999');
|
||||||
|
|
||||||
@@ -348,7 +357,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
|||||||
rect.fill = '#eaeaea';
|
rect.fill = '#eaeaea';
|
||||||
}
|
}
|
||||||
rect.x = actor.x;
|
rect.x = actor.x;
|
||||||
rect.y = actor.y;
|
rect.y = actorY;
|
||||||
rect.width = actor.width;
|
rect.width = actor.width;
|
||||||
rect.height = actor.height;
|
rect.height = actor.height;
|
||||||
rect.class = cssclass;
|
rect.class = cssclass;
|
||||||
@@ -388,8 +397,11 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
const center = actor.x + actor.width / 2;
|
const center = actor.x + actor.width / 2;
|
||||||
const centerY = actor.y + 80;
|
const centerY = actorY + 80;
|
||||||
|
|
||||||
|
elem.lower();
|
||||||
|
|
||||||
if (!isFooter) {
|
if (!isFooter) {
|
||||||
actorCnt++;
|
actorCnt++;
|
||||||
@@ -401,15 +413,18 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
|||||||
.attr('x2', center)
|
.attr('x2', center)
|
||||||
.attr('y2', 2000)
|
.attr('y2', 2000)
|
||||||
.attr('class', 'actor-line')
|
.attr('class', 'actor-line')
|
||||||
|
.attr('class', '200')
|
||||||
.attr('stroke-width', '0.5px')
|
.attr('stroke-width', '0.5px')
|
||||||
.attr('stroke', '#999');
|
.attr('stroke', '#999');
|
||||||
|
|
||||||
|
actor.actorCnt = actorCnt;
|
||||||
}
|
}
|
||||||
const actElem = elem.append('g');
|
const actElem = elem.append('g');
|
||||||
actElem.attr('class', 'actor-man');
|
actElem.attr('class', 'actor-man');
|
||||||
|
|
||||||
const rect = svgDrawCommon.getNoteRect();
|
const rect = svgDrawCommon.getNoteRect();
|
||||||
rect.x = actor.x;
|
rect.x = actor.x;
|
||||||
rect.y = actor.y;
|
rect.y = actorY;
|
||||||
rect.fill = '#eaeaea';
|
rect.fill = '#eaeaea';
|
||||||
rect.width = actor.width;
|
rect.width = actor.width;
|
||||||
rect.height = actor.height;
|
rect.height = actor.height;
|
||||||
@@ -421,33 +436,33 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
|||||||
.append('line')
|
.append('line')
|
||||||
.attr('id', 'actor-man-torso' + actorCnt)
|
.attr('id', 'actor-man-torso' + actorCnt)
|
||||||
.attr('x1', center)
|
.attr('x1', center)
|
||||||
.attr('y1', actor.y + 25)
|
.attr('y1', actorY + 25)
|
||||||
.attr('x2', center)
|
.attr('x2', center)
|
||||||
.attr('y2', actor.y + 45);
|
.attr('y2', actorY + 45);
|
||||||
|
|
||||||
actElem
|
actElem
|
||||||
.append('line')
|
.append('line')
|
||||||
.attr('id', 'actor-man-arms' + actorCnt)
|
.attr('id', 'actor-man-arms' + actorCnt)
|
||||||
.attr('x1', center - 18)
|
.attr('x1', center - ACTOR_TYPE_WIDTH / 2)
|
||||||
.attr('y1', actor.y + 33)
|
.attr('y1', actorY + 33)
|
||||||
.attr('x2', center + 18)
|
.attr('x2', center + ACTOR_TYPE_WIDTH / 2)
|
||||||
.attr('y2', actor.y + 33);
|
.attr('y2', actorY + 33);
|
||||||
actElem
|
actElem
|
||||||
.append('line')
|
.append('line')
|
||||||
.attr('x1', center - 18)
|
.attr('x1', center - ACTOR_TYPE_WIDTH / 2)
|
||||||
.attr('y1', actor.y + 60)
|
.attr('y1', actorY + 60)
|
||||||
.attr('x2', center)
|
.attr('x2', center)
|
||||||
.attr('y2', actor.y + 45);
|
.attr('y2', actorY + 45);
|
||||||
actElem
|
actElem
|
||||||
.append('line')
|
.append('line')
|
||||||
.attr('x1', center)
|
.attr('x1', center)
|
||||||
.attr('y1', actor.y + 45)
|
.attr('y1', actorY + 45)
|
||||||
.attr('x2', center + 16)
|
.attr('x2', center + ACTOR_TYPE_WIDTH / 2 - 2)
|
||||||
.attr('y2', actor.y + 60);
|
.attr('y2', actorY + 60);
|
||||||
|
|
||||||
const circle = actElem.append('circle');
|
const circle = actElem.append('circle');
|
||||||
circle.attr('cx', actor.x + actor.width / 2);
|
circle.attr('cx', actor.x + actor.width / 2);
|
||||||
circle.attr('cy', actor.y + 10);
|
circle.attr('cy', actorY + 10);
|
||||||
circle.attr('r', 15);
|
circle.attr('r', 15);
|
||||||
circle.attr('width', actor.width);
|
circle.attr('width', actor.width);
|
||||||
circle.attr('height', actor.height);
|
circle.attr('height', actor.height);
|
||||||
|
Reference in New Issue
Block a user