Merge branch 'release/8.13.0'

This commit is contained in:
Knut Sveidqvist
2021-09-23 18:59:51 +02:00
66 changed files with 3289 additions and 62911 deletions

View File

@@ -274,7 +274,7 @@ export const drawClass = function (elem, classDef, conf) {
};
export const parseMember = function (text) {
const fieldRegEx = /(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+)/;
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+) *(\*|\$)?$/;
const methodRegEx = /^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/;
let fieldMatch = text.match(fieldRegEx);
@@ -290,6 +290,7 @@ export const parseMember = function (text) {
};
const buildFieldDisplay = function (parsedText) {
let cssStyle = '';
let displayText = '';
try {
@@ -297,15 +298,17 @@ const buildFieldDisplay = function (parsedText) {
let fieldType = parsedText[2] ? parsedText[2].trim() : '';
let genericType = parsedText[3] ? parseGenericTypes(parsedText[3].trim()) : '';
let fieldName = parsedText[4] ? parsedText[4].trim() : '';
let classifier = parsedText[5] ? parsedText[5].trim() : '';
displayText = visibility + fieldType + genericType + ' ' + fieldName;
cssStyle = parseClassifier(classifier);
} catch (err) {
displayText = parsedText;
}
return {
displayText: displayText,
cssStyle: '',
cssStyle: cssStyle,
};
};
@@ -321,7 +324,6 @@ const buildMethodDisplay = function (parsedText) {
let returnType = parsedText[5] ? ' : ' + parseGenericTypes(parsedText[5]).trim() : '';
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
cssStyle = parseClassifier(classifier);
} catch (err) {
displayText = parsedText;

View File

@@ -51,7 +51,7 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe('');
});
it('should handle abstract classifier', function () {
it('should handle abstract method classifier', function () {
const str = 'foo()*';
let actual = svgDraw.parseMember(str);
@@ -59,7 +59,7 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe('font-style:italic;');
});
it('should handle static classifier', function () {
it('should handle static method classifier', function () {
const str = 'foo()$';
let actual = svgDraw.parseMember(str);
@@ -156,5 +156,13 @@ describe('class member Renderer, ', function () {
expect(actual.displayText).toBe('List<int> ids');
expect(actual.cssStyle).toBe('');
});
it('should handle static field classifier', function () {
const str = 'String foo$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('String foo');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
});
});

View File

@@ -35,13 +35,20 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
const attrFontSize = conf.fontSize * 0.85;
const labelBBox = entityTextNode.node().getBBox();
const attributeNodes = []; // Intermediate storage for attribute nodes created so that we can do a second pass
let hasKeyType = false;
let hasComment = false;
let maxWidth = 0;
let maxTypeWidth = 0;
let maxNameWidth = 0;
let maxKeyWidth = 0;
let maxCommentWidth = 0;
let cumulativeHeight = labelBBox.height + heightPadding * 2;
let attrNum = 1;
attributes.forEach((item) => {
const attrPrefix = `${entityTextNode.node().id}-attr-${attrNum}`;
let nodeWidth = 0;
let nodeHeight = 0;
// Add a text node for the attribute type
const typeNode = groupNode
@@ -73,16 +80,70 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
)
.text(item.attributeName);
// Keep a reference to the nodes so that we can iterate through them later
attributeNodes.push({ tn: typeNode, nn: nameNode });
const attributeNode = {};
attributeNode.tn = typeNode;
attributeNode.nn = nameNode;
const typeBBox = typeNode.node().getBBox();
const nameBBox = nameNode.node().getBBox();
maxTypeWidth = Math.max(maxTypeWidth, typeBBox.width);
maxNameWidth = Math.max(maxNameWidth, nameBBox.width);
nodeWidth += typeBBox.width;
nodeWidth += nameBBox.width;
cumulativeHeight += Math.max(typeBBox.height, nameBBox.height) + heightPadding * 2;
nodeHeight = Math.max(typeBBox.height, nameBBox.height);
if (hasKeyType || item.attributeKeyType !== undefined) {
const keyTypeNode = groupNode
.append('text')
.attr('class', 'er entityLabel')
.attr('id', `${attrPrefix}-name`)
.attr('x', 0)
.attr('y', 0)
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'left')
.attr(
'style',
'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px'
)
.text(item.attributeKeyType || '');
attributeNode.kn = keyTypeNode;
const keyTypeBBox = keyTypeNode.node().getBBox();
nodeWidth += keyTypeBBox.width;
maxKeyWidth = Math.max(maxKeyWidth, nodeWidth);
nodeHeight = Math.max(nodeHeight, keyTypeBBox.height);
hasKeyType = true;
}
if (hasComment || item.attributeComment !== undefined) {
const commentNode = groupNode
.append('text')
.attr('class', 'er entityLabel')
.attr('id', `${attrPrefix}-name`)
.attr('x', 0)
.attr('y', 0)
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'left')
.attr(
'style',
'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px'
)
.text(item.attributeComment || '');
attributeNode.cn = commentNode;
const commentNodeBBox = commentNode.node().getBBox();
nodeWidth += commentNodeBBox.width;
maxCommentWidth = Math.max(nodeWidth, nameBBox.width);
nodeHeight = Math.max(nodeHeight, commentNodeBBox.height);
hasComment = true;
}
attributeNode.height = nodeHeight;
// Keep a reference to the nodes so that we can iterate through them later
attributeNodes.push(attributeNode);
maxWidth = Math.max(maxWidth, nodeWidth);
cumulativeHeight += nodeHeight + heightPadding * 2;
attrNum += 1;
});
@@ -90,10 +151,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
const bBox = {
width: Math.max(
conf.minEntityWidth,
Math.max(
labelBBox.width + conf.entityPadding * 2,
maxTypeWidth + maxNameWidth + widthPadding * 4
)
Math.max(labelBBox.width + conf.entityPadding * 2, maxWidth + widthPadding * 4)
),
height:
attributes.length > 0
@@ -102,7 +160,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
};
// There might be some spare width for padding out attributes if the entity name is very long
const spareWidth = Math.max(0, bBox.width - (maxTypeWidth + maxNameWidth) - widthPadding * 4);
const spareWidth = Math.max(0, bBox.width - maxWidth - widthPadding * 4);
if (attributes.length > 0) {
// Position the entity label near the top of the entity bounding box
@@ -115,51 +173,85 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
let heightOffset = labelBBox.height + heightPadding * 2; // Start at the bottom of the entity label
let attribStyle = 'attributeBoxOdd'; // We will flip the style on alternate rows to achieve a banded effect
attributeNodes.forEach((nodePair) => {
attributeNodes.forEach((attributeNode) => {
// Calculate the alignment y co-ordinate for the type/name of the attribute
const alignY =
heightOffset +
heightPadding +
Math.max(nodePair.tn.node().getBBox().height, nodePair.nn.node().getBBox().height) / 2;
const alignY = heightOffset + heightPadding + attributeNode.height / 2;
// Position the type of the attribute
nodePair.tn.attr('transform', 'translate(' + widthPadding + ',' + alignY + ')');
attributeNode.tn.attr('transform', 'translate(' + widthPadding + ',' + alignY + ')');
// Insert a rectangle for the type
const typeRect = groupNode
.insert('rect', '#' + nodePair.tn.node().id)
.insert('rect', '#' + attributeNode.tn.node().id)
.attr('class', `er ${attribStyle}`)
.attr('fill', conf.fill)
.attr('fill-opacity', '100%')
.attr('stroke', conf.stroke)
.attr('x', 0)
.attr('y', heightOffset)
.attr('width', maxTypeWidth + widthPadding * 2 + spareWidth / 2)
.attr('height', nodePair.tn.node().getBBox().height + heightPadding * 2);
.attr('width', maxTypeWidth * 2 + spareWidth / 2)
.attr('height', attributeNode.tn.node().getBBox().height + heightPadding * 2);
// Position the name of the attribute
nodePair.nn.attr(
attributeNode.nn.attr(
'transform',
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
);
// Insert a rectangle for the name
groupNode
.insert('rect', '#' + nodePair.nn.node().id)
.insert('rect', '#' + attributeNode.nn.node().id)
.attr('class', `er ${attribStyle}`)
.attr('fill', conf.fill)
.attr('fill-opacity', '100%')
.attr('stroke', conf.stroke)
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
//.attr('x', maxTypeWidth + (widthPadding * 2))
.attr('y', heightOffset)
.attr('width', maxNameWidth + widthPadding * 2 + spareWidth / 2)
.attr('height', nodePair.nn.node().getBBox().height + heightPadding * 2);
.attr('height', attributeNode.nn.node().getBBox().height + heightPadding * 2);
if (hasKeyType) {
// Position the name of the attribute
attributeNode.kn.attr(
'transform',
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
);
// Insert a rectangle for the name
groupNode
.insert('rect', '#' + attributeNode.kn.node().id)
.attr('class', `er ${attribStyle}`)
.attr('fill', conf.fill)
.attr('fill-opacity', '100%')
.attr('stroke', conf.stroke)
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
.attr('y', heightOffset)
.attr('width', maxKeyWidth + widthPadding * 2 + spareWidth / 2)
.attr('height', attributeNode.kn.node().getBBox().height + heightPadding * 2);
}
if (hasComment) {
// Position the name of the attribute
attributeNode.cn.attr(
'transform',
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
);
// Insert a rectangle for the name
groupNode
.insert('rect', '#' + attributeNode.cn.node().id)
.attr('class', `er ${attribStyle}`)
.attr('fill', conf.fill)
.attr('fill-opacity', '100%')
.attr('stroke', conf.stroke)
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
.attr('y', heightOffset)
.attr('width', maxCommentWidth + widthPadding * 2 + spareWidth / 2)
.attr('height', attributeNode.cn.node().getBBox().height + heightPadding * 2);
}
// Increment the height offset to move to the next row
heightOffset +=
Math.max(nodePair.tn.node().getBBox().height, nodePair.nn.node().getBBox().height) +
heightPadding * 2;
heightOffset += attributeNode.height + heightPadding * 2;
// Flip the attribute style for row banding
attribStyle = attribStyle == 'attributeBoxOdd' ? 'attributeBoxEven' : 'attributeBoxOdd';

View File

@@ -18,7 +18,9 @@
"erDiagram" return 'ER_DIAGRAM';
"{" { this.begin("block"); return 'BLOCK_START'; }
<block>\s+ /* skip whitespace in block */
<block>[A-Za-z][A-Za-z0-9\-_]* { return 'ATTRIBUTE_WORD'; }
<block>(?:PK)|(?:FK) return 'ATTRIBUTE_KEY'
<block>[A-Za-z][A-Za-z0-9\-_]* return 'ATTRIBUTE_WORD'
<block>\"[^"]*\" return 'COMMENT';
<block>[\n]+ /* nothing */
<block>"}" { this.popState(); return 'BLOCK_STOP'; }
<block>. return yytext[0];
@@ -95,6 +97,9 @@ attributes
attribute
: attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; }
| attributeType attributeName attributeKeyType { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3 }; }
| attributeType attributeName COMMENT { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; }
| attributeType attributeName attributeKeyType COMMENT { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3, attributeComment: $4 }; }
;
attributeType
@@ -105,6 +110,10 @@ attributeName
: ATTRIBUTE_WORD { $$=$1; }
;
attributeKeyType
: ATTRIBUTE_KEY { $$=$1; }
;
relSpec
: cardinality relType cardinality
{

View File

@@ -42,6 +42,36 @@ describe('when parsing ER diagram it...', function () {
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow an entity with a single attribute to be defined with a key', function () {
const entity = 'BOOK';
const attribute = 'string title PK';
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
const entities = erDb.getEntities();
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow an entity with a single attribute to be defined with a comment', function () {
const entity = 'BOOK';
const attribute = `string title "comment"`;
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
const entities = erDb.getEntities();
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow an entity with a single attribute to be defined with a key and a comment', function () {
const entity = 'BOOK';
const attribute = `string title PK "comment"`;
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
const entities = erDb.getEntities();
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow an entity with multiple attributes to be defined', function () {
const entity = 'BOOK';
const attribute1 = 'string title';

View File

@@ -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; }

View File

@@ -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);

View File

@@ -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');
@@ -1452,7 +1501,7 @@ participant Alice`;
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + conf.boxMargin);
});
});
});
@@ -1501,7 +1550,7 @@ participant Alice
expect(bounds.startx).toBe(0);
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin);
});
it('it should handle one actor, when logLevel is 3', function() {
const str = `
@@ -1519,6 +1568,6 @@ participant Alice
expect(bounds.startx).toBe(0);
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin);
});
});

View File

@@ -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);
};
export const setConf = function (cnf) {
@@ -688,6 +689,8 @@ export const draw = function (text, id) {
// Draw actors below diagram
bounds.bumpVerticalPos(conf.boxMargin * 2);
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos());
bounds.bumpVerticalPos(conf.boxMargin);
fixLifeLineHeights(diagram, bounds.getVerticalPos());
}
const { bounds: box } = bounds.getBounds();

View File

@@ -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;

View File

@@ -181,13 +181,22 @@ export const drawLabel = function (elem, txtObject) {
};
let actorCnt = -1;
export const fixLifeLineHeights = (diagram, bounds) => {
if (!diagram.selectAll) return;
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');
@@ -213,7 +222,7 @@ export const drawActor = function (elem, actor, conf) {
rect.class = 'actor';
rect.rx = 3;
rect.ry = 3;
drawRect(g, rect);
const rectElem = drawRect(g, rect);
_drawTextCandidateFunc(conf)(
actor.description,
@@ -225,6 +234,105 @@ export const drawActor = function (elem, actor, conf) {
{ class: 'actor' },
conf
);
let height = actor.height;
if (rectElem.node) {
const bounds = rectElem.node().getBBox();
actor.height = bounds.height;
height = bounds.height;
}
return height;
};
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);
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 actor.height;
};
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 +684,5 @@ export default {
insertArrowCrossHead,
getTextObj,
getNoteRect,
fixLifeLineHeights,
};