Refactored rendering sequence diagrams
This commit is contained in:
parent
fd7dbaf0a7
commit
3c72d28511
|
@ -122,10 +122,23 @@ context('Sequence diagram', () => {
|
|||
it('should render long actor descriptions', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
%%{init: {'theme': 'dark'}}%%
|
||||
sequenceDiagram
|
||||
participant A as Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
A->>Bob: Hola
|
||||
Bob-->A: Pasten !
|
||||
`,
|
||||
{logLevel: 0}
|
||||
);
|
||||
});
|
||||
it('should render long actor descriptions', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
%%{wrap}%%
|
||||
participant A as Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
A->>Bob: Hola
|
||||
Bob-->A: Pasten !
|
||||
`,
|
||||
{}
|
||||
);
|
||||
|
@ -141,6 +154,17 @@ context('Sequence diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
it('should render long notes wrapped (inline) left of actor', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice->>Bob: Hola
|
||||
Note left of Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render long notes right of actor', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@ -152,6 +176,17 @@ context('Sequence diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
it('should render long notes wrapped (inline) right of actor', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice->>Bob: Hola
|
||||
Note right of Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render long notes over actor', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@ -163,6 +198,17 @@ context('Sequence diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
it('should render long notes wrapped (inline) over actor', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice->>Bob: Hola
|
||||
Note over Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render long messages from an actor to the left to one to the right', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@ -173,6 +219,16 @@ context('Sequence diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
it('should render long messages wrapped (inline) from an actor to the left to one to the right', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice->>Bob:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render long messages from an actor to the right to one to the left', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@ -183,6 +239,16 @@ context('Sequence diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
it('should render long messages wrapped (inline) from an actor to the right to one to the left', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice->>Bob: I'm short
|
||||
Bob->>Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
context('background rects', () => {
|
||||
it('should render a single and nested rects', () => {
|
||||
|
@ -327,5 +393,33 @@ context('Sequence diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
it('should render dark theme from init directive and size 24 font set from config directive', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
%%{init: {'theme': 'dark'}}%%
|
||||
sequenceDiagram
|
||||
%%{config: {'fontSize': 24}}%%
|
||||
Alice->>John: Hello John, how are you?
|
||||
Alice->>John: John, can you hear me?
|
||||
John-->>Alice: Hi Alice, I can hear you!
|
||||
John-->>Alice: I feel great!
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render with wrapping enabled', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
%%{wrap}%%
|
||||
participant A as Alice, the talkative one
|
||||
A->>John: Hello John, how are you today? I'm feeling quite verbose today.
|
||||
A->>John: John, can you hear me? If you are not available, we can talk later.
|
||||
John-->>A: Hi Alice, I can hear you! I was finishing up an important meeting.
|
||||
John-->>A: I feel great! I was not ignoring you. I am sorry you had to wait for a response.
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -454,12 +454,11 @@ mermaidAPI.initialize({
|
|||
### Parameters
|
||||
|
||||
- `id` the id of the element to be rendered
|
||||
- `_txt`
|
||||
- `_txt` the graph definition
|
||||
- `cb` callback which is called after rendering is finished with the svg code as inparam.
|
||||
- `container` selector to element in which a div with the graph temporarily will be inserted. In one is
|
||||
provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
||||
completed.
|
||||
- `txt` the graph definition
|
||||
|
||||
##
|
||||
|
||||
|
|
|
@ -1,24 +1,10 @@
|
|||
let config = {};
|
||||
import utils from './utils';
|
||||
|
||||
const config = {};
|
||||
|
||||
const setConf = function(cnf) {
|
||||
// Top level initially mermaid, gflow, sequenceDiagram and gantt
|
||||
const lvl1Keys = Object.keys(cnf);
|
||||
for (let i = 0; i < lvl1Keys.length; i++) {
|
||||
if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
|
||||
const lvl2Keys = Object.keys(cnf[lvl1Keys[i]]);
|
||||
|
||||
for (let j = 0; j < lvl2Keys.length; j++) {
|
||||
// logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j])
|
||||
if (typeof config[lvl1Keys[i]] === 'undefined') {
|
||||
config[lvl1Keys[i]] = {};
|
||||
}
|
||||
// logger.debug('Setting config: ' + lvl1Keys[i] + ' ' + lvl2Keys[j] + ' to ' + cnf[lvl1Keys[i]][lvl2Keys[j]])
|
||||
config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]];
|
||||
}
|
||||
} else {
|
||||
config[lvl1Keys[i]] = cnf[lvl1Keys[i]];
|
||||
}
|
||||
}
|
||||
utils.assignWithDepth(config, cnf);
|
||||
};
|
||||
|
||||
export const setConfig = conf => {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { logger } from '../../logger';
|
||||
import { getConfig, setConfig } from '../../config';
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
|
||||
let prevActor = undefined;
|
||||
let actors = {};
|
||||
|
@ -10,7 +8,6 @@ let title = '';
|
|||
let titleWrapped = false;
|
||||
let sequenceNumbersEnabled = false;
|
||||
let wrapEnabled = false;
|
||||
let configUpdated = false;
|
||||
let currentDirective = {};
|
||||
|
||||
export const parseDirective = function(statement, context) {
|
||||
|
@ -46,10 +43,7 @@ const handleDirective = function(directive) {
|
|||
switch (directive.type) {
|
||||
case 'init':
|
||||
case 'initialize':
|
||||
mermaidAPI.initialize(directive.args);
|
||||
break;
|
||||
case 'config':
|
||||
updateConfig(directive.args);
|
||||
logger.debug('init/initialize is handled in mermaid/mermaidAPI');
|
||||
break;
|
||||
case 'wrap':
|
||||
case 'nowrap':
|
||||
|
@ -57,7 +51,9 @@ const handleDirective = function(directive) {
|
|||
break;
|
||||
default:
|
||||
logger.warn(
|
||||
`Unrecognized directive: source: '%%{${directive.type}: ${directive.args}}%%`,
|
||||
`Unhandled directive: source: '%%{${directive.type}: ${JSON.stringify(
|
||||
directive.args ? directive.args : {}
|
||||
)}}%%`,
|
||||
directive
|
||||
);
|
||||
break;
|
||||
|
@ -77,7 +73,7 @@ export const addActor = function(id, name, description) {
|
|||
actors[id] = {
|
||||
name: name,
|
||||
description: description.text,
|
||||
wrap: (description.wrap === null && autoWrap()) || !!description.wrap,
|
||||
wrap: (description.wrap === undefined && autoWrap()) || !!description.wrap,
|
||||
prevActor: prevActor
|
||||
};
|
||||
if (prevActor && actors[prevActor]) {
|
||||
|
@ -111,12 +107,17 @@ export const addMessage = function(idFrom, idTo, message, answer) {
|
|||
from: idFrom,
|
||||
to: idTo,
|
||||
message: message.text,
|
||||
wrap: (message.wrap === null && autoWrap()) || !!message.wrap,
|
||||
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap,
|
||||
answer: answer
|
||||
});
|
||||
};
|
||||
|
||||
export const addSignal = function(idFrom, idTo, message = { text: null, wrap: null }, messageType) {
|
||||
export const addSignal = function(
|
||||
idFrom,
|
||||
idTo,
|
||||
message = { text: undefined, wrap: undefined },
|
||||
messageType
|
||||
) {
|
||||
logger.debug(
|
||||
'Adding message from=' +
|
||||
idFrom +
|
||||
|
@ -150,7 +151,7 @@ export const addSignal = function(idFrom, idTo, message = { text: null, wrap: nu
|
|||
from: idFrom,
|
||||
to: idTo,
|
||||
message: message.text,
|
||||
wrap: (message.wrap === null && autoWrap()) || !!message.wrap,
|
||||
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap,
|
||||
type: messageType
|
||||
});
|
||||
return true;
|
||||
|
@ -180,12 +181,8 @@ export const enableSequenceNumbers = function() {
|
|||
};
|
||||
export const showSequenceNumbers = () => sequenceNumbersEnabled;
|
||||
|
||||
export const enableWrap = function() {
|
||||
wrapEnabled = true;
|
||||
};
|
||||
|
||||
export const disableWrap = function() {
|
||||
wrapEnabled = false;
|
||||
export const setWrap = function(wrapSetting) {
|
||||
wrapEnabled = wrapSetting;
|
||||
};
|
||||
|
||||
export const autoWrap = () => wrapEnabled;
|
||||
|
@ -193,7 +190,6 @@ export const autoWrap = () => wrapEnabled;
|
|||
export const clear = function() {
|
||||
actors = {};
|
||||
messages = [];
|
||||
configUpdated = false;
|
||||
};
|
||||
|
||||
export const parseMessage = function(str) {
|
||||
|
@ -251,7 +247,7 @@ export const addNote = function(actor, placement, message) {
|
|||
actor: actor,
|
||||
placement: placement,
|
||||
message: message.text,
|
||||
wrap: (message.wrap === null && autoWrap()) || !!message.wrap
|
||||
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap
|
||||
};
|
||||
|
||||
// Coerce actor into a [to, from, ...] array
|
||||
|
@ -262,7 +258,7 @@ export const addNote = function(actor, placement, message) {
|
|||
from: actors[0],
|
||||
to: actors[1],
|
||||
message: message.text,
|
||||
wrap: (message.wrap === null && autoWrap()) || !!message.wrap,
|
||||
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap,
|
||||
type: LINETYPE.NOTE,
|
||||
placement: placement
|
||||
});
|
||||
|
@ -270,20 +266,7 @@ export const addNote = function(actor, placement, message) {
|
|||
|
||||
export const setTitle = function(titleWrap) {
|
||||
title = titleWrap.text;
|
||||
titleWrapped = (titleWrap.wrap === null && autoWrap()) || !!titleWrap.wrap;
|
||||
};
|
||||
|
||||
export const updateConfig = function(config = getConfig()) {
|
||||
try {
|
||||
setConfig(config);
|
||||
configUpdated = true;
|
||||
} catch (error) {
|
||||
logger.error('Error: unable to parse config');
|
||||
}
|
||||
};
|
||||
|
||||
export const hasConfigChange = function() {
|
||||
return configUpdated;
|
||||
titleWrapped = (titleWrap.wrap === undefined && autoWrap()) || !!titleWrap.wrap;
|
||||
};
|
||||
|
||||
export const apply = function(param) {
|
||||
|
@ -355,20 +338,16 @@ export default {
|
|||
addActor,
|
||||
addMessage,
|
||||
addSignal,
|
||||
enableWrap,
|
||||
disableWrap,
|
||||
autoWrap,
|
||||
setWrap,
|
||||
enableSequenceNumbers,
|
||||
showSequenceNumbers,
|
||||
autoWrap,
|
||||
getMessages,
|
||||
getActors,
|
||||
getActor,
|
||||
getActorKeys,
|
||||
getTitle,
|
||||
parseDirective,
|
||||
hasConfigChange,
|
||||
getConfig,
|
||||
updateConfig,
|
||||
getTitleWrapped,
|
||||
clear,
|
||||
parseMessage,
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { select, selectAll } from 'd3';
|
||||
import svgDraw from './svgDraw';
|
||||
import svgDraw, { drawText } from './svgDraw';
|
||||
import { logger } from '../../logger';
|
||||
import { parser } from './parser/sequenceDiagram';
|
||||
import common from '../common/common';
|
||||
import sequenceDb from './sequenceDb';
|
||||
import { getConfig } from '../../config';
|
||||
|
||||
parser.yy = sequenceDb;
|
||||
|
||||
|
@ -79,9 +78,6 @@ export const bounds = {
|
|||
stopy: undefined
|
||||
};
|
||||
this.verticalPos = 0;
|
||||
if (parser.yy.hasConfigChange()) {
|
||||
setConf(getConfig());
|
||||
}
|
||||
},
|
||||
updateVal: function(obj, key, val, fun) {
|
||||
if (typeof obj[key] === 'undefined') {
|
||||
|
@ -153,15 +149,20 @@ export const bounds = {
|
|||
.lastIndexOf(message.from.actor);
|
||||
return this.activations.splice(lastActorActivationIdx, 1)[0];
|
||||
},
|
||||
newLoop: function(title, fill) {
|
||||
this.sequenceItems.push({
|
||||
createLoop: function(title = { message: undefined, wrap: false, width: undefined }, fill) {
|
||||
return {
|
||||
startx: undefined,
|
||||
starty: this.verticalPos,
|
||||
stopx: undefined,
|
||||
stopy: undefined,
|
||||
title: title,
|
||||
title: title.message,
|
||||
wrap: title.wrap,
|
||||
width: title.width,
|
||||
fill: fill
|
||||
});
|
||||
};
|
||||
},
|
||||
newLoop: function(title = { message: undefined, wrap: false, width: undefined }, fill) {
|
||||
this.sequenceItems.push(this.createLoop(title, fill));
|
||||
},
|
||||
endLoop: function() {
|
||||
return this.sequenceItems.pop();
|
||||
|
@ -186,7 +187,7 @@ export const bounds = {
|
|||
}
|
||||
};
|
||||
|
||||
const wrapLabel = (label, maxWidth, joinWith = '<br/>') => {
|
||||
export const wrapLabel = (label, maxWidth, joinWith = '<br/>', cnf = conf) => {
|
||||
if (common.lineBreakRegex.test(label)) {
|
||||
return label;
|
||||
}
|
||||
|
@ -194,8 +195,13 @@ const wrapLabel = (label, maxWidth, joinWith = '<br/>') => {
|
|||
const completedLines = [];
|
||||
let nextLine = '';
|
||||
words.forEach((word, index) => {
|
||||
const wordLength = calculateTextWidth(`${word} `);
|
||||
const nextLineLength = calculateTextWidth(nextLine);
|
||||
const wordLength = calculateTextWidth(`${word} `, cnf.fontSize, cnf.fontFamily, cnf.fontWeight);
|
||||
const nextLineLength = calculateTextWidth(
|
||||
nextLine,
|
||||
cnf.fontSize,
|
||||
cnf.fontFamily,
|
||||
cnf.fontWeight
|
||||
);
|
||||
if (wordLength > maxWidth) {
|
||||
const { hyphenatedStrings, remainingWord } = breakString(word, maxWidth);
|
||||
completedLines.push(nextLine, ...hyphenatedStrings);
|
||||
|
@ -235,10 +241,7 @@ const breakString = (word, maxWidth, hyphenCharacter = '-') => {
|
|||
return { hyphenatedStrings: lines, remainingWord: currentLine };
|
||||
};
|
||||
|
||||
const _drawLongText = (text, x, y, g, width) => {
|
||||
let textHeight = 0;
|
||||
let prevTextHeight = 0;
|
||||
|
||||
const drawLongText = (text, x, y, g, width) => {
|
||||
const alignmentToAnchor = {
|
||||
left: 'start',
|
||||
start: 'start',
|
||||
|
@ -247,43 +250,45 @@ const _drawLongText = (text, x, y, g, width) => {
|
|||
right: 'end',
|
||||
end: 'end'
|
||||
};
|
||||
const lines = text.split(common.lineBreakRegex);
|
||||
for (const line of lines) {
|
||||
const textObj = svgDraw.getTextObj();
|
||||
const alignment = alignmentToAnchor[conf.noteAlign] || 'middle';
|
||||
|
||||
switch (alignment) {
|
||||
case 'start':
|
||||
textObj.x = x + conf.noteMargin;
|
||||
break;
|
||||
case 'middle':
|
||||
textObj.x = x + width / 2;
|
||||
break;
|
||||
case 'end':
|
||||
textObj.x = x + width - conf.noteMargin;
|
||||
break;
|
||||
}
|
||||
|
||||
textObj.y = y + textHeight;
|
||||
textObj.dy = '1em';
|
||||
textObj.text = line;
|
||||
textObj.class = 'noteText';
|
||||
|
||||
const textElem = svgDraw
|
||||
.drawText(g, textObj)
|
||||
.style('text-anchor', alignment)
|
||||
.style('font-size', conf.noteFontSize)
|
||||
.style('font-family', conf.noteFontFamily)
|
||||
.style('font-weight', conf.noteFontWeight)
|
||||
.attr('dominant-baseline', 'central')
|
||||
.attr('alignment-baseline', 'central');
|
||||
|
||||
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
|
||||
textElem.attr('y', y + (prevTextHeight + textHeight + 2 * conf.noteMargin) / 2);
|
||||
prevTextHeight = textHeight;
|
||||
const alignment = alignmentToAnchor[conf.noteAlign] || 'middle';
|
||||
const textObj = svgDraw.getTextObj();
|
||||
switch (alignment) {
|
||||
case 'start':
|
||||
textObj.x = x + conf.noteMargin;
|
||||
break;
|
||||
case 'middle':
|
||||
textObj.x = x + width / 2;
|
||||
break;
|
||||
case 'end':
|
||||
textObj.x = x + width - conf.noteMargin;
|
||||
break;
|
||||
}
|
||||
|
||||
return textHeight;
|
||||
textObj.y = y;
|
||||
textObj.dy = '1em';
|
||||
textObj.text = text;
|
||||
textObj.class = 'noteText';
|
||||
textObj.fontFamily = conf.noteFontFamily;
|
||||
textObj.fontSize = conf.noteFontSize;
|
||||
textObj.fontWeight = conf.noteFontWeight;
|
||||
textObj.anchor = alignment;
|
||||
textObj.textMargin = conf.noteMargin;
|
||||
textObj.valign = alignment;
|
||||
textObj.wrap = true;
|
||||
|
||||
let textElem = drawText(g, textObj);
|
||||
|
||||
if (!Array.isArray(textElem)) {
|
||||
textElem = [textElem];
|
||||
}
|
||||
|
||||
textElem.forEach(te => {
|
||||
te.attr('dominant-baseline', 'central').attr('alignment-baseline', 'central');
|
||||
});
|
||||
|
||||
return textElem
|
||||
.map(te => (te._groups || te)[0][0].getBBox().height)
|
||||
.reduce((acc, curr) => acc + curr);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -304,7 +309,7 @@ const drawNote = function(elem, startx, verticalPos, msg, forceWidth) {
|
|||
let g = elem.append('g');
|
||||
const rectElem = svgDraw.drawRect(g, rect);
|
||||
|
||||
const textHeight = _drawLongText(msg.message, startx, verticalPos, g, rect.width);
|
||||
const textHeight = drawLongText(msg.message, startx, verticalPos, g, rect.width);
|
||||
|
||||
bounds.insert(
|
||||
startx,
|
||||
|
@ -331,26 +336,6 @@ const drawMessage = function(elem, startx, stopx, verticalPos, msg, sequenceInde
|
|||
const txtCenter = startx + (stopx - startx) / 2;
|
||||
|
||||
let textElems = [];
|
||||
/*
|
||||
let textHeight = 0;
|
||||
const breaklines = msg.message.split(common.lineBreakRegex);
|
||||
for (const breakline of breaklines) {
|
||||
let textElem = g
|
||||
.append('text') // text label for the x axis
|
||||
.attr('x', txtCenter)
|
||||
.attr('y', verticalPos + textHeight)
|
||||
.style('font-size', conf.messageFontSize)
|
||||
.style('font-family', conf.messageFontFamily)
|
||||
.style('font-weight', conf.messageFontWeight)
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('class', 'messageText')
|
||||
.text(breakline.trim());
|
||||
textElems.push(textElem);
|
||||
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
|
||||
}
|
||||
|
||||
let totalOffset = textHeight;
|
||||
*/
|
||||
|
||||
let counterBreaklines = 0;
|
||||
let breaklineOffset = conf.messageFontSize + 4;
|
||||
|
@ -500,24 +485,13 @@ export const drawActors = function(diagram, actors, actorKeys, verticalPos) {
|
|||
// Draw the actors
|
||||
let prevWidth = 0;
|
||||
let prevMargin = 0;
|
||||
let maxActorHeight = conf.height;
|
||||
|
||||
for (let i = 0; i < actorKeys.length; i++) {
|
||||
const actor = actors[actorKeys[i]];
|
||||
|
||||
// Add some rendering data to the object
|
||||
actor.width = actor.width || calculateActorWidth(actor);
|
||||
actor.height = actor.wrap
|
||||
? calculateTextHeight(
|
||||
actor.message,
|
||||
conf.height,
|
||||
actor.width,
|
||||
conf.wrapPadding,
|
||||
actor.wrap,
|
||||
conf.actorFontSize
|
||||
)
|
||||
: conf.height;
|
||||
maxActorHeight = Math.max(maxActorHeight, actor.height);
|
||||
actor.height = conf.height;
|
||||
actor.margin = actor.margin || conf.actorMargin;
|
||||
|
||||
actor.x = prevWidth + prevMargin;
|
||||
|
@ -535,7 +509,7 @@ export const drawActors = function(diagram, actors, actorKeys, verticalPos) {
|
|||
}
|
||||
|
||||
// Add a margin between the actor boxes and the first arrow
|
||||
bounds.bumpVerticalPos(maxActorHeight);
|
||||
bounds.bumpVerticalPos(conf.height);
|
||||
};
|
||||
|
||||
export const setConf = function(cnf) {
|
||||
|
@ -645,15 +619,16 @@ export const calculateTextHeight = function(
|
|||
* @param fontSize - The font size of the given text
|
||||
* @param fontFamily - The font family (one, or more fonts) to render
|
||||
* @param fontWeight - The font weight (normal, bold, italics)
|
||||
* @param pad - Whether to add the left and right wrapPadding to the width (default: true)
|
||||
*/
|
||||
export const calculateTextWidth = function(text, fontSize, fontFamily, fontWeight) {
|
||||
export const calculateTextWidth = function(text, fontSize, fontFamily, fontWeight, pad = true) {
|
||||
if (!text) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fontSize = fontSize ? fontSize : conf.actorFontSize;
|
||||
fontFamily = fontFamily ? fontFamily : conf.actorFontFamily;
|
||||
fontWeight = fontWeight ? fontWeight : conf.actorFontWeight;
|
||||
fontSize = fontSize ? fontSize : conf.fontSize;
|
||||
fontFamily = fontFamily ? fontFamily : conf.fontFamily;
|
||||
fontWeight = fontWeight ? fontWeight : conf.fontWeight;
|
||||
|
||||
// We can't really know if the user supplied font family will render on the user agent;
|
||||
// thus, we'll take the max width between the user supplied font family, and a default
|
||||
|
@ -676,7 +651,7 @@ export const calculateTextWidth = function(text, fontSize, fontFamily, fontWeigh
|
|||
const textObj = svgDraw.getTextObj();
|
||||
textObj.text = line;
|
||||
const textElem = svgDraw
|
||||
.drawText(g, textObj)
|
||||
.drawSimpleText(g, textObj)
|
||||
.style('font-size', fontSize)
|
||||
.style('font-weight', fontWeight)
|
||||
.style('font-family', fontFamily);
|
||||
|
@ -688,9 +663,38 @@ export const calculateTextWidth = function(text, fontSize, fontFamily, fontWeigh
|
|||
g.remove();
|
||||
|
||||
// Adds some padding, so the text won't sit exactly within the actor's borders
|
||||
return maxWidth + conf.wrapPadding * 2;
|
||||
return maxWidth + (pad ? conf.wrapPadding * 2 : 0);
|
||||
};
|
||||
|
||||
function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoopFn) {
|
||||
let heightAdjust = 0;
|
||||
bounds.bumpVerticalPos(preMargin);
|
||||
if (msg.message && msg.wrap && loopWidths[msg.message]) {
|
||||
let loopWidth = loopWidths[msg.message].width;
|
||||
let minSize =
|
||||
Math.round((3 * conf.fontSize) / 4) < 10
|
||||
? conf.fontSize
|
||||
: Math.round((3 * conf.fontSize) / 4);
|
||||
msg.message = msg.message
|
||||
? wrapLabel(`[ ${msg.message} ]`, loopWidth - 2 * conf.wrapPadding, '<br/>', {
|
||||
fontSize: minSize,
|
||||
fontFamily: conf.fontFamily,
|
||||
fontWeight: conf.fontWeight
|
||||
})
|
||||
: msg.message;
|
||||
heightAdjust = calculateTextHeight(
|
||||
msg.message,
|
||||
minSize,
|
||||
loopWidth,
|
||||
conf.wrapPadding,
|
||||
msg.wrap,
|
||||
minSize
|
||||
);
|
||||
}
|
||||
addLoopFn(msg);
|
||||
bounds.bumpVerticalPos(heightAdjust + postMargin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||
* @param text
|
||||
|
@ -698,6 +702,7 @@ export const calculateTextWidth = function(text, fontSize, fontFamily, fontWeigh
|
|||
*/
|
||||
export const draw = function(text, id) {
|
||||
parser.yy.clear();
|
||||
parser.yy.setWrap(conf.wrapEnabled);
|
||||
parser.parse(text + '\n');
|
||||
|
||||
bounds.init();
|
||||
|
@ -714,15 +719,10 @@ export const draw = function(text, id) {
|
|||
const title = parser.yy.getTitle();
|
||||
|
||||
const maxMessageWidthPerActor = getMaxMessageWidthPerActor(actors, messages);
|
||||
const maxActorHeight = calculateActorMargins(actors, maxMessageWidthPerActor);
|
||||
conf.height = calculateActorMargins(actors, maxMessageWidthPerActor);
|
||||
|
||||
drawActors(diagram, actors, actorKeys, 0);
|
||||
|
||||
bounds.bumpVerticalPos(
|
||||
maxActorHeight > conf.height
|
||||
? Math.min(conf.boxMargin, Math.abs(maxActorHeight - conf.height))
|
||||
: 0
|
||||
);
|
||||
const loopWidths = calculateLoopMargins(messages, actors);
|
||||
|
||||
// The arrow head definition is attached to the svg once
|
||||
svgDraw.insertArrowHead(diagram);
|
||||
|
@ -826,13 +826,16 @@ export const draw = function(text, id) {
|
|||
activeEnd(msg, bounds.getVerticalPos());
|
||||
break;
|
||||
case parser.yy.LINETYPE.LOOP_START:
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
bounds.newLoop(msg.message);
|
||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||
adjustLoopHeightForWrap(
|
||||
loopWidths,
|
||||
msg,
|
||||
conf.boxMargin,
|
||||
conf.boxMargin + conf.boxTextMargin,
|
||||
message => bounds.newLoop(message)
|
||||
);
|
||||
break;
|
||||
case parser.yy.LINETYPE.LOOP_END:
|
||||
loopData = bounds.endLoop();
|
||||
|
||||
svgDraw.drawLoop(diagram, loopData, 'loop', conf);
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
break;
|
||||
|
@ -841,51 +844,56 @@ export const draw = function(text, id) {
|
|||
bounds.newLoop(undefined, msg.message);
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
break;
|
||||
case parser.yy.LINETYPE.RECT_END: {
|
||||
const rectData = bounds.endLoop();
|
||||
svgDraw.drawBackgroundRect(diagram, rectData);
|
||||
case parser.yy.LINETYPE.RECT_END:
|
||||
svgDraw.drawBackgroundRect(diagram, bounds.endLoop());
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
break;
|
||||
}
|
||||
case parser.yy.LINETYPE.OPT_START:
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
bounds.newLoop(msg.message);
|
||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||
adjustLoopHeightForWrap(
|
||||
loopWidths,
|
||||
msg,
|
||||
conf.boxMargin,
|
||||
conf.boxMargin + conf.boxTextMargin,
|
||||
message => bounds.newLoop(message)
|
||||
);
|
||||
break;
|
||||
case parser.yy.LINETYPE.OPT_END:
|
||||
loopData = bounds.endLoop();
|
||||
|
||||
svgDraw.drawLoop(diagram, loopData, 'opt', conf);
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
break;
|
||||
case parser.yy.LINETYPE.ALT_START:
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
bounds.newLoop(msg.message);
|
||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||
adjustLoopHeightForWrap(
|
||||
loopWidths,
|
||||
msg,
|
||||
conf.boxMargin,
|
||||
conf.boxMargin + conf.boxTextMargin,
|
||||
message => bounds.newLoop(message)
|
||||
);
|
||||
break;
|
||||
case parser.yy.LINETYPE.ALT_ELSE:
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
loopData = bounds.addSectionToLoop(msg.message);
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
adjustLoopHeightForWrap(loopWidths, msg, conf.boxMargin, conf.boxMargin, message =>
|
||||
bounds.addSectionToLoop(message)
|
||||
);
|
||||
break;
|
||||
case parser.yy.LINETYPE.ALT_END:
|
||||
loopData = bounds.endLoop();
|
||||
|
||||
svgDraw.drawLoop(diagram, loopData, 'alt', conf);
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
break;
|
||||
case parser.yy.LINETYPE.PAR_START:
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
if (shouldWrap) {
|
||||
msg.message = wrapLabel(msg.message, conf.boxMargin);
|
||||
}
|
||||
bounds.newLoop(msg.message);
|
||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||
adjustLoopHeightForWrap(
|
||||
loopWidths,
|
||||
msg,
|
||||
conf.boxMargin,
|
||||
conf.boxMargin + conf.boxTextMargin,
|
||||
message => bounds.newLoop(message)
|
||||
);
|
||||
break;
|
||||
case parser.yy.LINETYPE.PAR_AND:
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
loopData = bounds.addSectionToLoop(msg.message);
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
adjustLoopHeightForWrap(loopWidths, msg, conf.boxMargin, conf.boxMargin, message =>
|
||||
bounds.addSectionToLoop(message)
|
||||
);
|
||||
break;
|
||||
case parser.yy.LINETYPE.PAR_END:
|
||||
loopData = bounds.endLoop();
|
||||
|
@ -971,7 +979,7 @@ export const draw = function(text, id) {
|
|||
if (conf.useMaxWidth) {
|
||||
diagram.attr('height', '100%');
|
||||
diagram.attr('width', '100%');
|
||||
diagram.attr('style', 'max-width:' + width + 'px;');
|
||||
diagram.attr('style', 'max-width:100%;');
|
||||
} else {
|
||||
diagram.attr('height', height);
|
||||
diagram.attr('width', width);
|
||||
|
@ -1125,13 +1133,16 @@ const calculateActorMargins = function(actors, actorToMessageWidth) {
|
|||
);
|
||||
|
||||
act.height = act.wrap
|
||||
? calculateTextHeight(
|
||||
act.description,
|
||||
conf.height,
|
||||
actor.width,
|
||||
conf.actorMargin,
|
||||
act.wrap,
|
||||
conf.actorFontSize
|
||||
? Math.max(
|
||||
calculateTextHeight(
|
||||
act.description,
|
||||
conf.height,
|
||||
actor.width,
|
||||
conf.wrapPadding,
|
||||
act.wrap,
|
||||
conf.actorFontSize
|
||||
),
|
||||
conf.height
|
||||
)
|
||||
: conf.height;
|
||||
maxHeight = Math.max(maxHeight, act.height);
|
||||
|
@ -1142,11 +1153,57 @@ const calculateActorMargins = function(actors, actorToMessageWidth) {
|
|||
|
||||
actor.margin = Math.max(actorWidth, conf.actorMargin);
|
||||
}
|
||||
Object.keys(actors).forEach(function(key) {
|
||||
actors[key].height = maxHeight;
|
||||
});
|
||||
|
||||
return maxHeight;
|
||||
return Math.max(maxHeight, conf.height);
|
||||
};
|
||||
|
||||
const calculateLoopMargins = function(messages, actors) {
|
||||
const loops = {};
|
||||
const stack = [];
|
||||
let current;
|
||||
|
||||
messages.forEach(function(msg) {
|
||||
switch (msg.type) {
|
||||
case parser.yy.LINETYPE.LOOP_START:
|
||||
case parser.yy.LINETYPE.ALT_START:
|
||||
case parser.yy.LINETYPE.OPT_START:
|
||||
case parser.yy.LINETYPE.PAR_START:
|
||||
stack.push({
|
||||
msg: msg.message,
|
||||
from: Number.MAX_SAFE_INTEGER,
|
||||
to: Number.MIN_SAFE_INTEGER,
|
||||
width: 0
|
||||
});
|
||||
break;
|
||||
case parser.yy.LINETYPE.ALT_ELSE:
|
||||
case parser.yy.LINETYPE.PAR_AND:
|
||||
if (msg.message) {
|
||||
current = stack.pop();
|
||||
loops[msg.message] = current;
|
||||
stack.push(current);
|
||||
}
|
||||
break;
|
||||
case parser.yy.LINETYPE.LOOP_END:
|
||||
case parser.yy.LINETYPE.ALT_END:
|
||||
case parser.yy.LINETYPE.OPT_END:
|
||||
case parser.yy.LINETYPE.PAR_END:
|
||||
current = stack.pop();
|
||||
loops[current.msg] = current;
|
||||
break;
|
||||
}
|
||||
if (msg.from && msg.to && stack.length > 0) {
|
||||
current = stack.pop();
|
||||
let from = actors[msg.from];
|
||||
let to = actors[msg.to];
|
||||
current.from = Math.min(current.from, from.x - from.width / 2);
|
||||
current.to = Math.max(current.to, to.x + to.width / 2);
|
||||
current.width =
|
||||
Math.abs(current.from - current.to) - 2 * conf.wrapPadding - 40 /*2 * labelBoxWidth*/;
|
||||
stack.push(current);
|
||||
}
|
||||
});
|
||||
logger.debug('LoopWidths:', loops);
|
||||
return loops;
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -18,7 +18,7 @@ export const drawRect = function(elem, rectData) {
|
|||
return rectElem;
|
||||
};
|
||||
|
||||
export const drawText = function(elem, textData) {
|
||||
export const drawSimpleText = function(elem, textData) {
|
||||
// Remove and ignore br:s
|
||||
const nText = textData.text.replace(common.lineBreakRegex, ' ');
|
||||
|
||||
|
@ -42,6 +42,98 @@ export const drawText = function(elem, textData) {
|
|||
return textElem;
|
||||
};
|
||||
|
||||
export const drawText = function(elem, textData) {
|
||||
let prevTextHeight = 0,
|
||||
textHeight = 0;
|
||||
const lines = textData.wrap
|
||||
? textData.text.split(common.lineBreakRegex)
|
||||
: [textData.text.replace(common.lineBreakRegex, ' ')];
|
||||
|
||||
let textElems = [];
|
||||
let dy = 0;
|
||||
let yfunc = () => textData.y;
|
||||
if (
|
||||
typeof textData.valign !== 'undefined' &&
|
||||
typeof textData.textMargin !== 'undefined' &&
|
||||
textData.textMargin > 0
|
||||
) {
|
||||
switch (textData.valign) {
|
||||
case 'top':
|
||||
case 'start':
|
||||
yfunc = () => textData.y + textData.textMargin;
|
||||
break;
|
||||
case 'middle':
|
||||
case 'center':
|
||||
yfunc = () => textData.y + (prevTextHeight + textHeight + textData.textMargin) / 2;
|
||||
break;
|
||||
case 'bottom':
|
||||
case 'end':
|
||||
yfunc = () =>
|
||||
textData.y +
|
||||
(prevTextHeight + textHeight + 2 * textData.textMargin) -
|
||||
textData.textMargin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
if (
|
||||
typeof textData.textMargin !== 'undefined' &&
|
||||
textData.textMargin === 0 &&
|
||||
typeof textData.fontSize !== 'undefined'
|
||||
) {
|
||||
dy = i * textData.fontSize;
|
||||
}
|
||||
|
||||
const textElem = elem.append('text');
|
||||
textElem.attr('x', textData.x);
|
||||
textElem.attr('y', yfunc());
|
||||
if (typeof textData.anchor !== 'undefined') {
|
||||
textElem.style('text-anchor', textData.anchor);
|
||||
}
|
||||
if (typeof textData.fontFamily !== 'undefined') {
|
||||
textElem.style('font-family', textData.fontFamily);
|
||||
}
|
||||
if (typeof textData.fontSize !== 'undefined') {
|
||||
textElem.style('font-size', textData.fontSize);
|
||||
}
|
||||
if (typeof textData.fontWeight !== 'undefined') {
|
||||
textElem.style('font-weight', textData.fontWeight);
|
||||
}
|
||||
if (typeof textData.fill !== 'undefined') {
|
||||
textElem.attr('fill', textData.fill);
|
||||
}
|
||||
if (typeof textData.class !== 'undefined') {
|
||||
textElem.attr('class', textData.class);
|
||||
}
|
||||
if (typeof textData.dy !== 'undefined') {
|
||||
textElem.attr('dy', textData.dy);
|
||||
} else if (dy !== 0) {
|
||||
textElem.attr('dy', dy);
|
||||
}
|
||||
|
||||
const span = textElem.append('tspan');
|
||||
span.attr('x', textData.x);
|
||||
if (typeof textData.fill !== 'undefined') {
|
||||
span.attr('fill', textData.fill);
|
||||
}
|
||||
span.text(line);
|
||||
|
||||
if (
|
||||
typeof textData.valign !== 'undefined' &&
|
||||
typeof textData.textMargin !== 'undefined' &&
|
||||
textData.textMargin > 0
|
||||
) {
|
||||
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
|
||||
prevTextHeight = textHeight;
|
||||
}
|
||||
|
||||
textElems.push(textElem);
|
||||
}
|
||||
|
||||
return textElems.length === 1 ? textElems[0] : textElems;
|
||||
};
|
||||
|
||||
export const drawLabel = function(elem, txtObject) {
|
||||
function genPoints(x, y, width, height, cut) {
|
||||
return (
|
||||
|
@ -72,7 +164,7 @@ export const drawLabel = function(elem, txtObject) {
|
|||
|
||||
txtObject.y = txtObject.y + txtObject.labelMargin;
|
||||
txtObject.x = txtObject.x + 0.5 * txtObject.labelMargin;
|
||||
drawText(elem, txtObject);
|
||||
return drawText(elem, txtObject);
|
||||
};
|
||||
|
||||
let actorCnt = -1;
|
||||
|
@ -80,7 +172,7 @@ let actorCnt = -1;
|
|||
* Draws an actor in the diagram with the attaced line
|
||||
* @param elem - The diagram we'll draw to.
|
||||
* @param actor - The actor to draw.
|
||||
* @param conf - drawText implementation discriminator object
|
||||
* @param conf - utils.drawText implementation discriminator object
|
||||
*/
|
||||
export const drawActor = function(elem, actor, conf) {
|
||||
const center = actor.x + actor.width / 2;
|
||||
|
@ -185,33 +277,39 @@ export const drawLoop = function(elem, bounds, labelText, conf) {
|
|||
txt.fontWeight = conf.fontWeight;
|
||||
txt.class = 'labelText'; // Its size & position are fixed.
|
||||
|
||||
drawLabel(g, txt);
|
||||
|
||||
let labelElem = drawLabel(g, txt);
|
||||
let labelBoxWidth = (labelElem._groups || labelElem)[0][0].getBBox().width;
|
||||
txt = getTextObj();
|
||||
txt.text = '[ ' + bounds.title + ' ]';
|
||||
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2;
|
||||
txt.y = bounds.starty + 1.5 * conf.boxMargin;
|
||||
txt.text = bounds.title;
|
||||
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2 + labelBoxWidth;
|
||||
txt.y = bounds.starty + conf.boxMargin + conf.boxTextMargin;
|
||||
txt.anchor = 'middle';
|
||||
txt.class = 'loopText';
|
||||
txt.fontFamily = conf.fontFamily;
|
||||
txt.fontSize = minSize;
|
||||
txt.fontWeight = conf.fontWeight;
|
||||
txt.wrap = bounds.wrap;
|
||||
|
||||
let textElem = drawText(g, txt);
|
||||
let textHeight = (textElem._groups || textElem)[0][0].getBBox().height;
|
||||
drawText(g, txt);
|
||||
|
||||
if (typeof bounds.sectionTitles !== 'undefined') {
|
||||
bounds.sectionTitles.forEach(function(item, idx) {
|
||||
if (item !== '') {
|
||||
txt.text = '[ ' + item + ' ]';
|
||||
if (item.message) {
|
||||
txt.text = item.message;
|
||||
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2;
|
||||
txt.y = bounds.sections[idx] + 1.5 * conf.boxMargin;
|
||||
textElem = drawText(g, txt);
|
||||
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
|
||||
txt.class = 'loopText';
|
||||
txt.anchor = 'middle';
|
||||
txt.fontFamily = conf.fontFamily;
|
||||
txt.fontSize = minSize;
|
||||
txt.fontWeight = conf.fontWeight;
|
||||
txt.wrap = bounds.wrap;
|
||||
drawText(g, txt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return textHeight + 4;
|
||||
return g;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -300,23 +398,23 @@ export const insertArrowCrossHead = function(elem) {
|
|||
};
|
||||
|
||||
export const getTextObj = function() {
|
||||
const txt = {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: undefined,
|
||||
'text-anchor': 'start',
|
||||
anchor: 'start',
|
||||
style: '#666',
|
||||
width: 100,
|
||||
height: 100,
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0
|
||||
ry: 0,
|
||||
valign: undefined
|
||||
};
|
||||
return txt;
|
||||
};
|
||||
|
||||
export const getNoteRect = function() {
|
||||
const rect = {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: '#EDF2AE',
|
||||
|
@ -327,7 +425,6 @@ export const getNoteRect = function() {
|
|||
rx: 0,
|
||||
ry: 0
|
||||
};
|
||||
return rect;
|
||||
};
|
||||
|
||||
const _drawTextCandidateFunc = (function() {
|
||||
|
@ -412,6 +509,7 @@ const _drawTextCandidateFunc = (function() {
|
|||
export default {
|
||||
drawRect,
|
||||
drawText,
|
||||
drawSimpleText,
|
||||
drawLabel,
|
||||
drawActor,
|
||||
anchorElement,
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
import decode from 'entity-decode/browser';
|
||||
import mermaidAPI from './mermaidAPI';
|
||||
import { logger } from './logger';
|
||||
import utils from './utils';
|
||||
|
||||
/**
|
||||
* ## init
|
||||
* Function that goes through the document to find the chart definitions in there and render them.
|
||||
|
@ -98,6 +100,11 @@ const init = function() {
|
|||
.trim()
|
||||
.replace(/<br\s*\/?>/gi, '<br/>');
|
||||
|
||||
const init = utils.detectInit(txt);
|
||||
if (init) {
|
||||
logger.debug('Detected early reinit: ', init);
|
||||
}
|
||||
|
||||
try {
|
||||
mermaidAPI.render(
|
||||
id,
|
||||
|
|
|
@ -561,11 +561,6 @@ setLogLevel(config.logLevel);
|
|||
setConfig(config);
|
||||
|
||||
function parse(text) {
|
||||
const graphInit = utils.detectInit(text);
|
||||
if (graphInit) {
|
||||
reinitialize(graphInit);
|
||||
logger.debug('Init ', graphInit);
|
||||
}
|
||||
const graphType = utils.detectType(text);
|
||||
let parser;
|
||||
|
||||
|
@ -692,7 +687,7 @@ export const decodeEntities = function(text) {
|
|||
* });
|
||||
*```
|
||||
* @param id the id of the element to be rendered
|
||||
* @param txt the graph definition
|
||||
* @param _txt the graph definition
|
||||
* @param cb callback which is called after rendering is finished with the svg code as inparam.
|
||||
* @param container selector to element in which a div with the graph temporarily will be inserted. In one is
|
||||
* provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
||||
|
@ -741,10 +736,6 @@ const render = function(id, _txt, cb, container) {
|
|||
txt = encodeEntities(txt);
|
||||
|
||||
const element = select('#d' + id).node();
|
||||
const graphInit = utils.detectInit(txt);
|
||||
if (graphInit) {
|
||||
reinitialize(graphInit);
|
||||
}
|
||||
const graphType = utils.detectType(txt);
|
||||
|
||||
// insert inline style into svg
|
||||
|
@ -927,51 +918,18 @@ const render = function(id, _txt, cb, container) {
|
|||
|
||||
const setConf = function(cnf) {
|
||||
// Top level initially mermaid, gflow, sequenceDiagram and gantt
|
||||
const lvl1Keys = Object.keys(cnf);
|
||||
for (let i = 0; i < lvl1Keys.length; i++) {
|
||||
if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
|
||||
const lvl2Keys = Object.keys(cnf[lvl1Keys[i]]);
|
||||
|
||||
for (let j = 0; j < lvl2Keys.length; j++) {
|
||||
logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j]);
|
||||
if (typeof config[lvl1Keys[i]] === 'undefined') {
|
||||
config[lvl1Keys[i]] = {};
|
||||
}
|
||||
logger.debug(
|
||||
'Setting config: ' +
|
||||
lvl1Keys[i] +
|
||||
' ' +
|
||||
lvl2Keys[j] +
|
||||
' to ' +
|
||||
cnf[lvl1Keys[i]][lvl2Keys[j]]
|
||||
);
|
||||
config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]];
|
||||
}
|
||||
} else {
|
||||
config[lvl1Keys[i]] = cnf[lvl1Keys[i]];
|
||||
}
|
||||
}
|
||||
utils.assignWithDepth(config, cnf);
|
||||
};
|
||||
|
||||
function reinitialize(options) {
|
||||
function initialize(options) {
|
||||
logger.debug('Initializing mermaidAPI ', pkg.version);
|
||||
// Update default config with options supplied at initialization
|
||||
if (typeof options === 'object') {
|
||||
setConf(options);
|
||||
}
|
||||
setConfig(config);
|
||||
setLogLevel(config.logLevel);
|
||||
logger.debug('RE-Initializing mermaidAPI ', { version: pkg.version, options, config });
|
||||
}
|
||||
|
||||
function initialize(options) {
|
||||
let _config = config;
|
||||
logger.debug('Initializing mermaidAPI ', { version: pkg.version, options, _config });
|
||||
// Update default config with options supplied at initialization
|
||||
if (typeof options === 'object') {
|
||||
_config = Object.assign(_config, options);
|
||||
setConf(_config);
|
||||
}
|
||||
setConfig(_config);
|
||||
setLogLevel(_config.logLevel);
|
||||
logger.debug(`Initialized mermaidAPI ${JSON.stringify(config, null, 2)}`);
|
||||
}
|
||||
|
||||
// function getConfig () {
|
||||
|
@ -983,7 +941,6 @@ const mermaidAPI = {
|
|||
render,
|
||||
parse,
|
||||
initialize,
|
||||
reinitialize,
|
||||
getConfig
|
||||
};
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
$mainBkg: #1f2020;
|
||||
$secondBkg: lighten(#1f2020, 16);
|
||||
$mainContrastColor: lightgrey;
|
||||
$secondBkg: lighten(#1f2020, 24);
|
||||
$mainContrastColor: rgba(226, 226, 226, .75);
|
||||
$darkTextColor: #323D47;
|
||||
$lineColor: $mainContrastColor;
|
||||
$border1: #81B1DB;
|
||||
$border2: rgba(255, 255, 255, 0.25);
|
||||
$lineColor: lighten($mainContrastColor, 24);
|
||||
$border1: #81b1db;
|
||||
$border2: darken(#81b1db, 32);
|
||||
$arrowheadColor: $mainContrastColor;
|
||||
|
||||
/* Flowchart variables */
|
||||
|
@ -31,10 +31,10 @@ $labelTextColor: $mainContrastColor;
|
|||
$loopTextColor: $mainContrastColor;
|
||||
$noteBorderColor: $border2;
|
||||
$noteBkgColor: #fff5ad;
|
||||
$noteTextColor: $mainBkg;
|
||||
$noteTextColor: #1f2020;
|
||||
$activationBorderColor: $border1;
|
||||
$activationBkgColor: $secondBkg;
|
||||
$sequenceNumberColor: white;
|
||||
$sequenceNumberColor: $mainContrastColor;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
}
|
||||
|
||||
/* Classes common for multiple diagrams */
|
||||
|
||||
body {
|
||||
background-color: $mainBkg;
|
||||
}
|
||||
.error-icon {
|
||||
fill: $errorBkgColor;
|
||||
}
|
||||
|
|
41
src/utils.js
41
src/utils.js
|
@ -13,6 +13,7 @@ import {
|
|||
} from 'd3';
|
||||
import { logger } from './logger';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import mermaidAPI from './mermaidAPI';
|
||||
|
||||
// Effectively an enum of the supported curve types, accessible by name
|
||||
const d3CurveTypes = {
|
||||
|
@ -34,7 +35,7 @@ const anyComment = /\s*%%.*\n/gm;
|
|||
|
||||
/**
|
||||
* @function detectInit
|
||||
* Detects the init config object from the text
|
||||
* Detects the init config object from the text and (re)initializes mermaid
|
||||
* ```mermaid
|
||||
* %%{init: {"theme": "debug", "logLevel": 1 }}%%
|
||||
* graph LR
|
||||
|
@ -60,17 +61,20 @@ const anyComment = /\s*%%.*\n/gm;
|
|||
* ```
|
||||
*
|
||||
* @param {string} text The text defining the graph
|
||||
* @returns {object} the json object representing the init to pass to mermaid.initialize()
|
||||
* @returns {object} the json object representing the init passed to mermaid.initialize()
|
||||
*/
|
||||
export const detectInit = function(text) {
|
||||
let inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/);
|
||||
let results = {};
|
||||
if (Array.isArray(inits)) {
|
||||
let args = inits.map(init => init.args);
|
||||
results = Object.assign(results, ...args);
|
||||
results = assignWithDepth(results, ...args);
|
||||
} else {
|
||||
results = inits.args;
|
||||
}
|
||||
if (results) {
|
||||
mermaidAPI.initialize(results);
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
|
@ -382,6 +386,34 @@ export const generateId = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const assignWithDepth = function(dst, src, depth = 2) {
|
||||
if (depth <= 0) {
|
||||
if (dst !== undefined && dst !== null && typeof dst === 'object' && typeof src === 'object') {
|
||||
return Object.assign(dst, src);
|
||||
} else {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
if (src !== undefined && src !== null && typeof dst === 'object' && typeof src === 'object') {
|
||||
let optionsKeys = Object.keys(src);
|
||||
for (let i = 0; i < optionsKeys.length; i++) {
|
||||
let key = optionsKeys[i];
|
||||
if (
|
||||
typeof src[key] === 'object' &&
|
||||
(dst[key] === undefined || typeof dst[key] === 'object')
|
||||
) {
|
||||
if (dst[key] === undefined) {
|
||||
dst[key] = {};
|
||||
}
|
||||
dst[key] = assignWithDepth(dst[key], src[key], depth - 1);
|
||||
} else {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
};
|
||||
|
||||
export default {
|
||||
detectInit,
|
||||
detectDirective,
|
||||
|
@ -393,5 +425,6 @@ export default {
|
|||
formatUrl,
|
||||
getStylesFromArray,
|
||||
generateId,
|
||||
runFunc
|
||||
runFunc,
|
||||
assignWithDepth
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue