mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
#2315 Adding the possibility to add actor figures as participants
This commit is contained in:
parent
0d91eee5e0
commit
6ce1c80a47
@ -10,7 +10,7 @@
|
||||
<style>
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
background:#333;
|
||||
/* background:#333; */
|
||||
font-family: 'Arial';
|
||||
/* font-size: 18px !important; */
|
||||
}
|
||||
@ -53,13 +53,20 @@ stateDiagram
|
||||
|
||||
</div>
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
flowchart LR
|
||||
one --> two
|
||||
three -.-> four[whoa, big arrowhead nine o'clock]
|
||||
sequenceDiagram
|
||||
actor Alice as Alice2
|
||||
actor Bob
|
||||
participant John as John2
|
||||
participant Mandy
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
Alice->>John: Hi John
|
||||
John->>Mandy: Hi Mandy
|
||||
Mandy ->>Joan: Hi Joan
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
%%{init: { "apa":"b", "theme":"forest"}}%%
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
%%{int: { "apa":"b", "theme":"forest"}}%%
|
||||
sequenceDiagram
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
@ -112,8 +119,8 @@ YourState
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
theme: 'dark',
|
||||
// theme: 'forest',
|
||||
// theme: 'dark',
|
||||
theme: 'forest',
|
||||
arrowMarkerAbsolute: true,
|
||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||
logLevel: 2,
|
||||
|
@ -33,6 +33,7 @@
|
||||
\%%(?!\{)[^\n]* /* skip comments */
|
||||
[^\}]\%\%[^\n]* /* skip comments */
|
||||
"participant" { this.begin('ID'); return 'participant'; }
|
||||
"actor" { this.begin('ID'); return 'participant_actor'; }
|
||||
<ID>[^\->:\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>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
|
||||
@ -103,8 +104,10 @@ directive
|
||||
;
|
||||
|
||||
statement
|
||||
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.description=yy.parseMessage($4); $$=$2;}
|
||||
| 'participant' actor 'NEWLINE' {$$=$2;}
|
||||
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
||||
| 'participant' actor 'NEWLINE' {$2.type='addParticipant';$$=$2;}
|
||||
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.type='addActor';$2.description=yy.parseMessage($4); $$=$2;}
|
||||
| 'participant_actor' actor 'NEWLINE' {$2.type='addActor'; $$=$2;}
|
||||
| signal 'NEWLINE'
|
||||
| autonumber {yy.enableSequenceNumbers()}
|
||||
| 'activate' actor 'NEWLINE' {$$={type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $2};}
|
||||
@ -197,9 +200,13 @@ signal
|
||||
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
||||
;
|
||||
|
||||
actor
|
||||
: ACTOR {$$={type: 'addActor', actor:$1}}
|
||||
;
|
||||
// actor
|
||||
// : actor_participant
|
||||
// | actor_actor
|
||||
// ;
|
||||
|
||||
actor: ACTOR {$$={ type: 'addParticipant', actor:$1}};
|
||||
// actor_actor: ACTOR {$$={type: 'addActor', actor:$1}};
|
||||
|
||||
signaltype
|
||||
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
||||
|
@ -15,14 +15,17 @@ export const parseDirective = function (statement, context, type) {
|
||||
mermaidAPI.parseDirective(this, statement, context, type);
|
||||
};
|
||||
|
||||
export const addActor = function (id, name, description) {
|
||||
export const addActor = function (id, name, description, type) {
|
||||
// Don't allow description nulling
|
||||
const old = actors[id];
|
||||
if (old && name === old.name && description == null) return;
|
||||
|
||||
// Don't allow null descriptions, either
|
||||
if (description == null || description.text == null) {
|
||||
description = { text: name, wrap: null };
|
||||
description = { text: name, wrap: null, type };
|
||||
}
|
||||
if (type == null || description.text == null) {
|
||||
description = { text: name, wrap: null, type };
|
||||
}
|
||||
|
||||
actors[id] = {
|
||||
@ -30,6 +33,7 @@ export const addActor = function (id, name, description) {
|
||||
description: description.text,
|
||||
wrap: (description.wrap === undefined && autoWrap()) || !!description.wrap,
|
||||
prevActor: prevActor,
|
||||
type: type || 'participant'
|
||||
};
|
||||
if (prevActor && actors[prevActor]) {
|
||||
actors[prevActor].nextActor = id;
|
||||
@ -218,8 +222,11 @@ export const apply = function (param) {
|
||||
});
|
||||
} else {
|
||||
switch (param.type) {
|
||||
case 'addParticipant':
|
||||
addActor(param.actor, param.actor, param.description, 'participant');
|
||||
break;
|
||||
case 'addActor':
|
||||
addActor(param.actor, param.actor, param.description);
|
||||
addActor(param.actor, param.actor, param.description, 'actor');
|
||||
break;
|
||||
case 'activeStart':
|
||||
addSignal(param.actor, undefined, undefined, param.signalType);
|
||||
|
@ -121,6 +121,55 @@ B-->A: I am good thanks!`;
|
||||
|
||||
mermaidAPI.parse(str);
|
||||
|
||||
const actors = parser.yy.getActors();
|
||||
|
||||
expect(Object.keys(actors)).toEqual(['A', 'B']);
|
||||
expect(actors.A.description).toBe('Alice');
|
||||
expect(actors.B.description).toBe('Bob');
|
||||
|
||||
const messages = parser.yy.getMessages();
|
||||
expect(messages.length).toBe(2);
|
||||
expect(messages[0].from).toBe('A');
|
||||
expect(messages[1].from).toBe('B');
|
||||
});
|
||||
it('it should alias a mix of actors and participants apa12', function() {
|
||||
const str = `
|
||||
sequenceDiagram
|
||||
actor Alice as Alice2
|
||||
actor Bob
|
||||
participant John as John2
|
||||
participant Mandy
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
Alice->>John: Hi John
|
||||
John->>Mandy: Hi Mandy
|
||||
Mandy ->>Joan: Hi Joan`;
|
||||
|
||||
mermaidAPI.parse(str);
|
||||
|
||||
const actors = parser.yy.getActors();
|
||||
expect(Object.keys(actors)).toEqual(['Alice', 'Bob', 'John', 'Mandy', 'Joan']);
|
||||
expect(actors.Alice.description).toBe('Alice2');
|
||||
expect(actors.Alice.type).toBe('actor');
|
||||
expect(actors.Bob.description).toBe('Bob');
|
||||
expect(actors.John.type).toBe('participant');
|
||||
expect(actors.Joan.type).toBe('participant');
|
||||
|
||||
const messages = parser.yy.getMessages();
|
||||
expect(messages.length).toBe(5);
|
||||
expect(messages[0].from).toBe('Alice');
|
||||
expect(messages[4].to).toBe('Joan');
|
||||
});
|
||||
it('it should alias actors apa13', function() {
|
||||
const str = `
|
||||
sequenceDiagram
|
||||
actor A as Alice
|
||||
actor B as Bob
|
||||
A->B:Hello Bob, how are you?
|
||||
B-->A: I am good thanks!`;
|
||||
|
||||
mermaidAPI.parse(str);
|
||||
|
||||
const actors = parser.yy.getActors();
|
||||
expect(Object.keys(actors)).toEqual(['A', 'B']);
|
||||
expect(actors.A.description).toBe('Alice');
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { select, selectAll } from 'd3';
|
||||
import svgDraw, { drawText } from './svgDraw';
|
||||
import svgDraw, { drawText, fixLifeLineHeights } from './svgDraw';
|
||||
import { log } from '../../logger';
|
||||
import { parser } from './parser/sequenceDiagram';
|
||||
import common from '../common/common';
|
||||
@ -421,7 +421,7 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
||||
// Draw the actors
|
||||
let prevWidth = 0;
|
||||
let prevMargin = 0;
|
||||
|
||||
let maxHeight = 0;
|
||||
for (let i = 0; i < actorKeys.length; i++) {
|
||||
const actor = actors[actorKeys[i]];
|
||||
|
||||
@ -434,7 +434,8 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
||||
actor.y = verticalPos;
|
||||
|
||||
// Draw the box with the attached line
|
||||
svgDraw.drawActor(diagram, actor, conf);
|
||||
const height = svgDraw.drawActor(diagram, actor, conf);
|
||||
maxHeight = Math.max(maxHeight, height);
|
||||
bounds.insert(actor.x, verticalPos, actor.x + actor.width, actor.height);
|
||||
|
||||
prevWidth += actor.width;
|
||||
@ -443,7 +444,7 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
||||
}
|
||||
|
||||
// Add a margin between the actor boxes and the first arrow
|
||||
bounds.bumpVerticalPos(conf.height);
|
||||
bounds.bumpVerticalPos(maxHeight-conf.boxMargin);
|
||||
};
|
||||
|
||||
export const setConf = function (cnf) {
|
||||
@ -688,6 +689,7 @@ export const draw = function (text, id) {
|
||||
// Draw actors below diagram
|
||||
bounds.bumpVerticalPos(conf.boxMargin * 2);
|
||||
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos());
|
||||
fixLifeLineHeights(diagram, bounds.getVerticalPos());
|
||||
}
|
||||
|
||||
const { bounds: box } = bounds.getBounds();
|
||||
|
@ -95,6 +95,15 @@ const getStyles = (options) =>
|
||||
fill: ${options.activationBkgColor};
|
||||
stroke: ${options.activationBorderColor};
|
||||
}
|
||||
.actor-man line {
|
||||
stroke: ${options.actorBorder};
|
||||
fill: ${options.actorBkg};
|
||||
}
|
||||
.actor-man circle, line {
|
||||
stroke: ${options.actorBorder};
|
||||
fill: ${options.actorBkg};
|
||||
stroke-width: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default getStyles;
|
||||
|
@ -181,13 +181,23 @@ export const drawLabel = function (elem, txtObject) {
|
||||
};
|
||||
|
||||
let actorCnt = -1;
|
||||
|
||||
export const fixLifeLineHeights = (diagram, bounds) => {
|
||||
console.log('fixLifeLineHeights', diagram, bounds);
|
||||
diagram
|
||||
.selectAll(".actor-line")
|
||||
.attr("class", "200") //
|
||||
.attr("y2", bounds-55) //
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws an actor in the diagram with the attached line
|
||||
* @param elem - The diagram we'll draw to.
|
||||
* @param actor - The actor to draw.
|
||||
* @param conf - drawText implementation discriminator object
|
||||
*/
|
||||
export const drawActor = function (elem, actor, conf) {
|
||||
const drawActorTypeParticipant = function (elem, actor, conf) {
|
||||
const center = actor.x + actor.width / 2;
|
||||
|
||||
const g = elem.append('g');
|
||||
@ -225,6 +235,99 @@ export const drawActor = function (elem, actor, conf) {
|
||||
{ class: 'actor' },
|
||||
conf
|
||||
);
|
||||
|
||||
return 75;
|
||||
};
|
||||
|
||||
const drawActorTypeActor = function (elem, actor, conf) {
|
||||
const center = actor.x + actor.width / 2;
|
||||
|
||||
|
||||
if (actor.y === 0) {
|
||||
actorCnt++;
|
||||
elem.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', 80)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999');
|
||||
}
|
||||
const actElem = elem.append('g');
|
||||
actElem.attr('class', 'actor-man');
|
||||
|
||||
const rect = getNoteRect();
|
||||
rect.x = actor.x;
|
||||
rect.y = actor.y;
|
||||
rect.fill = '#eaeaea';
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = 'actor';
|
||||
rect.rx = 3;
|
||||
rect.ry = 3;
|
||||
// drawRect(actElem, rect);
|
||||
|
||||
actElem.append('line')
|
||||
.attr('id', 'actor-man-torso' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', actor.y+25)
|
||||
.attr('x2', center)
|
||||
.attr('y2', actor.y+45);
|
||||
|
||||
actElem.append('line')
|
||||
.attr('id', 'actor-man-arms' + actorCnt)
|
||||
.attr('x1', center-18)
|
||||
.attr('y1', actor.y + 33)
|
||||
.attr('x2', center+18)
|
||||
.attr('y2', actor.y + 33);
|
||||
actElem.append('line')
|
||||
.attr('x1', center-18)
|
||||
.attr('y1', actor.y + 60)
|
||||
.attr('x2', center)
|
||||
.attr('y2', actor.y + 45);
|
||||
actElem.append('line')
|
||||
.attr('x1', center)
|
||||
.attr('y1', actor.y + 45)
|
||||
.attr('x2', center+16)
|
||||
.attr('y2', actor.y + 60);
|
||||
|
||||
|
||||
const circle = actElem.append('circle');
|
||||
circle.attr('cx', actor.x + actor.width/2);
|
||||
circle.attr('cy', actor.y+10);
|
||||
circle.attr('r', 15);
|
||||
circle.attr('width', actor.width);
|
||||
circle.attr('height', actor.height);
|
||||
// circle.attr('rx', rectData.rx);
|
||||
// circle.attr('ry', rectData.ry);
|
||||
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height;
|
||||
|
||||
|
||||
_drawTextCandidateFunc(conf)(
|
||||
actor.description,
|
||||
actElem,
|
||||
rect.x,
|
||||
rect.y + 35,
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: 'actor' },
|
||||
conf
|
||||
);
|
||||
|
||||
return 100;
|
||||
};
|
||||
|
||||
export const drawActor = function (elem, actor, conf) {
|
||||
switch(actor.type) {
|
||||
case 'actor':
|
||||
return drawActorTypeActor(elem, actor, conf);
|
||||
case 'participant':
|
||||
return drawActorTypeParticipant(elem, actor, conf);
|
||||
}
|
||||
};
|
||||
|
||||
export const anchorElement = function (elem) {
|
||||
@ -576,4 +679,5 @@ export default {
|
||||
insertArrowCrossHead,
|
||||
getTextObj,
|
||||
getNoteRect,
|
||||
fixLifeLineHeights,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user