Merge branch 'release/8.13.0'
|
@ -1,4 +1,3 @@
|
|||
'Type: Bug / Error': 'bug/*'
|
||||
'Type: Enhancement': 'feature/*'
|
||||
'Type: Other': 'other/*'
|
||||
'Type: Dependabot': 'dependabot/*'
|
||||
|
|
|
@ -11,4 +11,4 @@ jobs:
|
|||
- uses: andymckay/labeler@1.0.3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
labels: "Status: Triage"
|
||||
add-labels: "Status: Triage"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: Apply labels to PR
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
|
|
|
@ -370,7 +370,21 @@ describe('Class diagram V2', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('16: should handle the direction statemment with TB', () => {
|
||||
|
||||
it('16a: should render a simple class diagram with static field', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Foo {
|
||||
+String bar$
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('16b: should handle the direction statemnent with TB', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
|
@ -394,55 +408,8 @@ describe('Class diagram V2', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('17: should handle the direction statemment with BT', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
direction BT
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides
|
||||
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('17: should handle the direction statemment with RL', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
direction RL
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides
|
||||
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('18: should handle the direction statemment with LR', () => {
|
||||
|
||||
it('18: should handle the direction statemnent with LR', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
|
@ -466,4 +433,52 @@ describe('Class diagram V2', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('17a: should handle the direction statemnent with BT', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
direction BT
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides
|
||||
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('17b: should handle the direction statemment with RL', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
direction RL
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides
|
||||
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -186,4 +186,15 @@ describe('Entity Relationship Diagram', () => {
|
|||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render entities with keys and comments', () => {
|
||||
renderGraph(
|
||||
`
|
||||
erDiagram
|
||||
BOOK { string title PK "comment"}
|
||||
`,
|
||||
{ logLevel : 1 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -173,6 +173,18 @@ context('Sequence diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
it('should be possible to use actor symbols instead of boxes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
actor Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render long notes left of actor', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
<style>
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
background:#333;
|
||||
/* background:#333; */
|
||||
font-family: 'Arial';
|
||||
/* font-size: 18px !important; */
|
||||
width: 100%;
|
||||
}
|
||||
h1 { color: grey;}
|
||||
.mermaid2,.mermaid3 {
|
||||
|
@ -25,7 +26,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div>info below</div>
|
||||
<div class="flex">
|
||||
<div class="flex flex-wrap">
|
||||
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
classDiagram
|
||||
|
@ -53,28 +54,56 @@ stateDiagram
|
|||
|
||||
</div>
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
flowchart LR
|
||||
one --> two
|
||||
three -.-> four[whoa, big arrowhead nine o'clock]
|
||||
sequenceDiagram
|
||||
%%{init: {'config': {'wrap': true }}}%%
|
||||
actor Alice as Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
actor Bob
|
||||
participant John as John2
|
||||
participant Mandy
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
Alice->>John: Hi John
|
||||
John->>Mandy: Hi Mandy
|
||||
Mandy ->>Joan: Hi Joan
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
%%{init: { "apa":"b", "theme":"forest"}}%%
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
%%{int: { "apa":"b", "theme":"forest"}}%%
|
||||
sequenceDiagram
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
</div>
|
||||
<div class="mermaid2">
|
||||
%%{init: { 'theme':'base', '__proto__': {'polluted': 'asdf'}} }%%
|
||||
%%{init: { 'theme':'base', '__proto__': {'polluted': 'asdf'}} }%%
|
||||
graph LR
|
||||
A --> B
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
flowchart TD
|
||||
Link --> b
|
||||
click Link href "javascript:alert('XSS')" "Tooltip for
|
||||
Amet"
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
par Action 1
|
||||
Alice->>John: Hello John, how are you?
|
||||
and Action 2
|
||||
Alice->>Bob: Hello Bob, how are you?
|
||||
end
|
||||
Alice->>+John: Hello John, how are you?
|
||||
Alice->>+John: John, can you hear me?
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
Note right of John: John is perceptive
|
||||
John-->>-Alice: I feel great!
|
||||
loop Every minute
|
||||
John-->Alice: Great!
|
||||
end
|
||||
</div>
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
%%{init: {'config': {'wrap': true }}}%%
|
||||
actor Alice
|
||||
actor Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
</div>
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
%%{init: {'config': {'wrap': true }}}%%
|
||||
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 !
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
stateDiagram-v2
|
||||
|
@ -112,8 +141,8 @@ YourState
|
|||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
theme: 'dark',
|
||||
// theme: 'forest',
|
||||
// theme: 'dark',
|
||||
theme: 'forest',
|
||||
arrowMarkerAbsolute: true,
|
||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||
logLevel: 2,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
- Introduction 📔
|
||||
- 📔 Introduction
|
||||
|
||||
- [About Mermaid](README.md)
|
||||
- [Deployment](n00b-gettingStarted.md)
|
||||
- [Syntax and Configuration](n00b-syntaxReference.md)
|
||||
|
||||
- Diagram Syntax 📊
|
||||
- 📊 Diagram Syntax
|
||||
- [Flowchart](flowchart.md)
|
||||
- [Sequence diagram](sequenceDiagram.md)
|
||||
- [Class Diagram](classDiagram.md)
|
||||
|
@ -16,21 +16,21 @@
|
|||
- [Requirement Diagram](requirementDiagram.md)
|
||||
- [Other Examples](examples.md)
|
||||
|
||||
- Deployment and Configuration ⚙️
|
||||
- ⚙️ Deployment and Configuration
|
||||
|
||||
- [Tutorials](Tutorials.md)
|
||||
- [API-Usage](usage.md)
|
||||
- [Mermaid API Configuration](Setup.md)
|
||||
- [Directives](directives.md)
|
||||
- [Theming](theming.md)
|
||||
- [mermaid CLI](mermaidCLI.md)
|
||||
- [Mermaid CLI](mermaidCLI.md)
|
||||
- [Advanced usage](n00b-advanced.md)
|
||||
|
||||
- Misc 📚
|
||||
- 📚 Misc
|
||||
- [Use-Cases and Integrations](integrations.md)
|
||||
- [FAQ](faq.md)
|
||||
|
||||
- Contributions and Community 🙌
|
||||
- 🙌 Contributions and Community
|
||||
- [Overview for Beginners](n00b-overview.md)
|
||||
- [Development and Contribution ](development.md)
|
||||
- [Changelog](CHANGELOG.md)
|
||||
|
|
|
@ -228,10 +228,14 @@ To specify the visibility of a class member (i.e. any attribute or method), thes
|
|||
- `~` Package/Internal
|
||||
|
||||
> _note_ you can also include additional _classifiers_ to a method definition by adding the following notations to the end of the method, i.e.: after the `()`:
|
||||
|
||||
> - `*` Abstract e.g.: `someAbstractMethod()*`
|
||||
> - `$` Static e.g.: `someStaticMethod()$`
|
||||
|
||||
> _note_ you can also include additional _classifiers_ to a field definition by adding the following notations to the end of the field name:
|
||||
> - `*` Static e.g.: `String someField$`
|
||||
|
||||
|
||||
|
||||
## Defining Relationship
|
||||
|
||||
A relationship is a general term covering the specific types of logical connections found on class and object diagrams.
|
||||
|
|
|
@ -726,6 +726,26 @@ defined in the linkStyle statement will belong to the fourth link in the graph:
|
|||
linkStyle 3 stroke:#ff3,stroke-width:4px,color:red;
|
||||
```
|
||||
|
||||
You can specify default to apply to all links, or you can give a list of link order numbers seperated by a comma.
|
||||
|
||||
Instead of giving a styles option, you can also use custom d3 curve types with the following syntax:
|
||||
|
||||
```
|
||||
linkStyle default|numList|num interpolate curveType
|
||||
```
|
||||
|
||||
If you want to add style options too, instead of writing:
|
||||
|
||||
```
|
||||
linkStyle default interpolate cardinal
|
||||
linkStyle default stroke:#ff3,stroke-width:4px,color:red;
|
||||
```
|
||||
|
||||
You can combine them into:
|
||||
|
||||
```
|
||||
linkStyle default interpolate cardinal stroke:#ff3,stroke-width:4px,color:red;
|
||||
```
|
||||
|
||||
### Styling a node
|
||||
|
||||
|
|
|
@ -191,6 +191,28 @@ erDiagram
|
|||
|
||||
The `type` and `name` values must begin with an alphabetic character and may contain digits, hyphens or underscores. Other than that, there are no restrictions, and there is no implicit set of valid data types.
|
||||
|
||||
#### Attribute Keys and Comments
|
||||
|
||||
Attributes may also have a `key` or comment defined. Keys can be "PK" or "FK", for Primary Key or Foreign Key. And a `comment` is defined by quotes at the end of an attribute. Comments themselves cannot have quote characters in them.
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
CAR ||--o{ NAMED-DRIVER : allows
|
||||
CAR {
|
||||
string allowedDriver FK 'The license of the allowed driver'
|
||||
string registrationNumber
|
||||
string make
|
||||
string model
|
||||
}
|
||||
PERSON ||--o{ NAMED-DRIVER : is
|
||||
PERSON {
|
||||
string driversLicense PK 'The license #'
|
||||
string firstName
|
||||
string lastName
|
||||
int age
|
||||
}
|
||||
```
|
||||
|
||||
### Other Things
|
||||
|
||||
- If you want the relationship label to be more than one word, you must use double quotes around the phrase
|
||||
|
|
Before Width: | Height: | Size: 321 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 878 B |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 809 KiB |
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 809 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 103 KiB |
|
@ -7,8 +7,9 @@
|
|||
<meta name="description" content="Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<!-- <link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css"> -->
|
||||
<link rel="stylesheet" href="theme.css"> <script src="//cdn.jsdelivr.net/npm/mermaid@8.12.0/dist/mermaid.min.js"></script>
|
||||
<!-- <script src="http://localhost:9000/mermaid.js"></script> -->
|
||||
<link rel="stylesheet" href="theme.css">
|
||||
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@8.12.0/dist/mermaid.min.js"></script> -->
|
||||
<script src="http://localhost:9000/mermaid.js"></script>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
|
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
@ -22,6 +22,17 @@
|
|||
text-shadow: #7557c9 0px 0px 5px
|
||||
}
|
||||
</style>
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-153180559-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<!-- End Google Analytics -->
|
||||
</head>
|
||||
<body class="leading-normal tracking-normal text-white gradient" style="font-family: 'Source Sans Pro', sans-serif;">
|
||||
<!--Nav-->
|
||||
|
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 15 KiB |
|
@ -19,7 +19,7 @@ This section talks about the different ways to deploy Mermaid. Learning the [Syn
|
|||
|
||||
> More in depth information can be found on [Usage](./usage.md).
|
||||
|
||||
## 1. Using [The Live Editor](https://mermaidjs.github.io/mermaid-live-editor/edit).
|
||||
## 1. Using [The Live Editor](https://mermaid-js.github.io/mermaid-live-editor/edit).
|
||||
|
||||
![EditingProcess](./img/Editing-process.png)
|
||||
|
||||
|
|
|
@ -36,18 +36,53 @@ appearance by doing the following:
|
|||
|
||||
```
|
||||
sequenceDiagram
|
||||
participant John
|
||||
participant Alice
|
||||
Alice->>John: Hello John, how are you?
|
||||
John-->>Alice: Great!
|
||||
participant Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
### Actors
|
||||
|
||||
If you specifically want to use the actor symbol instead of a rectangle with text you can do so by using actor statements as per below.
|
||||
```
|
||||
sequenceDiagram
|
||||
participant John
|
||||
participant Alice
|
||||
Alice->>John: Hello John, how are you?
|
||||
John-->>Alice: Great!
|
||||
actor Alice
|
||||
actor Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
actor Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
```
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
actor Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
actor Bob
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
### Aliases
|
||||
|
|
|
@ -292,17 +292,16 @@ module.exports = (options) ->
|
|||
|
||||
## Advanced usage
|
||||
|
||||
**Error handling**
|
||||
**Syntax validation without rendering (Work in Progress)**
|
||||
|
||||
The **mermaid.parse(txt)** function validates graph definitions without rendering a graph. **[This function is still a work in progress](https://github.com/mermaid-js/mermaid/issues/1066), find alternatives below.**
|
||||
|
||||
The function **mermaid.parse(txt)**, takes a text string as an argument and returns true if the definition follows mermaid's syntax and
|
||||
false if it does not. The parseError function will be called when the parse function returns false.
|
||||
|
||||
When the parser encounters invalid syntax the **mermaid.parseError** function is called. It is possible to override this
|
||||
function in order to handle the error in an application-specific way.
|
||||
|
||||
**Parsing text without rendering**
|
||||
|
||||
It is also possible to validate the syntax before rendering in order to streamline the user experience. The function
|
||||
**mermaid.parse(txt)** takes a text string as an argument and returns true if the text is syntactically correct and
|
||||
false if it is not. The parseError function will be called when the parse function returns false.
|
||||
|
||||
The code-example below in meta code illustrates how this could work:
|
||||
|
||||
```javascript
|
||||
|
@ -320,6 +319,8 @@ var textFieldUpdated = function(){
|
|||
|
||||
bindEventHandler('change', 'code', textFieldUpdated);
|
||||
```
|
||||
**Alternative to mermaid.parse():**
|
||||
One effective and more future-proof method of validating your graph deinitions, is to paste and render them via the [Mermaid Live Editor](https://mermaid-js.github.io/mermaid-live-editor/). This will ensure that your code is compliant with the syntax of Mermaid's most recent version.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
@ -395,9 +396,7 @@ This way of setting the configuration is deprecated. Instead the preferred way i
|
|||
|
||||
## Using the mermaid.init call
|
||||
|
||||
#
|
||||
Is it possible to set some configuration via the mermaid object. The two parameters that are supported using this
|
||||
approach are:
|
||||
To set some configuration via the mermaid object. The two parameters that are supported using this approach are:
|
||||
|
||||
* mermaid_config.startOnLoad
|
||||
* mermaid_config.htmlLabels
|
||||
|
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 32 KiB |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mermaid",
|
||||
"version": "8.12.0",
|
||||
"version": "8.13.0",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid.core.js",
|
||||
"keywords": [
|
||||
|
|
|
@ -672,13 +672,21 @@ const class_box = (parent, node) => {
|
|||
}
|
||||
const classAttributes = [];
|
||||
node.classData.members.forEach((str) => {
|
||||
let parsedText = parseMember(str).displayText;
|
||||
const parsedInfo = parseMember(str);
|
||||
let parsedText = parsedInfo.displayText;
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
parsedText = parsedText.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
const lbl = labelContainer
|
||||
.node()
|
||||
.appendChild(createLabel(parsedText, node.labelStyle, true, true));
|
||||
.appendChild(
|
||||
createLabel(
|
||||
parsedText,
|
||||
parsedInfo.cssStyle ? parsedInfo.cssStyle : node.labelStyle,
|
||||
true,
|
||||
true
|
||||
)
|
||||
);
|
||||
let bbox = lbl.getBBox();
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
const div = lbl.children[0];
|
||||
|
|
|
@ -274,7 +274,7 @@ export const drawClass = function (elem, classDef, conf) {
|
|||
};
|
||||
|
||||
export const parseMember = function (text) {
|
||||
const fieldRegEx = /(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+)/;
|
||||
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+) *(\*|\$)?$/;
|
||||
const methodRegEx = /^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/;
|
||||
|
||||
let fieldMatch = text.match(fieldRegEx);
|
||||
|
@ -290,6 +290,7 @@ export const parseMember = function (text) {
|
|||
};
|
||||
|
||||
const buildFieldDisplay = function (parsedText) {
|
||||
let cssStyle = '';
|
||||
let displayText = '';
|
||||
|
||||
try {
|
||||
|
@ -297,15 +298,17 @@ const buildFieldDisplay = function (parsedText) {
|
|||
let fieldType = parsedText[2] ? parsedText[2].trim() : '';
|
||||
let genericType = parsedText[3] ? parseGenericTypes(parsedText[3].trim()) : '';
|
||||
let fieldName = parsedText[4] ? parsedText[4].trim() : '';
|
||||
let classifier = parsedText[5] ? parsedText[5].trim() : '';
|
||||
|
||||
displayText = visibility + fieldType + genericType + ' ' + fieldName;
|
||||
cssStyle = parseClassifier(classifier);
|
||||
} catch (err) {
|
||||
displayText = parsedText;
|
||||
}
|
||||
|
||||
return {
|
||||
displayText: displayText,
|
||||
cssStyle: '',
|
||||
cssStyle: cssStyle,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -321,7 +324,6 @@ const buildMethodDisplay = function (parsedText) {
|
|||
let returnType = parsedText[5] ? ' : ' + parseGenericTypes(parsedText[5]).trim() : '';
|
||||
|
||||
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
|
||||
|
||||
cssStyle = parseClassifier(classifier);
|
||||
} catch (err) {
|
||||
displayText = parsedText;
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('class member Renderer, ', function () {
|
|||
expect(actual.cssStyle).toBe('');
|
||||
});
|
||||
|
||||
it('should handle abstract classifier', function () {
|
||||
it('should handle abstract method classifier', function () {
|
||||
const str = 'foo()*';
|
||||
let actual = svgDraw.parseMember(str);
|
||||
|
||||
|
@ -59,7 +59,7 @@ describe('class member Renderer, ', function () {
|
|||
expect(actual.cssStyle).toBe('font-style:italic;');
|
||||
});
|
||||
|
||||
it('should handle static classifier', function () {
|
||||
it('should handle static method classifier', function () {
|
||||
const str = 'foo()$';
|
||||
let actual = svgDraw.parseMember(str);
|
||||
|
||||
|
@ -156,5 +156,13 @@ describe('class member Renderer, ', function () {
|
|||
expect(actual.displayText).toBe('List<int> ids');
|
||||
expect(actual.cssStyle).toBe('');
|
||||
});
|
||||
|
||||
it('should handle static field classifier', function () {
|
||||
const str = 'String foo$';
|
||||
let actual = svgDraw.parseMember(str);
|
||||
|
||||
expect(actual.displayText).toBe('String foo');
|
||||
expect(actual.cssStyle).toBe('text-decoration:underline;');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,13 +35,20 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||
const attrFontSize = conf.fontSize * 0.85;
|
||||
const labelBBox = entityTextNode.node().getBBox();
|
||||
const attributeNodes = []; // Intermediate storage for attribute nodes created so that we can do a second pass
|
||||
let hasKeyType = false;
|
||||
let hasComment = false;
|
||||
let maxWidth = 0;
|
||||
let maxTypeWidth = 0;
|
||||
let maxNameWidth = 0;
|
||||
let maxKeyWidth = 0;
|
||||
let maxCommentWidth = 0;
|
||||
let cumulativeHeight = labelBBox.height + heightPadding * 2;
|
||||
let attrNum = 1;
|
||||
|
||||
attributes.forEach((item) => {
|
||||
const attrPrefix = `${entityTextNode.node().id}-attr-${attrNum}`;
|
||||
let nodeWidth = 0;
|
||||
let nodeHeight = 0;
|
||||
|
||||
// Add a text node for the attribute type
|
||||
const typeNode = groupNode
|
||||
|
@ -73,16 +80,70 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||
)
|
||||
.text(item.attributeName);
|
||||
|
||||
// Keep a reference to the nodes so that we can iterate through them later
|
||||
attributeNodes.push({ tn: typeNode, nn: nameNode });
|
||||
const attributeNode = {};
|
||||
attributeNode.tn = typeNode;
|
||||
attributeNode.nn = nameNode;
|
||||
|
||||
const typeBBox = typeNode.node().getBBox();
|
||||
const nameBBox = nameNode.node().getBBox();
|
||||
|
||||
maxTypeWidth = Math.max(maxTypeWidth, typeBBox.width);
|
||||
maxNameWidth = Math.max(maxNameWidth, nameBBox.width);
|
||||
nodeWidth += typeBBox.width;
|
||||
nodeWidth += nameBBox.width;
|
||||
|
||||
cumulativeHeight += Math.max(typeBBox.height, nameBBox.height) + heightPadding * 2;
|
||||
nodeHeight = Math.max(typeBBox.height, nameBBox.height);
|
||||
|
||||
if (hasKeyType || item.attributeKeyType !== undefined) {
|
||||
const keyTypeNode = groupNode
|
||||
.append('text')
|
||||
.attr('class', 'er entityLabel')
|
||||
.attr('id', `${attrPrefix}-name`)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'left')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px'
|
||||
)
|
||||
.text(item.attributeKeyType || '');
|
||||
|
||||
attributeNode.kn = keyTypeNode;
|
||||
const keyTypeBBox = keyTypeNode.node().getBBox();
|
||||
nodeWidth += keyTypeBBox.width;
|
||||
maxKeyWidth = Math.max(maxKeyWidth, nodeWidth);
|
||||
nodeHeight = Math.max(nodeHeight, keyTypeBBox.height);
|
||||
hasKeyType = true;
|
||||
}
|
||||
|
||||
if (hasComment || item.attributeComment !== undefined) {
|
||||
const commentNode = groupNode
|
||||
.append('text')
|
||||
.attr('class', 'er entityLabel')
|
||||
.attr('id', `${attrPrefix}-name`)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'left')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px'
|
||||
)
|
||||
.text(item.attributeComment || '');
|
||||
|
||||
attributeNode.cn = commentNode;
|
||||
const commentNodeBBox = commentNode.node().getBBox();
|
||||
nodeWidth += commentNodeBBox.width;
|
||||
maxCommentWidth = Math.max(nodeWidth, nameBBox.width);
|
||||
nodeHeight = Math.max(nodeHeight, commentNodeBBox.height);
|
||||
hasComment = true;
|
||||
}
|
||||
|
||||
attributeNode.height = nodeHeight;
|
||||
// Keep a reference to the nodes so that we can iterate through them later
|
||||
attributeNodes.push(attributeNode);
|
||||
maxWidth = Math.max(maxWidth, nodeWidth);
|
||||
cumulativeHeight += nodeHeight + heightPadding * 2;
|
||||
attrNum += 1;
|
||||
});
|
||||
|
||||
|
@ -90,10 +151,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||
const bBox = {
|
||||
width: Math.max(
|
||||
conf.minEntityWidth,
|
||||
Math.max(
|
||||
labelBBox.width + conf.entityPadding * 2,
|
||||
maxTypeWidth + maxNameWidth + widthPadding * 4
|
||||
)
|
||||
Math.max(labelBBox.width + conf.entityPadding * 2, maxWidth + widthPadding * 4)
|
||||
),
|
||||
height:
|
||||
attributes.length > 0
|
||||
|
@ -102,7 +160,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||
};
|
||||
|
||||
// There might be some spare width for padding out attributes if the entity name is very long
|
||||
const spareWidth = Math.max(0, bBox.width - (maxTypeWidth + maxNameWidth) - widthPadding * 4);
|
||||
const spareWidth = Math.max(0, bBox.width - maxWidth - widthPadding * 4);
|
||||
|
||||
if (attributes.length > 0) {
|
||||
// Position the entity label near the top of the entity bounding box
|
||||
|
@ -115,51 +173,85 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||
let heightOffset = labelBBox.height + heightPadding * 2; // Start at the bottom of the entity label
|
||||
let attribStyle = 'attributeBoxOdd'; // We will flip the style on alternate rows to achieve a banded effect
|
||||
|
||||
attributeNodes.forEach((nodePair) => {
|
||||
attributeNodes.forEach((attributeNode) => {
|
||||
// Calculate the alignment y co-ordinate for the type/name of the attribute
|
||||
const alignY =
|
||||
heightOffset +
|
||||
heightPadding +
|
||||
Math.max(nodePair.tn.node().getBBox().height, nodePair.nn.node().getBBox().height) / 2;
|
||||
const alignY = heightOffset + heightPadding + attributeNode.height / 2;
|
||||
|
||||
// Position the type of the attribute
|
||||
nodePair.tn.attr('transform', 'translate(' + widthPadding + ',' + alignY + ')');
|
||||
attributeNode.tn.attr('transform', 'translate(' + widthPadding + ',' + alignY + ')');
|
||||
|
||||
// Insert a rectangle for the type
|
||||
const typeRect = groupNode
|
||||
.insert('rect', '#' + nodePair.tn.node().id)
|
||||
.insert('rect', '#' + attributeNode.tn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.attr('x', 0)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxTypeWidth + widthPadding * 2 + spareWidth / 2)
|
||||
.attr('height', nodePair.tn.node().getBBox().height + heightPadding * 2);
|
||||
.attr('width', maxTypeWidth * 2 + spareWidth / 2)
|
||||
.attr('height', attributeNode.tn.node().getBBox().height + heightPadding * 2);
|
||||
|
||||
// Position the name of the attribute
|
||||
nodePair.nn.attr(
|
||||
attributeNode.nn.attr(
|
||||
'transform',
|
||||
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
|
||||
);
|
||||
|
||||
// Insert a rectangle for the name
|
||||
groupNode
|
||||
.insert('rect', '#' + nodePair.nn.node().id)
|
||||
.insert('rect', '#' + attributeNode.nn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
|
||||
//.attr('x', maxTypeWidth + (widthPadding * 2))
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxNameWidth + widthPadding * 2 + spareWidth / 2)
|
||||
.attr('height', nodePair.nn.node().getBBox().height + heightPadding * 2);
|
||||
.attr('height', attributeNode.nn.node().getBBox().height + heightPadding * 2);
|
||||
|
||||
if (hasKeyType) {
|
||||
// Position the name of the attribute
|
||||
attributeNode.kn.attr(
|
||||
'transform',
|
||||
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
|
||||
);
|
||||
|
||||
// Insert a rectangle for the name
|
||||
groupNode
|
||||
.insert('rect', '#' + attributeNode.kn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxKeyWidth + widthPadding * 2 + spareWidth / 2)
|
||||
.attr('height', attributeNode.kn.node().getBBox().height + heightPadding * 2);
|
||||
}
|
||||
|
||||
if (hasComment) {
|
||||
// Position the name of the attribute
|
||||
attributeNode.cn.attr(
|
||||
'transform',
|
||||
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
|
||||
);
|
||||
|
||||
// Insert a rectangle for the name
|
||||
groupNode
|
||||
.insert('rect', '#' + attributeNode.cn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxCommentWidth + widthPadding * 2 + spareWidth / 2)
|
||||
.attr('height', attributeNode.cn.node().getBBox().height + heightPadding * 2);
|
||||
}
|
||||
|
||||
// Increment the height offset to move to the next row
|
||||
heightOffset +=
|
||||
Math.max(nodePair.tn.node().getBBox().height, nodePair.nn.node().getBBox().height) +
|
||||
heightPadding * 2;
|
||||
heightOffset += attributeNode.height + heightPadding * 2;
|
||||
|
||||
// Flip the attribute style for row banding
|
||||
attribStyle = attribStyle == 'attributeBoxOdd' ? 'attributeBoxEven' : 'attributeBoxOdd';
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
"erDiagram" return 'ER_DIAGRAM';
|
||||
"{" { this.begin("block"); return 'BLOCK_START'; }
|
||||
<block>\s+ /* skip whitespace in block */
|
||||
<block>[A-Za-z][A-Za-z0-9\-_]* { return 'ATTRIBUTE_WORD'; }
|
||||
<block>(?:PK)|(?:FK) return 'ATTRIBUTE_KEY'
|
||||
<block>[A-Za-z][A-Za-z0-9\-_]* return 'ATTRIBUTE_WORD'
|
||||
<block>\"[^"]*\" return 'COMMENT';
|
||||
<block>[\n]+ /* nothing */
|
||||
<block>"}" { this.popState(); return 'BLOCK_STOP'; }
|
||||
<block>. return yytext[0];
|
||||
|
@ -95,6 +97,9 @@ attributes
|
|||
|
||||
attribute
|
||||
: attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; }
|
||||
| attributeType attributeName attributeKeyType { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3 }; }
|
||||
| attributeType attributeName COMMENT { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; }
|
||||
| attributeType attributeName attributeKeyType COMMENT { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3, attributeComment: $4 }; }
|
||||
;
|
||||
|
||||
attributeType
|
||||
|
@ -105,6 +110,10 @@ attributeName
|
|||
: ATTRIBUTE_WORD { $$=$1; }
|
||||
;
|
||||
|
||||
attributeKeyType
|
||||
: ATTRIBUTE_KEY { $$=$1; }
|
||||
;
|
||||
|
||||
relSpec
|
||||
: cardinality relType cardinality
|
||||
{
|
||||
|
|
|
@ -42,6 +42,36 @@ describe('when parsing ER diagram it...', function () {
|
|||
expect(entities[entity].attributes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow an entity with a single attribute to be defined with a key', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute = 'string title PK';
|
||||
|
||||
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
|
||||
const entities = erDb.getEntities();
|
||||
expect(Object.keys(entities).length).toBe(1);
|
||||
expect(entities[entity].attributes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow an entity with a single attribute to be defined with a comment', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute = `string title "comment"`;
|
||||
|
||||
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
|
||||
const entities = erDb.getEntities();
|
||||
expect(Object.keys(entities).length).toBe(1);
|
||||
expect(entities[entity].attributes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow an entity with a single attribute to be defined with a key and a comment', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute = `string title PK "comment"`;
|
||||
|
||||
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
|
||||
const entities = erDb.getEntities();
|
||||
expect(Object.keys(entities).length).toBe(1);
|
||||
expect(entities[entity].attributes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow an entity with multiple attributes to be defined', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute1 = 'string title';
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
\%%(?!\{)[^\n]* /* skip comments */
|
||||
[^\}]\%\%[^\n]* /* skip comments */
|
||||
"participant" { this.begin('ID'); return 'participant'; }
|
||||
"actor" { this.begin('ID'); return 'participant_actor'; }
|
||||
<ID>[^\->:\n,;]+?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
||||
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
|
||||
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
|
||||
|
@ -103,8 +104,10 @@ directive
|
|||
;
|
||||
|
||||
statement
|
||||
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.description=yy.parseMessage($4); $$=$2;}
|
||||
| 'participant' actor 'NEWLINE' {$$=$2;}
|
||||
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
||||
| 'participant' actor 'NEWLINE' {$2.type='addParticipant';$$=$2;}
|
||||
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.type='addActor';$2.description=yy.parseMessage($4); $$=$2;}
|
||||
| 'participant_actor' actor 'NEWLINE' {$2.type='addActor'; $$=$2;}
|
||||
| signal 'NEWLINE'
|
||||
| autonumber {yy.enableSequenceNumbers()}
|
||||
| 'activate' actor 'NEWLINE' {$$={type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $2};}
|
||||
|
@ -197,9 +200,13 @@ signal
|
|||
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
||||
;
|
||||
|
||||
actor
|
||||
: ACTOR {$$={type: 'addActor', actor:$1}}
|
||||
;
|
||||
// actor
|
||||
// : actor_participant
|
||||
// | actor_actor
|
||||
// ;
|
||||
|
||||
actor: ACTOR {$$={ type: 'addParticipant', actor:$1}};
|
||||
// actor_actor: ACTOR {$$={type: 'addActor', actor:$1}};
|
||||
|
||||
signaltype
|
||||
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
||||
|
|
|
@ -15,14 +15,17 @@ export const parseDirective = function (statement, context, type) {
|
|||
mermaidAPI.parseDirective(this, statement, context, type);
|
||||
};
|
||||
|
||||
export const addActor = function (id, name, description) {
|
||||
export const addActor = function (id, name, description, type) {
|
||||
// Don't allow description nulling
|
||||
const old = actors[id];
|
||||
if (old && name === old.name && description == null) return;
|
||||
|
||||
// Don't allow null descriptions, either
|
||||
if (description == null || description.text == null) {
|
||||
description = { text: name, wrap: null };
|
||||
description = { text: name, wrap: null, type };
|
||||
}
|
||||
if (type == null || description.text == null) {
|
||||
description = { text: name, wrap: null, type };
|
||||
}
|
||||
|
||||
actors[id] = {
|
||||
|
@ -30,6 +33,7 @@ export const addActor = function (id, name, description) {
|
|||
description: description.text,
|
||||
wrap: (description.wrap === undefined && autoWrap()) || !!description.wrap,
|
||||
prevActor: prevActor,
|
||||
type: type || 'participant',
|
||||
};
|
||||
if (prevActor && actors[prevActor]) {
|
||||
actors[prevActor].nextActor = id;
|
||||
|
@ -218,8 +222,11 @@ export const apply = function (param) {
|
|||
});
|
||||
} else {
|
||||
switch (param.type) {
|
||||
case 'addParticipant':
|
||||
addActor(param.actor, param.actor, param.description, 'participant');
|
||||
break;
|
||||
case 'addActor':
|
||||
addActor(param.actor, param.actor, param.description);
|
||||
addActor(param.actor, param.actor, param.description, 'actor');
|
||||
break;
|
||||
case 'activeStart':
|
||||
addSignal(param.actor, undefined, undefined, param.signalType);
|
||||
|
|
|
@ -121,6 +121,55 @@ B-->A: I am good thanks!`;
|
|||
|
||||
mermaidAPI.parse(str);
|
||||
|
||||
const actors = parser.yy.getActors();
|
||||
|
||||
expect(Object.keys(actors)).toEqual(['A', 'B']);
|
||||
expect(actors.A.description).toBe('Alice');
|
||||
expect(actors.B.description).toBe('Bob');
|
||||
|
||||
const messages = parser.yy.getMessages();
|
||||
expect(messages.length).toBe(2);
|
||||
expect(messages[0].from).toBe('A');
|
||||
expect(messages[1].from).toBe('B');
|
||||
});
|
||||
it('it should alias a mix of actors and participants apa12', function() {
|
||||
const str = `
|
||||
sequenceDiagram
|
||||
actor Alice as Alice2
|
||||
actor Bob
|
||||
participant John as John2
|
||||
participant Mandy
|
||||
Alice->>Bob: Hi Bob
|
||||
Bob->>Alice: Hi Alice
|
||||
Alice->>John: Hi John
|
||||
John->>Mandy: Hi Mandy
|
||||
Mandy ->>Joan: Hi Joan`;
|
||||
|
||||
mermaidAPI.parse(str);
|
||||
|
||||
const actors = parser.yy.getActors();
|
||||
expect(Object.keys(actors)).toEqual(['Alice', 'Bob', 'John', 'Mandy', 'Joan']);
|
||||
expect(actors.Alice.description).toBe('Alice2');
|
||||
expect(actors.Alice.type).toBe('actor');
|
||||
expect(actors.Bob.description).toBe('Bob');
|
||||
expect(actors.John.type).toBe('participant');
|
||||
expect(actors.Joan.type).toBe('participant');
|
||||
|
||||
const messages = parser.yy.getMessages();
|
||||
expect(messages.length).toBe(5);
|
||||
expect(messages[0].from).toBe('Alice');
|
||||
expect(messages[4].to).toBe('Joan');
|
||||
});
|
||||
it('it should alias actors apa13', function() {
|
||||
const str = `
|
||||
sequenceDiagram
|
||||
actor A as Alice
|
||||
actor B as Bob
|
||||
A->B:Hello Bob, how are you?
|
||||
B-->A: I am good thanks!`;
|
||||
|
||||
mermaidAPI.parse(str);
|
||||
|
||||
const actors = parser.yy.getActors();
|
||||
expect(Object.keys(actors)).toEqual(['A', 'B']);
|
||||
expect(actors.A.description).toBe('Alice');
|
||||
|
@ -1452,7 +1501,7 @@ participant Alice`;
|
|||
expect(bounds.startx).toBe(0);
|
||||
expect(bounds.starty).toBe(0);
|
||||
expect(bounds.stopx).toBe(conf.width);
|
||||
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
|
||||
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + conf.boxMargin);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1501,7 +1550,7 @@ participant Alice
|
|||
expect(bounds.startx).toBe(0);
|
||||
expect(bounds.startx).toBe(0);
|
||||
expect(bounds.starty).toBe(0);
|
||||
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
|
||||
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin);
|
||||
});
|
||||
it('it should handle one actor, when logLevel is 3', function() {
|
||||
const str = `
|
||||
|
@ -1519,6 +1568,6 @@ participant Alice
|
|||
expect(bounds.startx).toBe(0);
|
||||
expect(bounds.startx).toBe(0);
|
||||
expect(bounds.starty).toBe(0);
|
||||
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
|
||||
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { select, selectAll } from 'd3';
|
||||
import svgDraw, { drawText } from './svgDraw';
|
||||
import svgDraw, { drawText, fixLifeLineHeights } from './svgDraw';
|
||||
import { log } from '../../logger';
|
||||
import { parser } from './parser/sequenceDiagram';
|
||||
import common from '../common/common';
|
||||
|
@ -421,7 +421,7 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
|||
// Draw the actors
|
||||
let prevWidth = 0;
|
||||
let prevMargin = 0;
|
||||
|
||||
let maxHeight = 0;
|
||||
for (let i = 0; i < actorKeys.length; i++) {
|
||||
const actor = actors[actorKeys[i]];
|
||||
|
||||
|
@ -434,7 +434,8 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
|||
actor.y = verticalPos;
|
||||
|
||||
// Draw the box with the attached line
|
||||
svgDraw.drawActor(diagram, actor, conf);
|
||||
const height = svgDraw.drawActor(diagram, actor, conf);
|
||||
maxHeight = Math.max(maxHeight, height);
|
||||
bounds.insert(actor.x, verticalPos, actor.x + actor.width, actor.height);
|
||||
|
||||
prevWidth += actor.width;
|
||||
|
@ -443,7 +444,7 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
|||
}
|
||||
|
||||
// Add a margin between the actor boxes and the first arrow
|
||||
bounds.bumpVerticalPos(conf.height);
|
||||
bounds.bumpVerticalPos(maxHeight);
|
||||
};
|
||||
|
||||
export const setConf = function (cnf) {
|
||||
|
@ -688,6 +689,8 @@ export const draw = function (text, id) {
|
|||
// Draw actors below diagram
|
||||
bounds.bumpVerticalPos(conf.boxMargin * 2);
|
||||
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos());
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
fixLifeLineHeights(diagram, bounds.getVerticalPos());
|
||||
}
|
||||
|
||||
const { bounds: box } = bounds.getBounds();
|
||||
|
|
|
@ -95,6 +95,15 @@ const getStyles = (options) =>
|
|||
fill: ${options.activationBkgColor};
|
||||
stroke: ${options.activationBorderColor};
|
||||
}
|
||||
.actor-man line {
|
||||
stroke: ${options.actorBorder};
|
||||
fill: ${options.actorBkg};
|
||||
}
|
||||
.actor-man circle, line {
|
||||
stroke: ${options.actorBorder};
|
||||
fill: ${options.actorBkg};
|
||||
stroke-width: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default getStyles;
|
||||
|
|
|
@ -181,13 +181,22 @@ export const drawLabel = function (elem, txtObject) {
|
|||
};
|
||||
|
||||
let actorCnt = -1;
|
||||
|
||||
export const fixLifeLineHeights = (diagram, bounds) => {
|
||||
if (!diagram.selectAll) return;
|
||||
diagram
|
||||
.selectAll('.actor-line')
|
||||
.attr('class', '200')
|
||||
.attr('y2', bounds - 55);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws an actor in the diagram with the attached line
|
||||
* @param elem - The diagram we'll draw to.
|
||||
* @param actor - The actor to draw.
|
||||
* @param conf - drawText implementation discriminator object
|
||||
*/
|
||||
export const drawActor = function (elem, actor, conf) {
|
||||
const drawActorTypeParticipant = function (elem, actor, conf) {
|
||||
const center = actor.x + actor.width / 2;
|
||||
|
||||
const g = elem.append('g');
|
||||
|
@ -213,7 +222,7 @@ export const drawActor = function (elem, actor, conf) {
|
|||
rect.class = 'actor';
|
||||
rect.rx = 3;
|
||||
rect.ry = 3;
|
||||
drawRect(g, rect);
|
||||
const rectElem = drawRect(g, rect);
|
||||
|
||||
_drawTextCandidateFunc(conf)(
|
||||
actor.description,
|
||||
|
@ -225,6 +234,105 @@ export const drawActor = function (elem, actor, conf) {
|
|||
{ class: 'actor' },
|
||||
conf
|
||||
);
|
||||
|
||||
let height = actor.height;
|
||||
if (rectElem.node) {
|
||||
const bounds = rectElem.node().getBBox();
|
||||
actor.height = bounds.height;
|
||||
height = bounds.height;
|
||||
}
|
||||
return height;
|
||||
};
|
||||
|
||||
const drawActorTypeActor = function (elem, actor, conf) {
|
||||
const center = actor.x + actor.width / 2;
|
||||
|
||||
if (actor.y === 0) {
|
||||
actorCnt++;
|
||||
elem
|
||||
.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', 80)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999');
|
||||
}
|
||||
const actElem = elem.append('g');
|
||||
actElem.attr('class', 'actor-man');
|
||||
|
||||
const rect = getNoteRect();
|
||||
rect.x = actor.x;
|
||||
rect.y = actor.y;
|
||||
rect.fill = '#eaeaea';
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = 'actor';
|
||||
rect.rx = 3;
|
||||
rect.ry = 3;
|
||||
// drawRect(actElem, rect);
|
||||
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('id', 'actor-man-torso' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', actor.y + 25)
|
||||
.attr('x2', center)
|
||||
.attr('y2', actor.y + 45);
|
||||
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('id', 'actor-man-arms' + actorCnt)
|
||||
.attr('x1', center - 18)
|
||||
.attr('y1', actor.y + 33)
|
||||
.attr('x2', center + 18)
|
||||
.attr('y2', actor.y + 33);
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('x1', center - 18)
|
||||
.attr('y1', actor.y + 60)
|
||||
.attr('x2', center)
|
||||
.attr('y2', actor.y + 45);
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('x1', center)
|
||||
.attr('y1', actor.y + 45)
|
||||
.attr('x2', center + 16)
|
||||
.attr('y2', actor.y + 60);
|
||||
|
||||
const circle = actElem.append('circle');
|
||||
circle.attr('cx', actor.x + actor.width / 2);
|
||||
circle.attr('cy', actor.y + 10);
|
||||
circle.attr('r', 15);
|
||||
circle.attr('width', actor.width);
|
||||
circle.attr('height', actor.height);
|
||||
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height;
|
||||
|
||||
_drawTextCandidateFunc(conf)(
|
||||
actor.description,
|
||||
actElem,
|
||||
rect.x,
|
||||
rect.y + 35,
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: 'actor' },
|
||||
conf
|
||||
);
|
||||
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
export const drawActor = function (elem, actor, conf) {
|
||||
switch (actor.type) {
|
||||
case 'actor':
|
||||
return drawActorTypeActor(elem, actor, conf);
|
||||
case 'participant':
|
||||
return drawActorTypeParticipant(elem, actor, conf);
|
||||
}
|
||||
};
|
||||
|
||||
export const anchorElement = function (elem) {
|
||||
|
@ -576,4 +684,5 @@ export default {
|
|||
insertArrowCrossHead,
|
||||
getTextObj,
|
||||
getNoteRect,
|
||||
fixLifeLineHeights,
|
||||
};
|
||||
|
|