mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
commit
425b071a50
@ -374,6 +374,7 @@ describe('Flowchart', () => {
|
|||||||
click B testClick "click test"
|
click B testClick "click test"
|
||||||
classDef someclass fill:#f96;
|
classDef someclass fill:#f96;
|
||||||
class A someclass;
|
class A someclass;
|
||||||
|
class C someclass;
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
listUrl: false,
|
listUrl: false,
|
||||||
@ -396,6 +397,7 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('16: Render Stadium shape', () => {
|
it('16: Render Stadium shape', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
` graph TD
|
` graph TD
|
||||||
@ -408,10 +410,13 @@ describe('Flowchart', () => {
|
|||||||
click A "index.html#link-clicked" "link test"
|
click A "index.html#link-clicked" "link test"
|
||||||
click B testClick "click test"
|
click B testClick "click test"
|
||||||
classDef someclass fill:#f96;
|
classDef someclass fill:#f96;
|
||||||
class A someclass;`,
|
class A someclass;
|
||||||
|
class C someclass;
|
||||||
|
`,
|
||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('17: Render multiline texts', () => {
|
it('17: Render multiline texts', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph LR
|
`graph LR
|
||||||
@ -428,6 +433,7 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('18: Chaining of nodes', () => {
|
it('18: Chaining of nodes', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph LR
|
`graph LR
|
||||||
@ -436,6 +442,7 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('19: Multiple nodes and chaining in one statement', () => {
|
it('19: Multiple nodes and chaining in one statement', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph LR
|
`graph LR
|
||||||
@ -444,6 +451,7 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('20: Multiple nodes and chaining in one statement', () => {
|
it('20: Multiple nodes and chaining in one statement', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph TD
|
`graph TD
|
||||||
@ -453,6 +461,7 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('21: Render cylindrical shape', () => {
|
it('21: Render cylindrical shape', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph LR
|
`graph LR
|
||||||
@ -474,6 +483,7 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('22: Render a simple flowchart with nodeSpacing set to 100', () => {
|
it('22: Render a simple flowchart with nodeSpacing set to 100', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph TD
|
`graph TD
|
||||||
@ -487,6 +497,7 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { nodeSpacing: 50 } }
|
{ flowchart: { nodeSpacing: 50 } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('23: Render a simple flowchart with rankSpacing set to 100', () => {
|
it('23: Render a simple flowchart with rankSpacing set to 100', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph TD
|
`graph TD
|
||||||
@ -500,4 +511,17 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { rankSpacing: '100' } }
|
{ flowchart: { rankSpacing: '100' } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('24: Keep node label text (if already defined) when a style is applied', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
A(( )) -->|step 1| B(( ))
|
||||||
|
B(( )) -->|step 2| C(( ))
|
||||||
|
C(( )) -->|step 3| D(( ))
|
||||||
|
linkStyle 1 stroke:greenyellow,stroke-width:2px
|
||||||
|
style C fill:greenyellow,stroke:green,stroke-width:4px
|
||||||
|
`,
|
||||||
|
{ flowchart: { htmlLabels: false } }
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -121,6 +121,18 @@ describe('State diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('should handle multiline notes with different line breaks', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
stateDiagram
|
||||||
|
State1
|
||||||
|
note right of State1
|
||||||
|
Line1<br>Line2<br/>Line3<br />Line4<br />Line5
|
||||||
|
end note
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should render a states with descriptions including multi-line descriptions', () => {
|
it('should render a states with descriptions including multi-line descriptions', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
|
107
dist/index.html
vendored
107
dist/index.html
vendored
@ -289,16 +289,17 @@ graph TB
|
|||||||
style 456ac9b0d15a8b7f1e71073221059886 fill:#f9f,stroke:#333,stroke-width:4px
|
style 456ac9b0d15a8b7f1e71073221059886 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph TD
|
graph TD
|
||||||
A[Christmas] -->|Get money| B(Go shopping)
|
A[Christmas] -->|Get money| B(Go shopping)
|
||||||
B --> C{{Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?}}
|
B --> C{{Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?}}
|
||||||
C -->|One| D[Laptop]
|
C -->|One| D[Laptop]
|
||||||
C -->|Two| E[iPhone]
|
C -->|Two| E[iPhone]
|
||||||
C -->|Three| F[Car]
|
C -->|Three| F[Car]
|
||||||
click A "index.html#link-clicked" "link test"
|
click A "index.html#link-clicked" "link test"
|
||||||
click B testClick "click test"
|
click B testClick "click test"
|
||||||
classDef someclass fill:#f96;
|
classDef someclass fill:#f96;
|
||||||
class A someclass;
|
class A someclass;
|
||||||
|
class C someclass;
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph TD
|
graph TD
|
||||||
@ -312,6 +313,7 @@ class A someclass;
|
|||||||
click B testClick "click test"
|
click B testClick "click test"
|
||||||
classDef someclass fill:#f96;
|
classDef someclass fill:#f96;
|
||||||
class A someclass;
|
class A someclass;
|
||||||
|
class C someclass;
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph LR
|
graph LR
|
||||||
@ -343,6 +345,14 @@ class A someclass;
|
|||||||
linkStyle 1 stroke:DarkGray,stroke-width:2px
|
linkStyle 1 stroke:DarkGray,stroke-width:2px
|
||||||
linkStyle 2 stroke:DarkGray,stroke-width:2px
|
linkStyle 2 stroke:DarkGray,stroke-width:2px
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A(( )) -->|step 1| B(( ))
|
||||||
|
B(( )) -->|step 2| C(( ))
|
||||||
|
C(( )) -->|step 3| D(( ))
|
||||||
|
linkStyle 1 stroke:greenyellow,stroke-width:2px
|
||||||
|
style C fill:greenyellow,stroke:green,stroke-width:4px
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
@ -519,45 +529,46 @@ class Class10 {
|
|||||||
int id
|
int id
|
||||||
size()
|
size()
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
classDiagram
|
classDiagram
|
||||||
Class01~T~ <|-- AveryLongClass : Cool
|
Class01~T~ <|-- AveryLongClass : Cool
|
||||||
<<interface>> Class01
|
<<interface>> Class01
|
||||||
Class03~T~ "0" *-- "0..n" Class04
|
Class03~T~ "0" *-- "0..n" Class04
|
||||||
Class05 "1" o-- "many" Class06
|
Class05 "1" o-- "many" Class06
|
||||||
Class07~T~ .. Class08
|
Class07~T~ .. Class08
|
||||||
Class09 "many" --> "1" C2 : Where am i?
|
Class09 "many" --> "1" C2 : Where am i?
|
||||||
Class09 "0" --* "1..n" C3
|
Class09 "0" --* "1..n" C3
|
||||||
Class09 --|> Class07
|
Class09 --|> Class07
|
||||||
Class07 : equals()
|
Class07 : equals()
|
||||||
Class07 : Object[] elementData
|
Class07 : Object[] elementData
|
||||||
Class01 : #size()
|
Class01 : #size()
|
||||||
Class01 : -int chimp
|
Class01 : -int chimp
|
||||||
Class01 : +int gorilla
|
Class01 : +int gorilla
|
||||||
Class08 <--> C2: Cool label
|
Class08 <--> C2: Cool label
|
||||||
class Class10 {
|
class Class10 {
|
||||||
<<service>>
|
<<service>>
|
||||||
int id
|
int id
|
||||||
size()
|
size()
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
stateDiagram
|
stateDiagram
|
||||||
State1
|
State1
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<div class="mermaid">
|
|
||||||
stateDiagram
|
<div class="mermaid">
|
||||||
[*] --> First
|
stateDiagram
|
||||||
state First {
|
[*] --> First
|
||||||
[*] --> second
|
state First {
|
||||||
second --> [*]
|
[*] --> second
|
||||||
}
|
second --> [*]
|
||||||
</div>
|
}
|
||||||
|
</div>
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
stateDiagram
|
stateDiagram
|
||||||
State1: The state with a note
|
State1: The state with a note
|
||||||
@ -567,8 +578,14 @@ class Class10 {
|
|||||||
end note
|
end note
|
||||||
State1 --> State2
|
State1 --> State2
|
||||||
note left of State2 : This is the note to the left.
|
note left of State2 : This is the note to the left.
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mermaid">
|
||||||
|
stateDiagram
|
||||||
|
State1
|
||||||
|
note right of State1
|
||||||
|
Line1<br>Line2<br/>Line3<br />Line4<br />Line5
|
||||||
|
end note
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="./mermaid.js"></script>
|
<script src="./mermaid.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
@ -314,6 +314,37 @@ graph LR
|
|||||||
A -- text --> B -- text2 --> C
|
A -- text --> B -- text2 --> C
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It is also possible to declare multiple nodes links in the same line as per below:
|
||||||
|
```
|
||||||
|
graph LR
|
||||||
|
a --> b & c--> d
|
||||||
|
```
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
a --> b & c--> d
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then describe dependencies in a very expressive way. Like the onliner below:
|
||||||
|
```
|
||||||
|
graph TB
|
||||||
|
A & B--> C & D
|
||||||
|
```
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
A & B--> C & D
|
||||||
|
```
|
||||||
|
If you describe the same diagram using the the basic syntax, it will take four lines. A
|
||||||
|
word of warning, one could go overboard with this making the graph harder to read in
|
||||||
|
markdown form. The Swedish word `lagom` comes to mind. It means, not to much and not to little.
|
||||||
|
This goes for expressive syntaxes as well.
|
||||||
|
```
|
||||||
|
graph TB
|
||||||
|
A --> C
|
||||||
|
A --> D
|
||||||
|
B --> C
|
||||||
|
B --> D
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Special characters that break syntax
|
## Special characters that break syntax
|
||||||
|
|
||||||
@ -512,6 +543,19 @@ It is also possible to attach a class to a list of nodes in one statement:
|
|||||||
class nodeId1,nodeId2 className;
|
class nodeId1,nodeId2 className;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A shorter form of adding a class is to attach the classname to the node using the `:::`operator as per below:
|
||||||
|
|
||||||
|
```
|
||||||
|
graph LR
|
||||||
|
A:::someclass --> B
|
||||||
|
classDef someclass fill:#f96;
|
||||||
|
```
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A:::someclass --> B
|
||||||
|
classDef someclass fill:#f96;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Css classes
|
### Css classes
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ These are the default options which can be overridden with the initialization ca
|
|||||||
<pre>
|
<pre>
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
flowchart:{
|
flowchart:{
|
||||||
htmlLabels: false
|
htmlLabels: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</pre>
|
</pre>
|
||||||
@ -27,7 +27,7 @@ mermaid.initialize({
|
|||||||
**Example 2:**
|
**Example 2:**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<script>
|
<script>
|
||||||
var config = {
|
var config = {
|
||||||
startOnLoad:true,
|
startOnLoad:true,
|
||||||
flowchart:{
|
flowchart:{
|
||||||
@ -39,10 +39,10 @@ mermaid.initialize({
|
|||||||
securityLevel:'loose',
|
securityLevel:'loose',
|
||||||
};
|
};
|
||||||
mermaid.initialize(config);
|
mermaid.initialize(config);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below.
|
|
||||||
|
A summary of all options and their defaults is found [here][2]. A description of each option follows below.
|
||||||
|
|
||||||
## theme
|
## theme
|
||||||
|
|
||||||
@ -333,3 +333,5 @@ mermaidAPI.initialize({
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
[1]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#render
|
[1]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#render
|
||||||
|
|
||||||
|
[2]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid",
|
"name": "mermaid",
|
||||||
"version": "8.4.5",
|
"version": "8.4.6",
|
||||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||||
"main": "dist/mermaid.core.js",
|
"main": "dist/mermaid.core.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
@ -140,7 +140,7 @@ export const addMembers = function(className, members) {
|
|||||||
|
|
||||||
export const cleanupLabel = function(label) {
|
export const cleanupLabel = function(label) {
|
||||||
if (label.substring(0, 1) === ':') {
|
if (label.substring(0, 1) === ':') {
|
||||||
return label.substr(2).trim();
|
return label.substr(1).trim();
|
||||||
} else {
|
} else {
|
||||||
return label.trim();
|
return label.trim();
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,48 @@ describe('class diagram, ', function () {
|
|||||||
parser.parse(str);
|
parser.parse(str);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should break when another `{`is encountered before closing the first one while defining generic class with brackets', function() {
|
||||||
|
const str =
|
||||||
|
'classDiagram\n' +
|
||||||
|
'class Dummy_Class~T~ {\n' +
|
||||||
|
'String data\n' +
|
||||||
|
' void methods()\n' +
|
||||||
|
'}\n' +
|
||||||
|
'\n' +
|
||||||
|
'class Dummy_Class {\n' +
|
||||||
|
'class Flight {\n' +
|
||||||
|
' flightNumber : Integer\n' +
|
||||||
|
' departureTime : Date\n' +
|
||||||
|
'}';
|
||||||
|
let testPased =false;
|
||||||
|
try{
|
||||||
|
parser.parse(str);
|
||||||
|
}catch (error){
|
||||||
|
console.log(error.name);
|
||||||
|
testPased = true;
|
||||||
|
}
|
||||||
|
expect(testPased).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should break when EOF is encountered before closing the first `{` while defining generic class with brackets', function() {
|
||||||
|
const str =
|
||||||
|
'classDiagram\n' +
|
||||||
|
'class Dummy_Class~T~ {\n' +
|
||||||
|
'String data\n' +
|
||||||
|
' void methods()\n' +
|
||||||
|
'}\n' +
|
||||||
|
'\n' +
|
||||||
|
'class Dummy_Class {\n';
|
||||||
|
let testPased =false;
|
||||||
|
try{
|
||||||
|
parser.parse(str);
|
||||||
|
}catch (error){
|
||||||
|
console.log(error.name);
|
||||||
|
testPased = true;
|
||||||
|
}
|
||||||
|
expect(testPased).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle generic class with brackets', function() {
|
it('should handle generic class with brackets', function() {
|
||||||
const str =
|
const str =
|
||||||
'classDiagram\n' +
|
'classDiagram\n' +
|
||||||
@ -79,8 +121,6 @@ describe('class diagram, ', function () {
|
|||||||
' flightNumber : Integer\n' +
|
' flightNumber : Integer\n' +
|
||||||
' departureTime : Date\n' +
|
' departureTime : Date\n' +
|
||||||
'}';
|
'}';
|
||||||
|
|
||||||
parser.parse(str);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle class definitions', function() {
|
it('should handle class definitions', function() {
|
||||||
|
@ -540,9 +540,14 @@ export const draw = function(text, id) {
|
|||||||
logger.info(
|
logger.info(
|
||||||
'tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation)
|
'tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation)
|
||||||
);
|
);
|
||||||
g.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), {
|
g.setEdge(
|
||||||
relation: relation
|
getGraphId(relation.id1),
|
||||||
});
|
getGraphId(relation.id2),
|
||||||
|
{
|
||||||
|
relation: relation
|
||||||
|
},
|
||||||
|
relation.title || 'DEFAULT'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
dagre.layout(g);
|
dagre.layout(g);
|
||||||
g.nodes().forEach(function(v) {
|
g.nodes().forEach(function(v) {
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
\s+ /* skip whitespace */
|
\s+ /* skip whitespace */
|
||||||
"classDiagram" return 'CLASS_DIAGRAM';
|
"classDiagram" return 'CLASS_DIAGRAM';
|
||||||
[\{] { this.begin("struct"); /*console.log('Starting struct');*/return 'STRUCT_START';}
|
[\{] { this.begin("struct"); /*console.log('Starting struct');*/return 'STRUCT_START';}
|
||||||
|
<struct><<EOF>> return "EOF_IN_STRUCT";
|
||||||
|
<struct>[\{] return "OPEN_IN_STRUCT";
|
||||||
<struct>\} { /*console.log('Ending struct');*/this.popState(); return 'STRUCT_STOP';}}
|
<struct>\} { /*console.log('Ending struct');*/this.popState(); return 'STRUCT_STOP';}}
|
||||||
<struct>[\n] /* nothing */
|
<struct>[\n] /* nothing */
|
||||||
<struct>[^\{\}\n]* { /*console.log('lex-member: ' + yytext);*/ return "MEMBER";}
|
<struct>[^\{\}\n]* { /*console.log('lex-member: ' + yytext);*/ return "MEMBER";}
|
||||||
|
@ -52,7 +52,7 @@ export const addVertex = function(_id, text, type, style, classes) {
|
|||||||
|
|
||||||
vertices[id].text = txt;
|
vertices[id].text = txt;
|
||||||
} else {
|
} else {
|
||||||
if (!vertices[id].text) {
|
if (typeof vertices[id].text === 'undefined') {
|
||||||
vertices[id].text = _id;
|
vertices[id].text = _id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ export const addVertices = function(vert, g, svgId) {
|
|||||||
} else {
|
} else {
|
||||||
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
|
||||||
const rows = vertexText.split(/<br[/]{0,1}>/);
|
const rows = vertexText.split(/<br\s*\/?>/gi);
|
||||||
|
|
||||||
for (let j = 0; j < rows.length; j++) {
|
for (let j = 0; j < rows.length; j++) {
|
||||||
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||||
@ -237,7 +237,7 @@ export const addEdges = function(edges, g) {
|
|||||||
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
|
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
|
||||||
} else {
|
} else {
|
||||||
edgeData.labelType = 'text';
|
edgeData.labelType = 'text';
|
||||||
edgeData.label = edge.text.replace(/<br\s*\/?>/g, '\n');
|
edgeData.label = edge.text.replace(/<br\s*\/?>/gi, '\n');
|
||||||
|
|
||||||
if (typeof edge.style === 'undefined') {
|
if (typeof edge.style === 'undefined') {
|
||||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
|
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
|
||||||
@ -461,6 +461,7 @@ export const draw = function(text, id) {
|
|||||||
const node = d3.select('#' + id + ' [id="' + key + '"]');
|
const node = d3.select('#' + id + ' [id="' + key + '"]');
|
||||||
if (node) {
|
if (node) {
|
||||||
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
|
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
|
||||||
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
|
||||||
|
|
||||||
|
@ -55,6 +55,43 @@ describe('the flowchart renderer', function() {
|
|||||||
expect(addedNodes[0][1]).toHaveProperty('ry', expectedRadios);
|
expect(addedNodes[0][1]).toHaveProperty('ry', expectedRadios);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
'Multi<br>Line',
|
||||||
|
'Multi<br/>Line',
|
||||||
|
'Multi<br />Line',
|
||||||
|
'Multi<br\t/>Line'
|
||||||
|
].forEach(function(labelText) {
|
||||||
|
it('should handle multiline texts with different line breaks', function() {
|
||||||
|
const addedNodes = [];
|
||||||
|
const mockG = {
|
||||||
|
setNode: function(id, object) {
|
||||||
|
addedNodes.push([id, object]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addVertices(
|
||||||
|
{
|
||||||
|
v1: {
|
||||||
|
type: 'rect',
|
||||||
|
id: 'my-node-id',
|
||||||
|
classes: [],
|
||||||
|
styles: [],
|
||||||
|
text: 'Multi<br>Line'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mockG,
|
||||||
|
'svg-id'
|
||||||
|
);
|
||||||
|
expect(addedNodes).toHaveLength(1);
|
||||||
|
expect(addedNodes[0][0]).toEqual('my-node-id');
|
||||||
|
expect(addedNodes[0][1]).toHaveProperty('id', 'my-node-id');
|
||||||
|
expect(addedNodes[0][1]).toHaveProperty('labelType', 'svg');
|
||||||
|
expect(addedNodes[0][1].label).toBeDefined();
|
||||||
|
expect(addedNodes[0][1].label).toBeDefined(); // <text> node
|
||||||
|
expect(addedNodes[0][1].label.firstChild.innerHTML).toEqual('Multi'); // <tspan> node, line 1
|
||||||
|
expect(addedNodes[0][1].label.lastChild.innerHTML).toEqual('Line'); // <tspan> node, line 2
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
[
|
[
|
||||||
@ -109,9 +146,11 @@ describe('the flowchart renderer', function() {
|
|||||||
{ text: 'Multi<br>Line' },
|
{ text: 'Multi<br>Line' },
|
||||||
{ text: 'Multi<br/>Line' },
|
{ text: 'Multi<br/>Line' },
|
||||||
{ text: 'Multi<br />Line' },
|
{ text: 'Multi<br />Line' },
|
||||||
|
{ text: 'Multi<br\t/>Line' },
|
||||||
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br>Line' },
|
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br>Line' },
|
||||||
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br/>Line' },
|
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br/>Line' },
|
||||||
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br />Line' }
|
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br />Line' },
|
||||||
|
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br\t/>Line' }
|
||||||
],
|
],
|
||||||
mockG,
|
mockG,
|
||||||
'svg-id'
|
'svg-id'
|
||||||
|
@ -86,6 +86,17 @@ describe('[Style] when parsing', () => {
|
|||||||
expect(vert['T'].styles[1]).toBe('border:1px solid red');
|
expect(vert['T'].styles[1]).toBe('border:1px solid red');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should keep node label text (if already defined) when a style is applied', function() {
|
||||||
|
const res = flow.parser.parse('graph TD;A(( ));B((Test));C;style A background:#fff;style D border:1px solid red;');
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
|
||||||
|
expect(vert['A'].text).toBe('');
|
||||||
|
expect(vert['B'].text).toBe('Test');
|
||||||
|
expect(vert['C'].text).toBe('C');
|
||||||
|
expect(vert['D'].text).toBe('D');
|
||||||
|
});
|
||||||
|
|
||||||
it('should be possible to declare a class', function() {
|
it('should be possible to declare a class', function() {
|
||||||
const res = flow.parser.parse(
|
const res = flow.parser.parse(
|
||||||
'graph TD;classDef exClass background:#bbb,border:1px solid red;'
|
'graph TD;classDef exClass background:#bbb,border:1px solid red;'
|
||||||
|
@ -16,6 +16,25 @@ export const addActor = function(id, name, description) {
|
|||||||
actors[id] = { name: name, description: description };
|
actors[id] = { name: name, description: description };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const activationCount = part => {
|
||||||
|
let i = 0;
|
||||||
|
let count = 0;
|
||||||
|
for (i = 0; i < messages.length; i++) {
|
||||||
|
// console.warn(i, messages[i]);
|
||||||
|
if (messages[i].type === LINETYPE.ACTIVE_START) {
|
||||||
|
if (messages[i].from.actor === part) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messages[i].type === LINETYPE.ACTIVE_END) {
|
||||||
|
if (messages[i].from.actor === part) {
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
export const addMessage = function(idFrom, idTo, message, answer) {
|
export const addMessage = function(idFrom, idTo, message, answer) {
|
||||||
messages.push({ from: idFrom, to: idTo, message: message, answer: answer });
|
messages.push({ from: idFrom, to: idTo, message: message, answer: answer });
|
||||||
};
|
};
|
||||||
@ -24,7 +43,25 @@ export const addSignal = function(idFrom, idTo, message, messageType) {
|
|||||||
logger.debug(
|
logger.debug(
|
||||||
'Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType
|
'Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (messageType === LINETYPE.ACTIVE_END) {
|
||||||
|
const cnt = activationCount(idFrom.actor);
|
||||||
|
logger.debug('Adding message from=', messages, cnt);
|
||||||
|
if (cnt < 1) {
|
||||||
|
// Bail out as there is an activation signal from an inactive participant
|
||||||
|
var error = new Error('Trying to inactivate an inactive participant (' + idFrom.actor + ')');
|
||||||
|
error.hash = {
|
||||||
|
text: '->>-',
|
||||||
|
token: '->>-',
|
||||||
|
line: '1',
|
||||||
|
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
|
||||||
|
expected: ["'ACTIVE_PARTICIPANT'"]
|
||||||
|
};
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
messages.push({ from: idFrom, to: idTo, message: message, type: messageType });
|
messages.push({ from: idFrom, to: idTo, message: message, type: messageType });
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMessages = function() {
|
export const getMessages = function() {
|
||||||
|
@ -214,6 +214,33 @@ describe('when parsing a sequenceDiagram', function() {
|
|||||||
expect(messages[7].type).toBe(parser.yy.LINETYPE.ACTIVE_END);
|
expect(messages[7].type).toBe(parser.yy.LINETYPE.ACTIVE_END);
|
||||||
expect(messages[7].from.actor).toBe('Carol');
|
expect(messages[7].from.actor).toBe('Carol');
|
||||||
});
|
});
|
||||||
|
it('it should handle fail parsing when activating an inactive participant', function() {
|
||||||
|
const str =
|
||||||
|
`sequenceDiagram
|
||||||
|
participant user as End User
|
||||||
|
participant Server as Server
|
||||||
|
participant System as System
|
||||||
|
participant System2 as System2
|
||||||
|
|
||||||
|
user->>+Server: Test
|
||||||
|
user->>+Server: Test2
|
||||||
|
user->>System: Test
|
||||||
|
Server->>-user: Test
|
||||||
|
Server->>-user: Test2
|
||||||
|
|
||||||
|
%% The following deactivation of Server will fail
|
||||||
|
Server->>-user: Test3`;
|
||||||
|
|
||||||
|
let error = false;
|
||||||
|
try {
|
||||||
|
parser.parse(str);
|
||||||
|
} catch(e) {
|
||||||
|
console.log(e.hash);
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
expect(error).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('it should handle comments in a sequenceDiagram', function() {
|
it('it should handle comments in a sequenceDiagram', function() {
|
||||||
const str =
|
const str =
|
||||||
'sequenceDiagram\n' +
|
'sequenceDiagram\n' +
|
||||||
|
@ -280,7 +280,7 @@ const drawForkJoinState = (g, stateDef) => {
|
|||||||
|
|
||||||
export const drawText = function(elem, textData) {
|
export const drawText = function(elem, textData) {
|
||||||
// Remove and ignore br:s
|
// Remove and ignore br:s
|
||||||
const nText = textData.text.replace(/<br\/?>/gi, ' ');
|
const nText = textData.text.replace(/<br\s*\/?>/gi, ' ');
|
||||||
|
|
||||||
const textElem = elem.append('text');
|
const textElem = elem.append('text');
|
||||||
textElem.attr('x', textData.x);
|
textElem.attr('x', textData.x);
|
||||||
@ -308,7 +308,7 @@ const _drawLongText = (_text, x, y, g) => {
|
|||||||
|
|
||||||
let text = _text.replace(/\r\n/g, '<br/>');
|
let text = _text.replace(/\r\n/g, '<br/>');
|
||||||
text = text.replace(/\n/g, '<br/>');
|
text = text.replace(/\n/g, '<br/>');
|
||||||
const lines = text.split(/<br\/?>/gi);
|
const lines = text.split(/<br\s*\/?>/gi);
|
||||||
|
|
||||||
let tHeight = 1.25 * getConfig().state.noteMargin;
|
let tHeight = 1.25 * getConfig().state.noteMargin;
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
@ -392,7 +392,7 @@ export const drawState = function(elem, stateDef) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getRows = s => {
|
const getRows = s => {
|
||||||
let str = s.replace(/<br\/?>/gi, '#br#');
|
let str = s.replace(/<br\s*\/?>/gi, '#br#');
|
||||||
str = str.replace(/\\n/g, '#br#');
|
str = str.replace(/\\n/g, '#br#');
|
||||||
return str.split('#br#');
|
return str.split('#br#');
|
||||||
};
|
};
|
||||||
|
@ -261,6 +261,16 @@ describe('state diagram, ', function() {
|
|||||||
|
|
||||||
parser.parse(str);
|
parser.parse(str);
|
||||||
});
|
});
|
||||||
|
it('should handle multiline notes with different line breaks', function() {
|
||||||
|
const str = `stateDiagram
|
||||||
|
State1
|
||||||
|
note right of State1
|
||||||
|
Line1<br>Line2<br/>Line3<br />Line4<br />Line5
|
||||||
|
end note
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
});
|
||||||
it('should handle floating notes', function() {
|
it('should handle floating notes', function() {
|
||||||
const str = `stateDiagram
|
const str = `stateDiagram
|
||||||
foo: bar
|
foo: bar
|
||||||
|
@ -102,7 +102,7 @@ const getLabelWidth = text => {
|
|||||||
/* TODO: REMOVE DUPLICATION, SEE SHAPES */
|
/* TODO: REMOVE DUPLICATION, SEE SHAPES */
|
||||||
const getRows = s => {
|
const getRows = s => {
|
||||||
if (!s) return 1;
|
if (!s) return 1;
|
||||||
let str = s.replace(/<br\/?>/gi, '#br#');
|
let str = s.replace(/<br\s*\/?>/gi, '#br#');
|
||||||
str = str.replace(/\\n/g, '#br#');
|
str = str.replace(/\\n/g, '#br#');
|
||||||
return str.split('#br#');
|
return str.split('#br#');
|
||||||
};
|
};
|
||||||
|
@ -98,7 +98,7 @@ const init = function() {
|
|||||||
txt = he
|
txt = he
|
||||||
.decode(txt)
|
.decode(txt)
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/<br>/gi, '<br/>');
|
.replace(/<br\s*\/?>/gi, '<br/>');
|
||||||
|
|
||||||
mermaidAPI.render(
|
mermaidAPI.render(
|
||||||
id,
|
id,
|
||||||
|
@ -52,14 +52,14 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
|
|||||||
* <pre>
|
* <pre>
|
||||||
* mermaid.initialize({
|
* mermaid.initialize({
|
||||||
* flowchart:{
|
* flowchart:{
|
||||||
* htmlLabels: false
|
* htmlLabels: false
|
||||||
* }
|
* }
|
||||||
* });
|
* });
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* **Example 2:**
|
* **Example 2:**
|
||||||
* <pre>
|
* <pre>
|
||||||
* <script>
|
* <script>
|
||||||
* var config = {
|
* var config = {
|
||||||
* startOnLoad:true,
|
* startOnLoad:true,
|
||||||
* flowchart:{
|
* flowchart:{
|
||||||
@ -71,7 +71,7 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
|
|||||||
* securityLevel:'loose',
|
* securityLevel:'loose',
|
||||||
* };
|
* };
|
||||||
* mermaid.initialize(config);
|
* mermaid.initialize(config);
|
||||||
* </script>
|
* </script>
|
||||||
* </pre>
|
* </pre>
|
||||||
* A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below.
|
* A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below.
|
||||||
*
|
*
|
||||||
@ -142,6 +142,20 @@ const config = {
|
|||||||
*/
|
*/
|
||||||
htmlLabels: true,
|
htmlLabels: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the spacing between nodes on the same level (meaning horizontal spacing for
|
||||||
|
* TB or BT graphs, and the vertical spacing for LR as well as RL graphs).
|
||||||
|
* **Default value 50**.
|
||||||
|
*/
|
||||||
|
nodeSpacing: 50,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the spacing between nodes on different levels (meaning vertical spacing for
|
||||||
|
* TB or BT graphs, and the horizontal spacing for LR as well as RL graphs).
|
||||||
|
* **Default value 50**.
|
||||||
|
*/
|
||||||
|
rankSpacing: 50,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How mermaid renders curves for flowcharts. Possible values are
|
* How mermaid renders curves for flowcharts. Possible values are
|
||||||
* * basis
|
* * basis
|
||||||
|
@ -84,8 +84,7 @@ export const sanitize = (text, config) => {
|
|||||||
htmlLabels = false;
|
htmlLabels = false;
|
||||||
|
|
||||||
if (config.securityLevel !== 'loose' && htmlLabels) { // eslint-disable-line
|
if (config.securityLevel !== 'loose' && htmlLabels) { // eslint-disable-line
|
||||||
txt = txt.replace(/<br>/g, '#br#');
|
txt = txt.replace(/<br\s*\/?>/gi, '#br#');
|
||||||
txt = txt.replace(/<br\S*?\/>/g, '#br#');
|
|
||||||
txt = txt.replace(/</g, '<').replace(/>/g, '>');
|
txt = txt.replace(/</g, '<').replace(/>/g, '>');
|
||||||
txt = txt.replace(/=/g, '=');
|
txt = txt.replace(/=/g, '=');
|
||||||
txt = txt.replace(/#br#/g, '<br/>');
|
txt = txt.replace(/#br#/g, '<br/>');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user