Merge branch 'release/8.4.1'

This commit is contained in:
knsv 2019-11-06 10:51:59 -08:00
commit a3017b8456
31 changed files with 463 additions and 18992 deletions

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
**/*.spec.js

View File

@ -1,7 +1,8 @@
{
"env": {
"browser": true,
"es6": true
"es6": true,
"node": true
},
"parserOptions": {
"ecmaFeatures": {
@ -10,7 +11,7 @@
},
"sourceType": "module"
},
"extends": ["prettier"],
"extends": ["prettier", "eslint:recommended"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": ["error"]

View File

@ -33,8 +33,8 @@ jobs:
echo $VERSION
npm version --no-git-tag-version --allow-same-version $VERSION
npm set //npm.pkg.github.com/:_authToken ${{ secrets.GITHUB_TOKEN }}
npm set registry https://npm.pkg.github.com/knsv
json -I -f package.json -e 'this.name="@knsv/mermaid"' # Package name needs to be set to a scoped one because GitHub registry requires this
json -I -f package.json -e 'this.repository="git://github.com/knsv/mermaid"' # Repo url needs to have a specific format too
npm set registry https://npm.pkg.github.com/mermaid-js
json -I -f package.json -e 'this.name="@mermaid-js/mermaid"' # Package name needs to be set to a scoped one because GitHub registry requires this
json -I -f package.json -e 'this.repository="git://github.com/mermaid-js/mermaid"' # Repo url needs to have a specific format too
npm publish

2
.gitignore vendored
View File

@ -10,3 +10,5 @@ dist/*.map
yarn-error.log
.npmrc
token
package-lock.json

View File

@ -86,7 +86,7 @@ Finally, if it is not in the documentation, no one will know about it and then *
The docs are located in the docs folder and are ofc written in markdown. Just pick the right section and start typing. If you want to add to the structure as in adding a new section and new file you do that via the _navbar.md.
The changes in master is reflected in http://knsv.github.io/mermaid/ once released the updates are commited to https://mermaidjs.github.io/#/
The changes in master is reflected in http://mermaid-js.github.io/mermaid/ once released the updates are commited to https://mermaid-js.github.io/#/
## Last words

View File

@ -0,0 +1,20 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
describe('State diagram', () => {
it('should render a flowchart full of circles', () => {
imgSnapshotTest(
`
stateDiagram
State1: The state with a note
note right of State1
Important information! You\ncan write
notes with multiple lines...
Here is another line...
And another line...
end note
`,
{}
);
});
});

View File

@ -2,7 +2,7 @@
import { imgSnapshotTest } from '../../helpers/util';
describe('Flowcart', () => {
it('should render a simple flowchart no htmlLabels', () => {
it('1: should render a simple flowchart no htmlLabels', () => {
imgSnapshotTest(
`graph TD
A[Christmas] -->|Get money| B(Go shopping)
@ -14,7 +14,7 @@ describe('Flowcart', () => {
{ flowchart: { htmlLabels: false } }
);
});
it('should render a simple flowchart with htmlLabels', () => {
it('2: should render a simple flowchart with htmlLabels', () => {
imgSnapshotTest(
`graph TD
A[Christmas] -->|Get money| B(Go shopping)
@ -26,7 +26,7 @@ describe('Flowcart', () => {
{ flowchart: { htmlLabels: true } }
);
});
it('should render a simple flowchart with line breaks', () => {
it('3: should render a simple flowchart with line breaks', () => {
imgSnapshotTest(
`
graph TD
@ -40,7 +40,7 @@ describe('Flowcart', () => {
);
});
it('should render a simple flowchart with trapezoid and inverse trapezoid vertex options.', () => {
it('4: should render a simple flowchart with trapezoid and inverse trapezoid vertex options.', () => {
imgSnapshotTest(
`
graph TD
@ -55,7 +55,7 @@ describe('Flowcart', () => {
);
});
it('should style nodes via a class.', () => {
it('4: should style nodes via a class.', () => {
imgSnapshotTest(
`
graph TD
@ -71,7 +71,7 @@ describe('Flowcart', () => {
);
});
it('should render a flowchart full of circles', () => {
it('5: should render a flowchart full of circles', () => {
imgSnapshotTest(
`
graph LR
@ -99,7 +99,7 @@ describe('Flowcart', () => {
{}
);
});
it('should render a flowchart full of icons', () => {
it('6: should render a flowchart full of icons', () => {
imgSnapshotTest(
`
graph TD
@ -170,7 +170,7 @@ describe('Flowcart', () => {
);
});
it('should render labels with numbers at the start', () => {
it('7: should render labels with numbers at the start', () => {
imgSnapshotTest(
`
graph TB;subgraph "number as labels";1;end;
@ -178,7 +178,7 @@ describe('Flowcart', () => {
{}
);
});
it('should render subgraphs', () => {
it('8: should render subgraphs', () => {
imgSnapshotTest(
`
graph TB
@ -190,7 +190,7 @@ describe('Flowcart', () => {
);
});
it('should render subgraphs with a title startign with a digit', () => {
it('9: should render subgraphs with a title startign with a digit', () => {
imgSnapshotTest(
`
graph TB
@ -202,7 +202,7 @@ describe('Flowcart', () => {
);
});
it('should render styled subgraphs', () => {
it('10: should render styled subgraphs', () => {
imgSnapshotTest(
`
graph TB
@ -237,7 +237,7 @@ describe('Flowcart', () => {
);
});
it('should render a flowchart with ling sames and class definitoins', () => {
it('11: should render a flowchart with ling sames and class definitoins', () => {
imgSnapshotTest(
`graph LR
sid-B3655226-6C29-4D00-B685-3D5C734DC7E1["
@ -339,7 +339,7 @@ describe('Flowcart', () => {
);
});
it('should render color of styled nodes', () => {
it('12: should render color of styled nodes', () => {
imgSnapshotTest(
`
graph LR

View File

@ -12,5 +12,29 @@ describe('Pie Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should render a simple pie diagram with long labels', () => {
imgSnapshotTest(
`
pie title NETFLIX
"Time spent looking for movie" : 90
"Time spent watching it" : 10
`,
{}
);
cy.get('svg');
});
it('should render a simple pie diagram with capital letters for labels', () => {
imgSnapshotTest(
`
pie title What Voldemort doesn't have?
"FRIENDS" : 2
"FAMILY" : 3
"NOSE" : 45
`,
{}
);
cy.get('svg');
});
});

View File

@ -13,6 +13,30 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('should render a long descriptions instead of id when available', () => {
imgSnapshotTest(
`
stateDiagram
[*] --> S1
state "Some long name" as S1
`,
{ logLevel: 0 }
);
cy.get('svg');
});
it('should render a long descriptions with additional descriptions', () => {
imgSnapshotTest(
`
stateDiagram
[*] --> S1
state "Some long name" as S1: The description
`,
{ logLevel: 0 }
);
cy.get('svg');
});
it('should render a single state with short descr', () => {
imgSnapshotTest(
`
@ -24,6 +48,20 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('should render a transition descrions with new lines', () => {
imgSnapshotTest(
`
stateDiagram
[*] --> S1
S1 --> S2: long line using<br/>should work
S1 --> S3: long line using <br>should work
S1 --> S4: long line using \\nshould work
`,
{ logLevel: 0 }
);
cy.get('svg');
});
it('should render a state with a note', () => {
imgSnapshotTest(
`

View File

@ -1,7 +1,7 @@
<html>
<head>
<script src="/e2e.js"></script>
<link href="https://fonts.googleapis.com/css?family=Mansalva&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Mansalva&display=swap" rel="stylesheet" />
<style>
body {
/* font-family: 'Mansalva', cursive;
@ -17,6 +17,9 @@
--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive;
--mermaid-font-family: '"Lucida Console", Monaco, monospace';
} */
svg {
border: 2px solid darkred;
}
</style>
</head>
<body>

View File

@ -1,3 +1,27 @@
## Basic Pie Chart
```
pie title NETFLIX
"Time spent looking for movie" : 90
"Time spent watching it" : 10
```
``` mermaid
pie title NETFLIX
"Time spent looking for movie" : 90
"Time spent watching it" : 10
```
```
pie title What Voldemort doesn't have?
"FRIENDS" : 2
"FAMILY" : 3
"NOSE" : 45
```
```mermaid
pie title What Voldemort doesn't have?
"FRIENDS" : 2
"FAMILY" : 3
"NOSE" : 45
```
## Basic sequence diagram
```

View File

@ -1,11 +1,12 @@
# Pie chart diagrams
> A pie chart (or a circle chart) is a circular statistical graphic, which is divided into slices to illustrate numerical proportion. In a pie chart, the arc length of each slice (and consequently its central angle and area), is proportional to the quantity it represents. While it is named for its resemblance to a pie which has been sliced, there are variations on the way it can be presented. The earliest known pie chart is generally credited to William Playfair's Statistical Breviary of 1801
-Wikipedia
Mermaid can render Pie Chart diagrams.
```
pie
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
@ -19,17 +20,34 @@ pie title Pets adopted by volunteers
## Syntax
Drawing a pie chart is really simple in mermaid.
- Start with `pie` keyword to begin the diagram
- Followed by `title` keyword and its value in string to give a title to the pie-chart. This is ***OPTIONAL***
- Followed by dataSet
- `label` for a section in the pie diagram within `" "` quotes.
- Followed by `:` semi-colon as separator
- Followed by `positive numeric value` (supported upto two decimal places)
[pie]
[title] [titlevalue] (OPTIONAL)
"[datakey1]" : [dataValue1]
"[datakey2]" : [dataValue2]
"[datakey3]" : [dataValue3]
.
.
## Example
```
pie
"DataKey1" : Positive numeric value (upto two decimal places)
title Key elements in Product X
"Calcium" : 42.96
"Potassium" : 50.05
"Magnesium" : 10.01
"Iron" : 5
```
```mermaid
pie title Key elements in Product X
pie
title Key elements in Product X
"Calcium" : 42.96
"Potassium" : 50.05
"Magnesium" : 25.01

18816
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "8.4.0",
"version": "8.4.1",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "dist/mermaid.core.js",
"keywords": [
@ -49,9 +49,9 @@
"@braintree/sanitize-url": "^3.1.0",
"crypto-random-string": "^3.0.1",
"d3": "^5.7.0",
"dagre-d3-renderer": "^0.5.8",
"dagre-layout": "^0.8.8",
"graphlibrary": "^2.2.0",
"dagre-d3": "dagrejs/dagre-d3",
"dagre": "^0.8.4",
"graphlib": "^2.1.7",
"he": "^1.2.0",
"lodash": "^4.17.11",
"minify": "^4.1.1",

View File

@ -1,6 +1,6 @@
import * as d3 from 'd3';
import dagre from 'dagre-layout';
import graphlib from 'graphlibrary';
import dagre from 'dagre';
import graphlib from 'graphlib';
import { logger } from '../../logger';
import classDb from './classDb';
import utils from '../../utils';
@ -204,14 +204,12 @@ const drawEdge = function(elem, path, relation) {
x = labalPosition.x;
y = labalPosition.y;
let p1_card_x,
p1_card_y,
p1_card_padd_x = conf.padding * 2,
p1_card_padd_y = conf.padding;
let p2_card_x,
p2_card_y,
p2_card_padd_x = conf.padding * 2,
p2_card_padd_y = -conf.padding / 2;
let p1_card_x, p1_card_y;
// p1_card_padd_x = conf.padding * 2,
// p1_card_padd_y = conf.padding;
let p2_card_x, p2_card_y;
// p2_card_padd_x = conf.padding * 2,
// p2_card_padd_y = -conf.padding / 2;
if (l % 2 !== 0 && l > 1) {
let cardinality_1_point = utils.calcCardinalityPosition(
relation.relation.type1 !== 'none',
@ -258,8 +256,7 @@ const drawEdge = function(elem, path, relation) {
logger.info('Rendering relation ' + JSON.stringify(relation));
if (typeof relation.relationTitle1 !== 'undefined' && relation.relationTitle1 !== 'none') {
const g = elem.append('g').attr('class', 'cardinality');
const label = g
.append('text')
g.append('text')
.attr('class', 'type1')
.attr('x', p1_card_x)
.attr('y', p1_card_y)
@ -269,8 +266,7 @@ const drawEdge = function(elem, path, relation) {
}
if (typeof relation.relationTitle2 !== 'undefined' && relation.relationTitle2 !== 'none') {
const g = elem.append('g').attr('class', 'cardinality');
const label = g
.append('text')
g.append('text')
.attr('class', 'type2')
.attr('x', p2_card_x)
.attr('y', p2_card_y)
@ -323,8 +319,6 @@ const drawClass = function(elem, classDef) {
isFirst = false;
});
console.warn('classDef.id', classDef.id);
console.warn('isFirst', isFirst);
// add class title
const classTitle = title
.append('tspan')
@ -348,7 +342,6 @@ const drawClass = function(elem, classDef) {
.attr('y', titleHeight + conf.dividerMargin + conf.textHeight)
.attr('fill', 'white')
.attr('class', 'classText');
console.warn(classDef.id, titleHeight, conf.dividerMargin, conf.textHeight);
isFirst = true;
classDef.members.forEach(function(member) {

View File

@ -163,7 +163,7 @@ members
methodStatement
: className {/*console.log('Rel found',$1);*/}
| className LABEL {yy.addMember($1,yy.cleanupLabel($2));}
| MEMBER {console.warn('Member',$1);}
| MEMBER {/*console.warn('Member',$1);*/}
| SEPARATOR {/*console.log('sep found',$1);*/}
;

View File

@ -1,4 +1,4 @@
import dagreD3 from 'dagre-d3-renderer';
import dagreD3 from 'dagre-d3';
function question(parent, bbox, node) {
const w = bbox.width;

View File

@ -223,7 +223,7 @@ const setClickFun = function(_id, functionName) {
return;
}
if (typeof vertices[id] !== 'undefined') {
funs.push(function(element) {
funs.push(function() {
const elem = document.querySelector(`[id="${id}"]`);
if (elem !== null) {
elem.addEventListener(
@ -395,7 +395,7 @@ export const addSubGraph = function(_id, list, _title) {
return false;
}
if (type in prims) {
return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true); // eslint-disable-line
} else {
return objs.indexOf(item) >= 0 ? false : objs.push(item);
}

View File

@ -1,11 +1,16 @@
import graphlib from 'graphlibrary';
import graphlib from 'graphlib';
import * as d3 from 'd3';
import flowDb from './flowDb';
import flow from './parser/flow';
import { getConfig } from '../../config';
import dagreD3 from 'dagre-d3-renderer';
import addHtmlLabel from 'dagre-d3-renderer/lib/label/add-html-label.js';
const newDagreD3 = true;
import dagreD3 from 'dagre-d3';
// const newDagreD3 = false;
// import dagreD3 from '../../../../dagre-d3-renderer/dist/dagre-d3.core.js';
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
import { logger } from '../../logger';
import { interpolateToCurve } from '../../utils';
import flowChartShapes from './flowChartShapes';
@ -291,18 +296,35 @@ export const draw = function(text, id) {
}
// Create the input mermaid.graph
const g = new graphlib.Graph({
multigraph: true,
compound: true
})
.setGraph({
rankdir: dir,
marginx: 20,
marginy: 20
let g;
// Todo remove newDagreD3 when properly verified
if (newDagreD3) {
g = new graphlib.Graph({
multigraph: true,
compound: true
})
.setDefaultEdgeLabel(function() {
return {};
});
.setGraph({
rankdir: dir,
marginx: 8,
marginy: 8
})
.setDefaultEdgeLabel(function() {
return {};
});
} else {
g = new graphlib.Graph({
multigraph: true,
compound: true
})
.setGraph({
rankdir: dir,
marginx: 20,
marginy: 20
})
.setDefaultEdgeLabel(function() {
return {};
});
}
let subG;
const subGraphs = flowDb.getSubGraphs();
@ -354,7 +376,7 @@ export const draw = function(text, id) {
};
// Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
render.arrows().normal = function normal(parent, id, edge, type) {
render.arrows().normal = function normal(parent, id) {
const marker = parent
.append('marker')
.attr('id', id)
@ -386,22 +408,50 @@ export const draw = function(text, id) {
});
const conf = getConfig().flowchart;
const padding = 8;
const width = g.maxX - g.minX + padding * 2;
const height = g.maxY - g.minY + padding * 2;
// Todo remove newDagreD3 when properly verified
if (newDagreD3) {
const svgBounds = svg.node().getBBox();
const width = svgBounds.width + padding * 2;
const height = svgBounds.height + padding * 2;
logger.debug(
`new ViewBox 0 0 ${width} ${height}`,
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
);
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
} else {
svg.attr('height', height);
svg.attr('width', width);
}
svg.attr('viewBox', `0 0 ${width} ${height}`);
svg
.select('g')
.attr('transform', `translate(${padding - g._label.marginx}, ${padding - svgBounds.y})`);
} else {
svg.attr('height', height);
svg.attr('width', width);
const width = g.maxX - g.minX + padding * 2;
const height = g.maxY - g.minY + padding * 2;
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
} else {
svg.attr('height', height);
svg.attr('width', width);
}
logger.debug(
`Org ViewBox 0 0 ${width} ${height}`,
`translate(${padding - g.minX}, ${padding - g.minY})\n${location.href}`
);
svg.attr('viewBox', `0 0 ${width} ${height}`);
svg.select('g').attr('transform', `translate(${padding - g.minX}, ${padding - g.minY})`);
// svg.select('g').attr('transform', `translate(${padding - minX}, ${padding - minY})`);
}
svg.attr('viewBox', `0 0 ${width} ${height}`);
svg.select('g').attr('transform', `translate(${padding - g.minX}, ${padding - g.minY})`);
// Index nodes
flowDb.indexNodes('subGraph' + i);

View File

@ -387,10 +387,11 @@ const compileTasks = function() {
const task = rawTasks[pos];
let startTime = '';
switch (rawTasks[pos].raw.startTime.type) {
case 'prevTaskEnd':
case 'prevTaskEnd': {
const prevTask = findTaskById(task.prevTaskId);
task.startTime = prevTask.endTime;
break;
}
case 'getStartDate':
startTime = getStartDate(undefined, dateFormat, rawTasks[pos].raw.startTime.startData);
if (startTime) {
@ -501,7 +502,7 @@ const setClickFun = function(id, functionName, functionArgs) {
* @param callbackFunction A function to be executed when clicked on the task or the task's text
*/
const pushFun = function(id, callbackFunction) {
funs.push(function(element) {
funs.push(function() {
// const elem = d3.select(element).select(`[id="${id}"]`)
const elem = document.querySelector(`[id="${id}"]`);
if (elem !== null) {
@ -510,7 +511,7 @@ const pushFun = function(id, callbackFunction) {
});
}
});
funs.push(function(element) {
funs.push(function() {
// const elem = d3.select(element).select(`[id="${id}-text"]`)
const elem = document.querySelector(`[id="${id}-text"]`);
if (elem !== null) {

View File

@ -102,7 +102,7 @@ export const draw = function(text, id) {
drawToday(leftPadding, topPadding, pageWidth, pageHeight);
}
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w) {
// Draw background rects covering the entire width of the graph, these form the section rows.
svg
.append('g')
@ -401,7 +401,7 @@ export const draw = function(text, id) {
const hash = {};
const result = [];
for (let i = 0, l = arr.length; i < l; ++i) {
if (!hash.hasOwnProperty(arr[i])) {
if (!hash.hasOwnProperty(arr[i])) { // eslint-disable-line
// it works with objects! in FF, at least
hash[arr[i]] = true;
result.push(arr[i]);

View File

@ -21,7 +21,7 @@ export const setConf = function(cnf) {
* @param id
*/
let w;
export const draw = (txt, id, ver) => {
export const draw = (txt, id) => {
try {
const parser = pieParser.parser;
parser.yy = pieData;
@ -50,6 +50,8 @@ export const draw = (txt, id, ver) => {
var width = w; // 450
var height = 450;
var margin = 40;
var legendRectSize = 18;
var legendSpacing = 4;
var radius = Math.min(width, height) / 2 - margin;
@ -62,6 +64,10 @@ export const draw = (txt, id, ver) => {
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var data = pieData.getSections();
var sum = 0;
Object.keys(data).forEach(function(key) {
sum += data[key];
});
logger.info(data);
// set the color scale
@ -75,7 +81,6 @@ export const draw = (txt, id, ver) => {
return d.value;
});
var dataReady = pie(d3.entries(data));
// Now I know that group A goes from 0 degrees to x degrees and so on.
// shape helper to build arcs:
var arcGenerator = d3
@ -97,14 +102,14 @@ export const draw = (txt, id, ver) => {
.style('stroke-width', '2px')
.style('opacity', 0.7);
// Now add the annotation. Use the centroid method to get the best coordinates
// Now add the Percentage. Use the centroid method to get the best coordinates
svg
.selectAll('mySlices')
.data(dataReady)
.enter()
.append('text')
.text(function(d) {
return d.data.key;
return ((d.data.value / sum) * 100).toFixed(0) + '%';
})
.attr('transform', function(d) {
return 'translate(' + arcGenerator.centroid(d) + ')';
@ -119,6 +124,36 @@ export const draw = (txt, id, ver) => {
.attr('x', 0)
.attr('y', -(h - 50) / 2)
.attr('class', 'pieTitleText');
//Add the slegend/annotations for each section
var legend = svg
.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = (height * color.domain().length) / 2;
var horz = 12 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend
.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend
.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) {
return d;
});
} catch (e) {
logger.error('Error while rendering info diagram');
logger.error(e.message);

View File

@ -514,11 +514,12 @@ export const draw = function(text, id) {
bounds.newLoop(undefined, msg.message);
bounds.bumpVerticalPos(conf.boxMargin);
break;
case parser.yy.LINETYPE.RECT_END:
case parser.yy.LINETYPE.RECT_END: {
const rectData = bounds.endLoop();
svgDraw.drawBackgroundRect(diagram, rectData);
bounds.bumpVerticalPos(conf.boxMargin);
break;
}
case parser.yy.LINETYPE.OPT_START:
bounds.bumpVerticalPos(conf.boxMargin);
bounds.newLoop(msg.message);

View File

@ -16,7 +16,7 @@ export const drawRect = function(elem, rectData) {
return rectElem;
};
export const drawText = function(elem, textData, width) {
export const drawText = function(elem, textData) {
// Remove and ignore br:s
const nText = textData.text.replace(/<br\/?>/gi, ' ');
@ -374,7 +374,7 @@ const _drawTextCandidateFunc = (function() {
function _setTextAttrs(toText, fromTextAttrsDict) {
for (const key in fromTextAttrsDict) {
if (fromTextAttrsDict.hasOwnProperty(key)) {
if (fromTextAttrsDict.hasOwnProperty(key)) { // eslint-disable-line
toText.attr(key, fromTextAttrsDict[key]);
}
}

View File

@ -43,10 +43,10 @@
<SCALE>\s+"width" {this.popState();}
<INITIAL,struct>"state"\s+ { this.pushState('STATE'); }
<STATE>.*"<<fork>>" {this.popState();yytext=yytext.slice(0,-8).trim(); console.warn('Fork Fork: ',yytext);return 'FORK';}
<STATE>.*"<<join>>" {this.popState();yytext=yytext.slice(0,-8).trim();console.warn('Fork Join: ',yytext);return 'JOIN';}
<STATE>.*"[[fork]]" {this.popState();yytext=yytext.slice(0,-8).trim();console.warn('Fork Fork: ',yytext);return 'FORK';}
<STATE>.*"[[join]]" {this.popState();yytext=yytext.slice(0,-8).trim();console.warn('Fork Join: ',yytext);return 'JOIN';}
<STATE>.*"<<fork>>" {this.popState();yytext=yytext.slice(0,-8).trim(); /*console.warn('Fork Fork: ',yytext);*/return 'FORK';}
<STATE>.*"<<join>>" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}
<STATE>.*"[[fork]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Fork: ',yytext);*/return 'FORK';}
<STATE>.*"[[join]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}
<STATE>["] this.begin("STATE_STRING");
<STATE>"as"\s* {this.popState();this.pushState('STATE_ID');return "AS";}
<STATE_ID>[^\n\{]* {this.popState();/* console.log('STATE_ID', yytext);*/return "ID";}
@ -133,7 +133,17 @@ statement
/* console.warn('Adding document for state without id ', $1);*/
$$={ stmt: 'state', id: $1, type: 'default', description: '', doc: $3 }
}
| STATE_DESCR AS ID { $$={stmt: 'state', id: $3, type: 'default', description: $1.trim()};}
| STATE_DESCR AS ID {
var id=$3;
var description = $1.trim();
if($3.match(':')){
var parts = $3.split(':');
id=parts[0];
description = [description, parts[1]];
}
$$={stmt: 'state', id: id, type: 'default', description: description};
}
| STATE_DESCR AS ID STRUCT_START document STRUCT_STOP
{
//console.warn('Adding document for state with id ', $3, $4); yy.addDocument($3);

View File

@ -2,7 +2,7 @@ import * as d3 from 'd3';
import idCache from './id-cache.js';
import stateDb from './stateDb';
import utils from '../../utils';
import { getConfig, conf } from '../../config';
import { getConfig } from '../../config';
// let conf;
@ -76,7 +76,7 @@ export const drawDescrState = (g, stateDef) => {
.attr('y', getConfig().state.textHeight + 1.5 * getConfig().state.padding)
.attr('font-size', getConfig().state.fontSize)
.attr('class', 'state-title')
.text(stateDef.id);
.text(stateDef.descriptions[0]);
const titleBox = title.node().getBBox();
const titleHeight = titleBox.height;
@ -94,8 +94,12 @@ export const drawDescrState = (g, stateDef) => {
.attr('class', 'state-description');
let isFirst = true;
let isSecond = true;
stateDef.descriptions.forEach(function(descr) {
addTspan(description, descr, isFirst);
if (!isFirst) {
addTspan(description, descr, isSecond);
isSecond = false;
}
isFirst = false;
});
@ -106,7 +110,6 @@ export const drawDescrState = (g, stateDef) => {
.attr('y2', getConfig().state.padding + titleHeight + getConfig().state.dividerMargin / 2)
.attr('class', 'descr-divider');
const descrBox = description.node().getBBox();
console.warn(descrBox.width, titleBox.width);
const width = Math.max(descrBox.width, titleBox.width);
descrLine.attr('x2', width + 3 * getConfig().state.padding);
@ -128,15 +131,15 @@ export const drawDescrState = (g, stateDef) => {
*/
export const addIdAndBox = (g, stateDef) => {
// TODO Move hardcodings to conf
const addTspan = function(textEl, txt, isFirst) {
const tSpan = textEl
.append('tspan')
.attr('x', 2 * getConfig().state.padding)
.text(txt);
if (!isFirst) {
tSpan.attr('dy', getConfig().state.textHeight);
}
};
// const addTspan = function(textEl, txt, isFirst) {
// const tSpan = textEl
// .append('tspan')
// .attr('x', 2 * getConfig().state.padding)
// .text(txt);
// if (!isFirst) {
// tSpan.attr('dy', getConfig().state.textHeight);
// }
// };
const title = g
.append('text')
.attr('x', 2 * getConfig().state.padding)
@ -145,7 +148,7 @@ export const addIdAndBox = (g, stateDef) => {
.attr('class', 'state-title')
.text(stateDef.id);
const titleHeight = title.node().getBBox().height;
const titleBox = title.node().getBBox();
const lineY = 1 - getConfig().state.textHeight;
const descrLine = g
@ -156,7 +159,7 @@ export const addIdAndBox = (g, stateDef) => {
.attr('class', 'descr-divider');
const graphBox = g.node().getBBox();
title.attr('x', graphBox.width / 2 - title.node().getBBox().width / 2);
title.attr('x', graphBox.width / 2 - titleBox.width / 2);
descrLine.attr('x2', graphBox.width + getConfig().state.padding);
// White color
@ -238,7 +241,7 @@ const drawForkJoinState = (g, stateDef) => {
.attr('y', getConfig().state.padding);
};
export const drawText = function(elem, textData, width) {
export const drawText = function(elem, textData) {
// Remove and ignore br:s
const nText = textData.text.replace(/<br\/?>/gi, ' ');
@ -261,7 +264,7 @@ export const drawText = function(elem, textData, width) {
const _drawLongText = (_text, x, y, g) => {
let textHeight = 0;
let textWidth = 0;
const textElem = g.append('text');
textElem.style('text-anchor', 'start');
textElem.attr('class', 'noteText');
@ -269,17 +272,22 @@ const _drawLongText = (_text, x, y, g) => {
let text = _text.replace(/\r\n/g, '<br/>');
text = text.replace(/\n/g, '<br/>');
const lines = text.split(/<br\/?>/gi);
let tHeight = 1.25 * getConfig().state.noteMargin;
for (const line of lines) {
const txt = line.trim();
if (txt.length > 0) {
const span = textElem.append('tspan');
span.text(txt);
const textBounds = span.node().getBBox();
textHeight += textBounds.height;
if (tHeight === 0) {
const textBounds = span.node().getBBox();
tHeight += textBounds.height;
}
// console.warn('textBounds', textBounds);
textHeight += tHeight;
span.attr('x', x + getConfig().state.noteMargin);
span.attr('y', y + textHeight + 1.25 * getConfig().state.noteMargin);
// textWidth = Math.max(textBounds.width, textWidth);
}
}
return { textWidth: textElem.node().getBBox().width, textHeight };
@ -314,8 +322,7 @@ export const drawNote = (text, g) => {
* @param {*} stateDef
*/
let cnt = 0;
export const drawState = function(elem, stateDef, graph, doc) {
export const drawState = function(elem, stateDef) {
const id = stateDef.id;
const stateInfo = {
id: id,
@ -347,6 +354,12 @@ export const drawState = function(elem, stateDef, graph, doc) {
return stateInfo;
};
const getRows = s => {
let str = s.replace(/<br\/?>/gi, '#br#');
str = str.replace(/\\n/g, '#br#');
return str.split('#br#');
};
let edgeCount = 0;
export const drawEdge = function(elem, path, relation) {
const getRelationType = function(type) {
@ -401,20 +414,48 @@ export const drawEdge = function(elem, path, relation) {
);
if (typeof relation.title !== 'undefined') {
const g = elem.append('g').attr('class', 'stateLabel');
const label = g
.append('text')
.attr('text-anchor', 'middle')
.text(relation.title);
const label = elem.append('g').attr('class', 'stateLabel');
const { x, y } = utils.calcLabelPosition(path.points);
label.attr('x', x).attr('y', y);
const rows = getRows(relation.title);
// console.warn(rows);
let titleHeight = 0;
const titleRows = [];
for (let i = 0; i <= rows.length; i++) {
const title = label
.append('text')
.attr('text-anchor', 'middle')
.text(rows[i])
.attr('x', x)
.attr('y', y + titleHeight);
if (titleHeight === 0) {
const titleBox = title.node().getBBox();
titleHeight = titleBox.height;
}
titleRows.push(title);
}
if (rows.length > 1) {
const heightAdj = rows.length * titleHeight * 0.25;
titleRows.forEach((title, i) => title.attr('y', y + i * titleHeight - heightAdj));
}
const bounds = label.node().getBBox();
g.insert('rect', ':first-child')
label
.insert('rect', ':first-child')
.attr('class', 'box')
.attr('x', bounds.x - getConfig().state.padding / 2)
.attr('y', bounds.y - getConfig().state.padding / 2)
.attr('width', bounds.width + getConfig().state.padding)
.attr('height', bounds.height + getConfig().state.padding);
//label.attr('transform', '0 -' + (bounds.y / 2));
// Debug points
// path.points.forEach(point => {
// g.append('circle')

View File

@ -9,7 +9,7 @@ const setRootDoc = o => {
const getRootDoc = () => rootDoc;
const extract = doc => {
const res = { states: [], relations: [] };
// const res = { states: [], relations: [] };
clear();
doc.forEach(item => {
@ -37,8 +37,8 @@ let documents = {
let currentDocument = documents.root;
let startCnt = 0;
let endCnt = 0;
let stateCnt = 0;
let endCnt = 0; // eslint-disable-line
// let stateCnt = 0;
/**
* Function called by parser when a node definition has been found.
@ -64,7 +64,14 @@ export const addState = function(id, type, doc, descr, note) {
currentDocument.states[id].type = type;
}
}
if (descr) addDescription(id, descr.trim());
if (descr) {
if (typeof descr === 'string') addDescription(id, descr.trim());
if (typeof descr === 'object') {
descr.forEach(des => addDescription(id, des.trim()));
}
}
if (note) currentDocument.states[id].note = note;
};

View File

@ -1,18 +1,15 @@
import * as d3 from 'd3';
import dagre from 'dagre-layout';
import graphlib from 'graphlibrary';
import dagre from 'dagre';
import graphlib from 'graphlib';
import { logger } from '../../logger';
import stateDb from './stateDb';
import { parser } from './parser/stateDiagram';
import utils from '../../utils';
import idCache from './id-cache';
import { drawState, addIdAndBox, drawEdge, drawNote } from './shapes';
// import idCache from './id-cache';
import { drawState, addIdAndBox, drawEdge } from './shapes';
import { getConfig } from '../../config';
parser.yy = stateDb;
let total = 0;
// TODO Move conf object to main conf in mermaidAPI
let conf;
// {
@ -28,20 +25,20 @@ let conf;
const transformationLog = {};
export const setConf = function(cnf) {};
export const setConf = function() {};
// Todo optimize
const getGraphId = function(label) {
const keys = idCache.keys();
// const getGraphId = function(label) {
// const keys = idCache.keys();
for (let i = 0; i < keys.length; i++) {
if (idCache.get(keys[i]).label === label) {
return keys[i];
}
}
// for (let i = 0; i < keys.length; i++) {
// if (idCache.get(keys[i]).label === label) {
// return keys[i];
// }
// }
return undefined;
};
// return undefined;
// };
/**
* Setup arrow head and define the marker. The result is appended to the svg.
@ -90,24 +87,49 @@ export const draw = function(text, id) {
});
const rootDoc = stateDb.getRootDoc();
const n = renderDoc(rootDoc, diagram);
renderDoc(rootDoc, diagram);
const padding = conf.padding;
const bounds = diagram.node().getBBox();
diagram.attr('height', '100%');
diagram.attr('style', `width: ${bounds.width * 3 + conf.padding * 2};`);
console.warn(bounds);
const width = bounds.width + padding * 2;
const height = bounds.height + padding * 2;
// diagram.attr('height', '100%');
// diagram.attr('style', `width: ${bounds.width * 3 + conf.padding * 2};`);
// diagram.attr('height', height);
// Zoom in a bit
diagram.attr('width', width * 2);
// diagram.attr('height', bounds.height * 3 + conf.padding * 2);
diagram.attr(
'viewBox',
`${conf.padding * -1} ${conf.padding * -1} ` +
(bounds.width * 1.5 + conf.padding * 2) +
' ' +
(bounds.height + conf.padding * 5)
`${bounds.x - conf.padding} ${bounds.y - conf.padding} ` + width + ' ' + height
);
// diagram.attr('transform', `translate(, 0)`);
// diagram.attr(
// 'viewBox',
// `${conf.padding * -1} ${conf.padding * -1} ` +
// (bounds.width * 1.5 + conf.padding * 2) +
// ' ' +
// (bounds.height + conf.padding * 5)
// );
};
const getLabelWidth = text => {
return text ? text.length * conf.fontSizeFactor : 1;
};
/* TODO: REMOVE DUPLICATION, SEE SHAPES */
const getRows = s => {
if (!s) return 1;
let str = s.replace(/<br\/?>/gi, '#br#');
str = str.replace(/\\n/g, '#br#');
return str.split('#br#');
};
const renderDoc = (doc, diagram, parentId) => {
// // Layout graph, Create a new directed graph
const graph = new graphlib.Graph({
@ -121,7 +143,6 @@ const renderDoc = (doc, diagram, parentId) => {
// multigraph: false,
compound: true,
// acyclicer: 'greedy',
rankdir: 'LR',
ranker: 'tight-tree',
ranksep: conf.edgeLengthFactor
// isMultiGraph: false
@ -151,7 +172,6 @@ const renderDoc = (doc, diagram, parentId) => {
const keys = Object.keys(states);
total = keys.length;
let first = true;
for (let i = 0; i < keys.length; i++) {
@ -221,7 +241,7 @@ const renderDoc = (doc, diagram, parentId) => {
graph.setEdge(relation.id1, relation.id2, {
relation: relation,
width: getLabelWidth(relation.title),
height: conf.labelHeight,
height: conf.labelHeight * getRows(relation.title).length,
labelpos: 'c'
});
});
@ -278,7 +298,7 @@ const renderDoc = (doc, diagram, parentId) => {
});
stateBox = svgElem.getBBox();
console.warn('Diagram node', svgElem.id);
const stateInfo = {
id: parentId ? parentId : 'root',
label: parentId ? parentId : 'root',

View File

@ -310,14 +310,13 @@ const config = {
state: {
dividerMargin: 10,
sizeUnit: 5,
padding: 5,
padding: 8,
textHeight: 10,
titleShift: -15,
noteMargin: 10,
forkWidth: 70,
forkHeight: 7,
// Used
padding: 5,
miniPadding: 2,
// Font size factor, this is used to guess the width of the edges labels before rendering by dagre
// layout. This might need updating if/when switching font
@ -366,8 +365,6 @@ function parse(text) {
break;
case 'info':
logger.debug('info info info');
console.warn('In API', pkg.version);
parser = infoParser;
parser.parser.yy = infoDb;
break;

View File

@ -122,7 +122,7 @@ const calcLabelPosition = points => {
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
let prevPoint;
let totalDistance = 0;
let totalDistance = 0; // eslint-disable-line
if (points[0] !== initialPosition) {
points = points.reverse();
}

View File

@ -3319,7 +3319,7 @@ d3-zoom@1:
d3-selection "1"
d3-transition "1"
d3@^5.7.0:
d3@^5.12, d3@^5.7.0:
version "5.12.0"
resolved "https://registry.yarnpkg.com/d3/-/d3-5.12.0.tgz#0ddeac879c28c882317cd439b495290acd59ab61"
integrity sha512-flYVMoVuhPFHd9zVCe2BxIszUWqBcd5fvQGMNRmSiBrgdnh6Vlruh60RJQTouAK9xPbOB0plxMvBm4MoyODXNg==
@ -3356,21 +3356,22 @@ d3@^5.7.0:
d3-voronoi "1"
d3-zoom "1"
dagre-d3-renderer@^0.5.8:
version "0.5.8"
resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.5.8.tgz#aa071bb71d3c4d67426925906f3f6ddead49c1a3"
integrity sha512-XH2a86isUHRxzIYbjQVEuZtJnWEufb64H5DuXIUmn8esuB40jgLEbUUclulWOW62/ZoXlj2ZDyL8SJ+YRxs+jQ==
dagre-d3@dagrejs/dagre-d3:
version "0.6.4-pre"
resolved "https://codeload.github.com/dagrejs/dagre-d3/tar.gz/e1a00e5cb518f5d2304a35647e024f31d178e55b"
dependencies:
dagre-layout "^0.8.8"
lodash "^4.17.5"
d3 "^5.12"
dagre "^0.8.4"
graphlib "^2.1.7"
lodash "^4.17.15"
dagre-layout@^0.8.8:
version "0.8.8"
resolved "https://registry.yarnpkg.com/dagre-layout/-/dagre-layout-0.8.8.tgz#9b6792f24229f402441c14162c1049e3f261f6d9"
integrity sha512-ZNV15T9za7X+fV8Z07IZquUKugCxm5owoiPPxfEx6OJRD331nkiIaF3vSt0JEY5FkrY0KfRQxcpQ3SpXB7pLPQ==
dagre@^0.8.4:
version "0.8.4"
resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.4.tgz#26b9fb8f7bdc60c6110a0458c375261836786061"
integrity sha512-Dj0csFDrWYKdavwROb9FccHfTC4fJbyF/oJdL9LNZJ8WUvl968P6PAKEriGqfbdArVJEmmfA+UyumgWEwcHU6A==
dependencies:
graphlibrary "^2.2.0"
lodash "^4.17.5"
graphlib "^2.1.7"
lodash "^4.17.4"
dashdash@^1.12.0:
version "1.14.1"
@ -5132,10 +5133,10 @@ graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
graphlibrary@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/graphlibrary/-/graphlibrary-2.2.0.tgz#017a14899775228dec4497a39babfdd6bf56eac6"
integrity sha512-XTcvT55L8u4MBZrM37zXoUxsgxs/7sow7YSygd9CIwfWTVO8RVu7AYXhhCiTuFEf+APKgx6Jk4SuQbYR0vYKmQ==
graphlib@^2.1.7:
version "2.1.7"
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.7.tgz#b6a69f9f44bd9de3963ce6804a2fc9e73d86aecc"
integrity sha512-TyI9jIy2J4j0qgPmOOrHTCtpPqJGN/aurBwc6ZT+bRii+di1I+Wv3obRhVrmBEXet+qkMaEX67dXrwsd3QQM6w==
dependencies:
lodash "^4.17.5"
@ -7044,7 +7045,7 @@ lodash@4.17.11:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10:
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==