mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
merge upstream/develop into branch
This commit is contained in:
commit
6563c0292a
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -12,32 +12,35 @@ A clear and concise description of what the bug is.
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
1. Go to the [live editor](https://mermaid.live).
|
||||
2. Enter the below code sample.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
**Code Sample**
|
||||
Live version: A link to https://mermaid.live/edit#…
|
||||
|
||||
```
|
||||
graph TD;
|
||||
…
|
||||
```
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Code Sample**
|
||||
If applicable, add the code sample or a link to the [live editor](https://mermaid.live).
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
- Browser: [e.g. chrome, safari]
|
||||
- Version: [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
- Browser: [e.g. stock browser, safari]
|
||||
- Version: [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
@ -164,7 +164,6 @@ Class01 <|-- AveryLongClass : Cool
|
||||
Class09 --> C2 : Where am I?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
note "I love this diagram!\nDo you love it?"
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
@ -175,7 +174,6 @@ class Class10 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
note for Class10 "Cool class\nI said it's very cool class!"
|
||||
```
|
||||
|
||||
### State diagram [<a href="https://mermaid-js.github.io/mermaid/#/stateDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkEFvgzAMhf8K8nEqpYSNthx22Xbcqcexg0sCiZQQlDhIFeK_L8A6TfXp6fOz9ewJGssFVOAJSbwr7ByadGR1n8T6evpO0vQ1uZDSekOrXGFsPqJPO6q-2-imH8f_0TeHXm50lfelsAMjnEHFY6xpMdRAUhhRQxUlFy0GTTXU_RytYeAx-AdXZB1ULWovdoCB7OXWN1CRC-Ju-r3uz6UtchGHJqDbsPygU57iysb2reoWHpyOWBINvsqypb3vFMlw3TfWZF5xiY7keC6zkpUnZIUojwW-FAVvrvn51LLnvOXHQ84Q5nn-AVtLcwk">live editor</a>]
|
||||
|
@ -39,7 +39,7 @@ Mermaid 甚至能让非程序员也能通过 [Mermaid Live Editor](https://merma
|
||||
<table>
|
||||
<!-- <Flowchart> -->
|
||||
|
||||
### 流程图 [<a href="https://mermaid-js.github.io/mermaid/#/flowchart">文档</a> - <a href="https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0hhcmRdIC0tPnxUZXh0fCBCKFJvdW5kKVxuICAgIEIgLS0-IEN7RGVjaXNpb259XG4gICAgQyAtLT58T25lfCBEW1Jlc3VsdCAxXVxuICAgIEMgLS0-fFR3b3wgRVtSZXN1bHQgMl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ">live editor</a>]
|
||||
### 流程图 [<a href="https://mermaid-js.github.io/mermaid/#/flowchart">文档</a> - <a href="https://mermaid.live/edit#base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0hhcmRdIC0tPnxUZXh0fCBCKFJvdW5kKVxuICAgIEIgLS0-IEN7RGVjaXNpb259XG4gICAgQyAtLT58T25lfCBEW1Jlc3VsdCAxXVxuICAgIEMgLS0-fFR3b3wgRVtSZXN1bHQgMl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ">live editor</a>]
|
||||
|
||||
```
|
||||
flowchart LR
|
||||
@ -57,7 +57,7 @@ C -->|One| D[Result 1]
|
||||
C -->|Two| E[Result 2]
|
||||
```
|
||||
|
||||
### 时序图 [<a href="https://mermaid-js.github.io/mermaid/#/sequenceDiagram">文档</a> - <a href="https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG5BbGljZS0-PkpvaG46IEhlbGxvIEpvaG4sIGhvdyBhcmUgeW91P1xubG9vcCBIZWFsdGhjaGVja1xuICAgIEpvaG4tPj5Kb2huOiBGaWdodCBhZ2FpbnN0IGh5cG9jaG9uZHJpYVxuZW5kXG5Ob3RlIHJpZ2h0IG9mIEpvaG46IFJhdGlvbmFsIHRob3VnaHRzIVxuSm9obi0tPj5BbGljZTogR3JlYXQhXG5Kb2huLT4-Qm9iOiBIb3cgYWJvdXQgeW91P1xuQm9iLS0-PkpvaG46IEpvbGx5IGdvb2QhIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifX0">live editor</a>]
|
||||
### 时序图 [<a href="https://mermaid-js.github.io/mermaid/#/sequenceDiagram">文档</a> - <a href="https://mermaid.live/edit#base64:eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG5BbGljZS0-PkpvaG46IEhlbGxvIEpvaG4sIGhvdyBhcmUgeW91P1xubG9vcCBIZWFsdGhjaGVja1xuICAgIEpvaG4tPj5Kb2huOiBGaWdodCBhZ2FpbnN0IGh5cG9jaG9uZHJpYVxuZW5kXG5Ob3RlIHJpZ2h0IG9mIEpvaG46IFJhdGlvbmFsIHRob3VnaHRzIVxuSm9obi0tPj5BbGljZTogR3JlYXQhXG5Kb2huLT4-Qm9iOiBIb3cgYWJvdXQgeW91P1xuQm9iLS0-PkpvaG46IEpvbGx5IGdvb2QhIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifX0">live editor</a>]
|
||||
|
||||
```
|
||||
sequenceDiagram
|
||||
@ -83,7 +83,7 @@ John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
```
|
||||
|
||||
### 甘特图 [<a href="https://mermaid-js.github.io/mermaid/#/gantt">文档</a> - <a href="https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ2FudHRcbnNlY3Rpb24gU2VjdGlvblxuQ29tcGxldGVkIDpkb25lLCAgICBkZXMxLCAyMDE0LTAxLTA2LDIwMTQtMDEtMDhcbkFjdGl2ZSAgICAgICAgOmFjdGl2ZSwgIGRlczIsIDIwMTQtMDEtMDcsIDNkXG5QYXJhbGxlbCAxICAgOiAgICAgICAgIGRlczMsIGFmdGVyIGRlczEsIDFkXG5QYXJhbGxlbCAyICAgOiAgICAgICAgIGRlczQsIGFmdGVyIGRlczEsIDFkXG5QYXJhbGxlbCAzICAgOiAgICAgICAgIGRlczUsIGFmdGVyIGRlczMsIDFkXG5QYXJhbGxlbCA0ICAgOiAgICAgICAgIGRlczYsIGFmdGVyIGRlczQsIDFkIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifX0">live editor</a>]
|
||||
### 甘特图 [<a href="https://mermaid-js.github.io/mermaid/#/gantt">文档</a> - <a href="https://mermaid.live/edit#base64:eyJjb2RlIjoiZ2FudHRcbnNlY3Rpb24gU2VjdGlvblxuQ29tcGxldGVkIDpkb25lLCAgICBkZXMxLCAyMDE0LTAxLTA2LDIwMTQtMDEtMDhcbkFjdGl2ZSAgICAgICAgOmFjdGl2ZSwgIGRlczIsIDIwMTQtMDEtMDcsIDNkXG5QYXJhbGxlbCAxICAgOiAgICAgICAgIGRlczMsIGFmdGVyIGRlczEsIDFkXG5QYXJhbGxlbCAyICAgOiAgICAgICAgIGRlczQsIGFmdGVyIGRlczEsIDFkXG5QYXJhbGxlbCAzICAgOiAgICAgICAgIGRlczUsIGFmdGVyIGRlczMsIDFkXG5QYXJhbGxlbCA0ICAgOiAgICAgICAgIGRlczYsIGFmdGVyIGRlczQsIDFkIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifX0">live editor</a>]
|
||||
|
||||
```
|
||||
gantt
|
||||
@ -107,7 +107,7 @@ gantt
|
||||
Parallel 4 : des6, after des4, 1d
|
||||
```
|
||||
|
||||
### 类图 [<a href="https://mermaid-js.github.io/mermaid/#/classDiagram">文档</a> - <a href="https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiY2xhc3NEaWFncmFtXG5DbGFzczAxIDx8LS0gQXZlcnlMb25nQ2xhc3MgOiBDb29sXG48PGludGVyZmFjZT4-IENsYXNzMDFcbkNsYXNzMDkgLS0-IEMyIDogV2hlcmUgYW0gaT9cbkNsYXNzMDkgLS0qIEMzXG5DbGFzczA5IC0tfD4gQ2xhc3MwN1xuQ2xhc3MwNyA6IGVxdWFscygpXG5DbGFzczA3IDogT2JqZWN0W10gZWxlbWVudERhdGFcbkNsYXNzMDEgOiBzaXplKClcbkNsYXNzMDEgOiBpbnQgY2hpbXBcbkNsYXNzMDEgOiBpbnQgZ29yaWxsYVxuY2xhc3MgQ2xhc3MxMCB7XG4gID4-c2VydmljZT4-XG4gIGludCBpZFxuICBzaXplKClcbn0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ">live editor</a>]
|
||||
### 类图 [<a href="https://mermaid-js.github.io/mermaid/#/classDiagram">文档</a> - <a href="https://mermaid.live/edit#base64:eyJjb2RlIjoiY2xhc3NEaWFncmFtXG5DbGFzczAxIDx8LS0gQXZlcnlMb25nQ2xhc3MgOiBDb29sXG48PGludGVyZmFjZT4-IENsYXNzMDFcbkNsYXNzMDkgLS0-IEMyIDogV2hlcmUgYW0gaT9cbkNsYXNzMDkgLS0qIEMzXG5DbGFzczA5IC0tfD4gQ2xhc3MwN1xuQ2xhc3MwNyA6IGVxdWFscygpXG5DbGFzczA3IDogT2JqZWN0W10gZWxlbWVudERhdGFcbkNsYXNzMDEgOiBzaXplKClcbkNsYXNzMDEgOiBpbnQgY2hpbXBcbkNsYXNzMDEgOiBpbnQgZ29yaWxsYVxuY2xhc3MgQ2xhc3MxMCB7XG4gID4-c2VydmljZT4-XG4gIGludCBpZFxuICBzaXplKClcbn0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ">live editor</a>]
|
||||
|
||||
```
|
||||
classDiagram
|
||||
@ -147,7 +147,7 @@ class Class10 {
|
||||
}
|
||||
```
|
||||
|
||||
### 状态图 [[<a href="https://mermaid-js.github.io/mermaid/#/stateDiagram">docs</a> - <a href="https://mermaid.live/#/edit/eyJjb2RlIjoic3RhdGVEaWFncmFtLXYyXG4gICAgWypdIC0tPiBTdGlsbFxuICAgIFN0aWxsIC0tPiBbKl1cbiAgICBTdGlsbCAtLT4gTW92aW5nXG4gICAgTW92aW5nIC0tPiBTdGlsbFxuICAgIE1vdmluZyAtLT4gQ3Jhc2hcbiAgICBDcmFzaCAtLT4gWypdIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQiLCJ0aGVtZVZhcmlhYmxlcyI6eyJiYWNrZ3JvdW5kIjoid2hpdGUiLCJwcmltYXJ5Q29sb3IiOiIjRUNFQ0ZGIiwic2Vjb25kYXJ5Q29sb3IiOiIjZmZmZmRlIiwidGVydGlhcnlDb2xvciI6ImhzbCg4MCwgMTAwJSwgOTYuMjc0NTA5ODAzOSUpIiwicHJpbWFyeUJvcmRlckNvbG9yIjoiaHNsKDI0MCwgNjAlLCA4Ni4yNzQ1MDk4MDM5JSkiLCJzZWNvbmRhcnlCb3JkZXJDb2xvciI6ImhzbCg2MCwgNjAlLCA4My41Mjk0MTE3NjQ3JSkiLCJ0ZXJ0aWFyeUJvcmRlckNvbG9yIjoiaHNsKDgwLCA2MCUsIDg2LjI3NDUwOTgwMzklKSIsInByaW1hcnlUZXh0Q29sb3IiOiIjMTMxMzAwIiwic2Vjb25kYXJ5VGV4dENvbG9yIjoiIzAwMDAyMSIsInRlcnRpYXJ5VGV4dENvbG9yIjoicmdiKDkuNTAwMDAwMDAwMSwgOS41MDAwMDAwMDAxLCA5LjUwMDAwMDAwMDEpIiwibGluZUNvbG9yIjoiIzMzMzMzMyIsInRleHRDb2xvciI6IiMzMzMiLCJtYWluQmtnIjoiI0VDRUNGRiIsInNlY29uZEJrZyI6IiNmZmZmZGUiLCJib3JkZXIxIjoiIzkzNzBEQiIsImJvcmRlcjIiOiIjYWFhYTMzIiwiYXJyb3doZWFkQ29sb3IiOiIjMzMzMzMzIiwiZm9udEZhbWlseSI6IlwidHJlYnVjaGV0IG1zXCIsIHZlcmRhbmEsIGFyaWFsIiwiZm9udFNpemUiOiIxNnB4IiwibGFiZWxCYWNrZ3JvdW5kIjoiI2U4ZThlOCIsIm5vZGVCa2ciOiIjRUNFQ0ZGIiwibm9kZUJvcmRlciI6IiM5MzcwREIiLCJjbHVzdGVyQmtnIjoiI2ZmZmZkZSIsImNsdXN0ZXJCb3JkZXIiOiIjYWFhYTMzIiwiZGVmYXVsdExpbmtDb2xvciI6IiMzMzMzMzMiLCJ0aXRsZUNvbG9yIjoiIzMzMyIsImVkZ2VMYWJlbEJhY2tncm91bmQiOiIjZThlOGU4IiwiYWN0b3JCb3JkZXIiOiJoc2woMjU5LjYyNjE2ODIyNDMsIDU5Ljc3NjUzNjMxMjglLCA4Ny45MDE5NjA3ODQzJSkiLCJhY3RvckJrZyI6IiNFQ0VDRkYiLCJhY3RvclRleHRDb2xvciI6ImJsYWNrIiwiYWN0b3JMaW5lQ29sb3IiOiJncmV5Iiwic2lnbmFsQ29sb3IiOiIjMzMzIiwic2lnbmFsVGV4dENvbG9yIjoiIzMzMyIsImxhYmVsQm94QmtnQ29sb3IiOiIjRUNFQ0ZGIiwibGFiZWxCb3hCb3JkZXJDb2xvciI6ImhzbCgyNTkuNjI2MTY4MjI0MywgNTkuNzc2NTM2MzEyOCUsIDg3LjkwMTk2MDc4NDMlKSIsImxhYmVsVGV4dENvbG9yIjoiYmxhY2siLCJsb29wVGV4dENvbG9yIjoiYmxhY2siLCJub3RlQm9yZGVyQ29sb3IiOiIjYWFhYTMzIiwibm90ZUJrZ0NvbG9yIjoiI2ZmZjVhZCIsIm5vdGVUZXh0Q29sb3IiOiJibGFjayIsImFjdGl2YXRpb25Cb3JkZXJDb2xvciI6IiM2NjYiLCJhY3RpdmF0aW9uQmtnQ29sb3IiOiIjZjRmNGY0Iiwic2VxdWVuY2VOdW1iZXJDb2xvciI6IndoaXRlIiwic2VjdGlvbkJrZ0NvbG9yIjoicmdiYSgxMDIsIDEwMiwgMjU1LCAwLjQ5KSIsImFsdFNlY3Rpb25Ca2dDb2xvciI6IndoaXRlIiwic2VjdGlvbkJrZ0NvbG9yMiI6IiNmZmY0MDAiLCJ0YXNrQm9yZGVyQ29sb3IiOiIjNTM0ZmJjIiwidGFza0JrZ0NvbG9yIjoiIzhhOTBkZCIsInRhc2tUZXh0TGlnaHRDb2xvciI6IndoaXRlIiwidGFza1RleHRDb2xvciI6IndoaXRlIiwidGFza1RleHREYXJrQ29sb3IiOiJibGFjayIsInRhc2tUZXh0T3V0c2lkZUNvbG9yIjoiYmxhY2siLCJ0YXNrVGV4dENsaWNrYWJsZUNvbG9yIjoiIzAwMzE2MyIsImFjdGl2ZVRhc2tCb3JkZXJDb2xvciI6IiM1MzRmYmMiLCJhY3RpdmVUYXNrQmtnQ29sb3IiOiIjYmZjN2ZmIiwiZ3JpZENvbG9yIjoibGlnaHRncmV5IiwiZG9uZVRhc2tCa2dDb2xvciI6ImxpZ2h0Z3JleSIsImRvbmVUYXNrQm9yZGVyQ29sb3IiOiJncmV5IiwiY3JpdEJvcmRlckNvbG9yIjoiI2ZmODg4OCIsImNyaXRCa2dDb2xvciI6InJlZCIsInRvZGF5TGluZUNvbG9yIjoicmVkIiwibGFiZWxDb2xvciI6ImJsYWNrIiwiZXJyb3JCa2dDb2xvciI6IiM1NTIyMjIiLCJlcnJvclRleHRDb2xvciI6IiM1NTIyMjIiLCJjbGFzc1RleHQiOiIjMTMxMzAwIiwiZmlsbFR5cGUwIjoiI0VDRUNGRiIsImZpbGxUeXBlMSI6IiNmZmZmZGUiLCJmaWxsVHlwZTIiOiJoc2woMzA0LCAxMDAlLCA5Ni4yNzQ1MDk4MDM5JSkiLCJmaWxsVHlwZTMiOiJoc2woMTI0LCAxMDAlLCA5My41Mjk0MTE3NjQ3JSkiLCJmaWxsVHlwZTQiOiJoc2woMTc2LCAxMDAlLCA5Ni4yNzQ1MDk4MDM5JSkiLCJmaWxsVHlwZTUiOiJoc2woLTQsIDEwMCUsIDkzLjUyOTQxMTc2NDclKSIsImZpbGxUeXBlNiI6ImhzbCg4LCAxMDAlLCA5Ni4yNzQ1MDk4MDM5JSkiLCJmaWxsVHlwZTciOiJoc2woMTg4LCAxMDAlLCA5My41Mjk0MTE3NjQ3JSkifX0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9">live editor</a>]
|
||||
### 状态图 [[<a href="https://mermaid-js.github.io/mermaid/#/stateDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkLsOwjAMRX-l8ojahTEDCzB26kgYrMYtkfJAqVMJVf13QiIKqqfr44d8vUDvFYGAiZHponEMaJv5KF2V4na4V01zqjrWxhSUZYapuEetn7UbCy16P_5HzwGnR6FZfpdCDZaCRa3SWcunQQI_yJIEkaSiAaNhCdKtqRUj--7lehAcItUQn-pnBMSAZtroVWn2YYOU07b4z29Y37gJVYk">live editor</a>]
|
||||
|
||||
```
|
||||
stateDiagram-v2
|
||||
@ -169,7 +169,7 @@ Moving --> Crash
|
||||
Crash --> [*]
|
||||
```
|
||||
|
||||
### 饼图 [<a href="https://mermaid-js.github.io/mermaid/#/pie">文档</a> - <a href="https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoicGllXG5cIkRvZ3NcIiA6IDQyLjk2XG5cIkNhdHNcIiA6IDUwLjA1XG5cIlJhdHNcIiA6IDEwLjAxIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifX0">live editor</a>]
|
||||
### 饼图 [<a href="https://mermaid-js.github.io/mermaid/#/pie">文档</a> - <a href="https://mermaid.live/edit#base64:eyJjb2RlIjoicGllXG5cIkRvZ3NcIiA6IDQyLjk2XG5cIkNhdHNcIiA6IDUwLjA1XG5cIlJhdHNcIiA6IDEwLjAxIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifX0">live editor</a>]
|
||||
|
||||
```
|
||||
pie
|
||||
@ -185,9 +185,9 @@ pie
|
||||
"Rats" : 15
|
||||
```
|
||||
|
||||
### Git 图 [实验特性 - <a href="https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ2l0R3JhcGg6XG5vcHRpb25zXG57XG4gICAgXCJub2RlU3BhY2luZ1wiOiAxNTAsXG4gICAgXCJub2RlUmFkaXVzXCI6IDEwXG59XG5lbmRcbmNvbW1pdFxuYnJhbmNoIG5ld2JyYW5jaFxuY2hlY2tvdXQgbmV3YnJhbmNoXG5jb21taXRcbmNvbW1pdFxuY2hlY2tvdXQgbWFzdGVyXG5jb21taXRcbmNvbW1pdFxubWVyZ2UgbmV3YnJhbmNoXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ">live editor</a>]
|
||||
### Git 图 [实验特性 - <a href="https://mermaid.live/edit#base64:eyJjb2RlIjoiZ2l0R3JhcGg6XG5vcHRpb25zXG57XG4gICAgXCJub2RlU3BhY2luZ1wiOiAxNTAsXG4gICAgXCJub2RlUmFkaXVzXCI6IDEwXG59XG5lbmRcbmNvbW1pdFxuYnJhbmNoIG5ld2JyYW5jaFxuY2hlY2tvdXQgbmV3YnJhbmNoXG5jb21taXRcbmNvbW1pdFxuY2hlY2tvdXQgbWFzdGVyXG5jb21taXRcbmNvbW1pdFxubWVyZ2UgbmV3YnJhbmNoXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ">live editor</a>]
|
||||
|
||||
### 用户体验旅程图 [<a href="https://mermaid-js.github.io/mermaid/#/user-journey">文档</a> - <a href="https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoic3RhdGVEaWFncmFtXG4gICAgWypdIC0tPiBTdGlsbFxuICAgIFN0aWxsIC0tPiBbKl1cbiAgICBTdGlsbCAtLT4gTW92aW5nXG4gICAgTW92aW5nIC0tPiBTdGlsbFxuICAgIE1vdmluZyAtLT4gQ3Jhc2hcbiAgICBDcmFzaCAtLT4gWypdIiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifX0">live editor</a>]
|
||||
### 用户体验旅程图 [<a href="https://mermaid-js.github.io/mermaid/#/user-journey">文档</a> - <a href="https://mermaid.live/edit#pako:eNpljzEPgkAMhf9K05nFGJdbJXFiYmVpuKIncDVHL4QQ_ruHaILaqXnf63vpjLVYRoMAd4nB81R5SKNOO4ZiglFC6_wVLL3JwLU68XARUHnhTQcoqGVQJgMnAwV_5GSMj0HJhcHAcU_y7d7AYVUzOJP-ddyk3ydZGf0n66uldPqCPxWYYc-hJ2fTj_OqVqg3Tplo0mq5odhphZVfkpWiSjn5Go2GyBnGhyXl3NE1UI-moW7g5QkSoF5m">live editor</a>]
|
||||
|
||||
```
|
||||
journey
|
||||
|
96
cSpell.json
96
cSpell.json
@ -2,71 +2,71 @@
|
||||
"version": "0.2",
|
||||
"language": "en",
|
||||
"words": [
|
||||
"antiscript",
|
||||
"applitools",
|
||||
"colour",
|
||||
"customizability",
|
||||
"cytoscape",
|
||||
"dagre",
|
||||
"docsify",
|
||||
"dompurify",
|
||||
"flatmap",
|
||||
"gantt",
|
||||
"gitea",
|
||||
"gitgraph",
|
||||
"Gantt",
|
||||
"jison",
|
||||
"mermiad",
|
||||
"knsv",
|
||||
"Knut",
|
||||
"mindmap",
|
||||
"mindmaps",
|
||||
"Mindmaps",
|
||||
"mitigations",
|
||||
"phpbb",
|
||||
"sandboxed",
|
||||
"santize",
|
||||
"stylis",
|
||||
"Sveidqvist",
|
||||
"verdana",
|
||||
"visio",
|
||||
"vitepress",
|
||||
"xlink"
|
||||
"Visio",
|
||||
"mermiad"
|
||||
],
|
||||
"ignoreWords": [
|
||||
"acyclicer",
|
||||
"adamiecki",
|
||||
"asciidoctor",
|
||||
"ashish",
|
||||
"astah",
|
||||
"bbox",
|
||||
"bilkent",
|
||||
"bisheng",
|
||||
"Adamiecki",
|
||||
"applitools",
|
||||
"Asciidoctor",
|
||||
"Astah",
|
||||
"Bisheng",
|
||||
"codedoc",
|
||||
"cpettitt",
|
||||
"descr",
|
||||
"docsy",
|
||||
"doku",
|
||||
"edgechromium",
|
||||
"graphlib",
|
||||
"grav",
|
||||
"inkdrop",
|
||||
"jaoude",
|
||||
"kaufmann",
|
||||
"knsv",
|
||||
"knut",
|
||||
"lucida",
|
||||
"Docsy",
|
||||
"Doku",
|
||||
"Gitea",
|
||||
"Gitgraph",
|
||||
"Grav",
|
||||
"Inkdrop",
|
||||
"Jaoude",
|
||||
"mdbook",
|
||||
"mermerd",
|
||||
"mkdocs",
|
||||
"plantuml",
|
||||
"playfair's",
|
||||
"podlite",
|
||||
"ranksep",
|
||||
"phpbb",
|
||||
"Plantuml",
|
||||
"Playfair's",
|
||||
"Podlite",
|
||||
"redmine",
|
||||
"setupgraphviewbox",
|
||||
"sphinxcontrib",
|
||||
"Tuleap",
|
||||
"dagre",
|
||||
"vitepress",
|
||||
"docsify",
|
||||
"colour",
|
||||
"graphlib",
|
||||
"acyclicer",
|
||||
"ranksep",
|
||||
"descr",
|
||||
"substate",
|
||||
"sveidqvist",
|
||||
"Ashish",
|
||||
"bbox",
|
||||
"techn",
|
||||
"cytoscape",
|
||||
"Lucida",
|
||||
"Bilkent",
|
||||
"cpettitt",
|
||||
"antiscript",
|
||||
"ts-nocheck",
|
||||
"tuleap",
|
||||
"viewports"
|
||||
"setupGraphViewbox",
|
||||
"flatmap",
|
||||
"Kaufmann",
|
||||
"viewports",
|
||||
"edgechromium",
|
||||
"statediagram",
|
||||
"stylis",
|
||||
"svgXlink",
|
||||
"xlink"
|
||||
],
|
||||
"patterns": [
|
||||
{
|
||||
|
@ -341,4 +341,130 @@ describe('Gantt diagram', () => {
|
||||
expect(descriptionEl.textContent).to.equal(expectedAccDescription);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 15 minutes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %H:%M
|
||||
tickInterval 15minute
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 2022-10-03, 6h
|
||||
Another task : after a1, 6h
|
||||
section Another
|
||||
Task in sec : 2022-10-03, 3h
|
||||
another task : 3h
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 6 hours', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %d %H:%M
|
||||
tickInterval 6hour
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 2022-10-03, 1d
|
||||
Another task : after a1, 2d
|
||||
section Another
|
||||
Task in sec : 2022-10-04, 2d
|
||||
another task : 2d
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 1 day', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
tickInterval 1day
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 2022-10-01, 30d
|
||||
Another task : after a1, 20d
|
||||
section Another
|
||||
Task in sec : 2022-10-20, 12d
|
||||
another task : 24d
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 1 week', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
tickInterval 1week
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 2022-10-01, 30d
|
||||
Another task : after a1, 20d
|
||||
section Another
|
||||
Task in sec : 2022-10-20, 12d
|
||||
another task : 24d
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 1 month', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
tickInterval 1month
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 2022-10-01, 30d
|
||||
Another task : after a1, 20d
|
||||
section Another
|
||||
Task in sec : 2022-10-20, 12d
|
||||
another task : 24d
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 1 day and topAxis is true', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
tickInterval 1day
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 2022-10-01, 30d
|
||||
Another task : after a1, 20d
|
||||
section Another
|
||||
Task in sec : 2022-10-20, 12d
|
||||
another task : 24d
|
||||
`,
|
||||
{ gantt: { topAxis: true } }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -521,4 +521,42 @@ stateDiagram-v2
|
||||
{ logLevel: 0, fontFamily: 'courier' }
|
||||
);
|
||||
});
|
||||
|
||||
describe('classDefs and applying classes', () => {
|
||||
it('v2 states can have a class applied', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
[*] --> A
|
||||
A --> B: test({ foo#colon; 'far' })
|
||||
B --> [*]
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold
|
||||
class B badBadEvent
|
||||
`,
|
||||
{ logLevel: 0, fontFamily: 'courier' }
|
||||
);
|
||||
});
|
||||
it('v2 can have multiple classes applied to multiple states', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic;
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold
|
||||
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]
|
||||
|
||||
class Still notMoving
|
||||
class Moving, Crash movement
|
||||
class Crash badBadEvent
|
||||
`,
|
||||
{ logLevel: 0, fontFamily: 'courier' }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
106
demos/state.html
106
demos/state.html
@ -15,6 +15,7 @@
|
||||
|
||||
<body>
|
||||
<h1>State diagram demos</h1>
|
||||
<h2>Very simple showing change from State1 to State2</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram
|
||||
accTitle: This is the accessible title
|
||||
@ -24,59 +25,126 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>This has classDef statements to apply style classes to specific states</h2>
|
||||
<h4>Here are the <code>classDef</code> statements:</h4>
|
||||
<p>
|
||||
<code>
|
||||
classDef notMoving fill:white<br />
|
||||
classDef movement font-style:italic;<br />
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold<br />
|
||||
</code>
|
||||
</p>
|
||||
<h4>And these are how they are applied:</h4>
|
||||
<p>
|
||||
<code>
|
||||
class Still notMoving<br />
|
||||
class Moving, Crash movement<br />
|
||||
class Crash badBadEvent<br />
|
||||
</code>
|
||||
</p>
|
||||
<pre class="mermaid">
|
||||
stateDiagram-v2
|
||||
accTitle: This is the accessible title
|
||||
accDescr: This is an accessible description
|
||||
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic;
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold
|
||||
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]
|
||||
|
||||
class Still notMoving
|
||||
class Moving, Crash movement
|
||||
class Crash badBadEvent
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
|
||||
<pre class="mermaid">
|
||||
stateDiagram
|
||||
stateDiagram-v2
|
||||
accTitle: very very simple state
|
||||
accDescr: This is a state diagram showing one state
|
||||
State1
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>You can label the relationships</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram
|
||||
stateDiagram-v2
|
||||
[*] --> State1
|
||||
State1 --> State2 : Transition 1
|
||||
State1 --> State3 : Transition 2
|
||||
State1 --> State4 : Transition 3
|
||||
State1 --> [*]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>This shows Composite states</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram-v2
|
||||
[*] --> First
|
||||
First --> Second
|
||||
First --> Third
|
||||
|
||||
state First {
|
||||
[*] --> second
|
||||
second --> [*]
|
||||
[*] --> 1st
|
||||
1st --> [*]
|
||||
}
|
||||
state Second {
|
||||
[*] --> 2nd
|
||||
2nd --> [*]
|
||||
}
|
||||
state Third {
|
||||
[*] --> 3rd
|
||||
3rd --> [*]
|
||||
}
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Compsite states can link to themselves</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram
|
||||
State1: The state with a note
|
||||
note right of State1
|
||||
Important information! You can write
|
||||
notes.
|
||||
end note
|
||||
State1 --> State2
|
||||
note left of State2 : This is the note to the left.
|
||||
stateDiagram-v2
|
||||
state Active {
|
||||
Idle
|
||||
}
|
||||
Inactive --> Idle: ACT
|
||||
Active --> Active: LOG
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>transition labels can span multiple lines using "br" tags or \n</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram
|
||||
State1
|
||||
note right of State1
|
||||
Line1<br>Line2<br />Line3<br />Line4<br />Line5
|
||||
end note
|
||||
stateDiagram-v2
|
||||
[*] --> S1
|
||||
S1 --> S2: This long line uses a br tag<br/>to create multiple<br/>lines.
|
||||
S1 --> S3: This transition descripton uses \na newline character\nto create multiple\nlines.
|
||||
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>You can add Notes</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram-v2
|
||||
direction LR
|
||||
State1: A state with a note
|
||||
note right of State1
|
||||
Important information!<br />You can write notes.<br/>And\nthey\ncan\nbe\nmulti-\nline.
|
||||
end note
|
||||
State1 --> State2
|
||||
note left of State2 : Notes can be to the left of a state\n(like this one).
|
||||
note right of State2 : Notes can be to the right of a state\n(like this one).
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
theme: 'base',
|
||||
theme: 'default',
|
||||
// themeCSS: '.node rect { fill: red; }',
|
||||
logLevel: 3,
|
||||
securityLevel: 'loose',
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/8.6.0_docs.md](../packages/mermaid/src/docs/8.6.0_docs.md).
|
||||
|
||||
# Version 8.6.0 Changes
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/CHANGELOG.md](../packages/mermaid/src/docs/CHANGELOG.md).
|
||||
|
||||
# Change Log
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/Configuration.md](../packages/mermaid/src/docs/Configuration.md).
|
||||
|
||||
# Configuration
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/README.md](../packages/mermaid/src/docs/README.md).
|
||||
|
||||
# About Mermaid
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/SUMMARY.md](../packages/mermaid/src/docs/SUMMARY.md).
|
||||
|
||||
# Summary
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/Setup.md](../packages/mermaid/src/docs/Setup.md).
|
||||
|
||||
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/Tutorials.md](../packages/mermaid/src/docs/Tutorials.md).
|
||||
|
||||
# Tutorials
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/\_navbar.md](../packages/mermaid/src/docs/_navbar.md).
|
||||
|
||||
- Getting started
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/\_sidebar.md](../packages/mermaid/src/docs/_sidebar.md).
|
||||
|
||||
- 📔 Introduction
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/accessibility.md](../packages/mermaid/src/docs/accessibility.md).
|
||||
|
||||
# Accessibility Options
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/breakingChanges.md](../packages/mermaid/src/docs/breakingChanges.md).
|
||||
|
||||
# Breaking changes
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/c4c.md](../packages/mermaid/src/docs/c4c.md).
|
||||
|
||||
# C4 Diagrams
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/classDiagram.md](../packages/mermaid/src/docs/classDiagram.md).
|
||||
|
||||
# Class diagrams
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/developer-docs/configuration.md](../../packages/mermaid/src/docs/developer-docs/configuration.md).
|
||||
|
||||
# Configuration
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/development.md](../packages/mermaid/src/docs/development.md).
|
||||
|
||||
# Development and Contribution 🙌
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/diagrams-and-syntax-and-examples/flowchart.md](../../packages/mermaid/src/docs/diagrams-and-syntax-and-examples/flowchart.md).
|
||||
|
||||
---
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/directives.md](../packages/mermaid/src/docs/directives.md).
|
||||
|
||||
# Directives
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/entityRelationshipDiagram.md](../packages/mermaid/src/docs/entityRelationshipDiagram.md).
|
||||
|
||||
# Entity Relationship Diagrams
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/examples.md](../packages/mermaid/src/docs/examples.md).
|
||||
|
||||
# Examples
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/faq.md](../packages/mermaid/src/docs/faq.md).
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/flowchart.md](../packages/mermaid/src/docs/flowchart.md).
|
||||
|
||||
# Flowcharts - Basic Syntax
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/gantt.md](../packages/mermaid/src/docs/gantt.md).
|
||||
|
||||
# Gantt diagrams
|
||||
|
||||
@ -230,6 +230,18 @@ The following formatting strings are supported:
|
||||
|
||||
More info in: https://github.com/mbostock/d3/wiki/Time-Formatting
|
||||
|
||||
### Axis ticks
|
||||
|
||||
The default output ticks are auto. You can custom your `tickInterval`, like `1day` or `1week`.
|
||||
|
||||
tickInterval 1day
|
||||
|
||||
The pattern is:
|
||||
|
||||
/^([1-9][0-9]*)(minute|hour|day|week|month)$/
|
||||
|
||||
More info in: <https://github.com/d3/d3-time#interval_every>
|
||||
|
||||
## Comments
|
||||
|
||||
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/gitgraph.md](../packages/mermaid/src/docs/gitgraph.md).
|
||||
|
||||
# Gitgraph Diagrams
|
||||
|
||||
@ -165,7 +165,7 @@ In this example, we have given custom tags to the commits. Also, see how we have
|
||||
|
||||
### Create a new branch
|
||||
|
||||
In Mermaid, in-order to create a new branch, you make use of the `branch` keyword. You also need to provide a name of the new branch. The name has to be unique and cannot be that of an existing branch. Usage example: `branch develop`
|
||||
In Mermaid, in-order to create a new branch, you make use of the `branch` keyword. You also need to provide a name of the new branch. The name has to be unique and cannot be that of an existing branch. A branch name that could be confused for a keyword must be quoted within `""`. Usage examples: `branch develop`, `branch "cherry-pick"`
|
||||
|
||||
When Mermaid, reads the `branch` keyword, it creates a new branch and sets it as the current branch. Equivalent to you creating a new branch and checking it out in Git world.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.--><head>
|
||||
<!--# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/index.html](../packages/mermaid/src/docs/index.html).--><head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>
|
||||
mermaid - Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams,
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/integrations.md](../packages/mermaid/src/docs/integrations.md).
|
||||
|
||||
# Integrations
|
||||
|
||||
|
@ -1 +1 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/introduction.md](../packages/mermaid/src/docs/introduction.md).
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.--><head>
|
||||
<!--# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/landing/index.html](../../packages/mermaid/src/docs/landing/index.html).--><head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/mermaidCLI.md](../packages/mermaid/src/docs/mermaidCLI.md).
|
||||
|
||||
# mermaid CLI
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/mindmap.md](../packages/mermaid/src/docs/mindmap.md).
|
||||
|
||||
# Mindmap
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/n00b-advanced.md](../packages/mermaid/src/docs/n00b-advanced.md).
|
||||
|
||||
# Advanced n00b mermaid (Coming soon..)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/n00b-gettingStarted.md](../packages/mermaid/src/docs/n00b-gettingStarted.md).
|
||||
|
||||
# A Mermaid User-Guide for Beginners
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/n00b-overview.md](../packages/mermaid/src/docs/n00b-overview.md).
|
||||
|
||||
# Overview for Beginners
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/n00b-syntaxReference.md](../packages/mermaid/src/docs/n00b-syntaxReference.md).
|
||||
|
||||
# Diagram Syntax
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/newDiagram.md](../packages/mermaid/src/docs/newDiagram.md).
|
||||
|
||||
# Adding a New Diagram/Chart 📊
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/pie.md](../packages/mermaid/src/docs/pie.md).
|
||||
|
||||
# Pie chart diagrams
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/requirementDiagram.md](../packages/mermaid/src/docs/requirementDiagram.md).
|
||||
|
||||
# Requirement Diagram
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/security.md](../packages/mermaid/src/docs/security.md).
|
||||
|
||||
# Security
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/sequenceDiagram.md](../packages/mermaid/src/docs/sequenceDiagram.md).
|
||||
|
||||
# Sequence diagrams
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/stateDiagram.md](../packages/mermaid/src/docs/stateDiagram.md).
|
||||
|
||||
# State diagrams
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/theming.md](../packages/mermaid/src/docs/theming.md).
|
||||
|
||||
# Theme Configuration
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/upgrading.md](../packages/mermaid/src/docs/upgrading.md).
|
||||
|
||||
# Upgrading
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/usage.md](../packages/mermaid/src/docs/usage.md).
|
||||
|
||||
# Usage
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in packages/mermaid/src/docs.
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [packages/mermaid/src/docs/user-journey.md](../packages/mermaid/src/docs/user-journey.md).
|
||||
|
||||
# User Journey Diagram
|
||||
|
||||
|
38
package.json
38
package.json
@ -7,7 +7,7 @@
|
||||
"module": "dist/mermaid.core.mjs",
|
||||
"types": "dist/mermaid.d.ts",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@7.13.6",
|
||||
"packageManager": "pnpm@7.14.1",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./dist/mermaid.min.js",
|
||||
@ -71,7 +71,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.1",
|
||||
"@types/node": "18.11.0",
|
||||
"@types/node": "18.11.8",
|
||||
"@types/uuid": "8.3.4",
|
||||
"d3": "7.6.1",
|
||||
"dagre": "0.8.5",
|
||||
@ -84,39 +84,39 @@
|
||||
"moment-mini": "2.29.4",
|
||||
"non-layered-tidy-tree-layout": "2.0.2",
|
||||
"rollup": "2.79.1",
|
||||
"stylis": "4.1.2",
|
||||
"stylis": "4.1.3",
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-cypress": "3.27.5",
|
||||
"@applitools/eyes-cypress": "3.27.6",
|
||||
"@commitlint/cli": "17.1.2",
|
||||
"@commitlint/config-conventional": "17.1.0",
|
||||
"@cspell/eslint-plugin": "6.12.0",
|
||||
"@cspell/eslint-plugin": "6.13.2",
|
||||
"@types/d3": "7.4.0",
|
||||
"@types/dompurify": "2.3.4",
|
||||
"@types/eslint": "8.4.7",
|
||||
"@types/eslint": "8.4.9",
|
||||
"@types/express": "4.17.14",
|
||||
"@types/jsdom": "20.0.0",
|
||||
"@types/lodash": "4.14.186",
|
||||
"@types/mdast": "3.0.10",
|
||||
"@types/prettier": "2.7.1",
|
||||
"@types/stylis": "4.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.40.1",
|
||||
"@typescript-eslint/parser": "5.40.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.41.0",
|
||||
"@typescript-eslint/parser": "5.41.0",
|
||||
"@vitest/coverage-c8": "0.24.3",
|
||||
"@vitest/ui": "0.24.3",
|
||||
"concurrently": "7.4.0",
|
||||
"concurrently": "7.5.0",
|
||||
"coveralls": "3.1.1",
|
||||
"cypress": "10.10.0",
|
||||
"cypress": "10.11.0",
|
||||
"cypress-image-snapshot": "4.0.1",
|
||||
"documentation": "13.2.5",
|
||||
"esbuild": "0.15.12",
|
||||
"eslint": "8.25.0",
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-cypress": "2.12.1",
|
||||
"eslint-plugin-html": "7.1.0",
|
||||
"eslint-plugin-jest": "27.1.3",
|
||||
"eslint-plugin-jsdoc": "39.3.23",
|
||||
"eslint-plugin-jsdoc": "39.4.0",
|
||||
"eslint-plugin-json": "3.1.0",
|
||||
"eslint-plugin-markdown": "3.0.0",
|
||||
"eslint-plugin-no-only-tests": "3.1.0",
|
||||
@ -125,13 +125,13 @@
|
||||
"globby": "13.1.2",
|
||||
"husky": "8.0.1",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"jest": "29.2.1",
|
||||
"jest": "29.2.2",
|
||||
"jison": "0.4.18",
|
||||
"jsdom": "20.0.1",
|
||||
"jsdom": "20.0.2",
|
||||
"lint-staged": "13.0.3",
|
||||
"markdown-it": "13.0.1",
|
||||
"path-browserify": "1.0.1",
|
||||
"pnpm": "7.13.6",
|
||||
"pnpm": "7.14.1",
|
||||
"prettier": "2.7.1",
|
||||
"prettier-plugin-jsdoc": "0.4.2",
|
||||
"remark": "14.0.2",
|
||||
@ -140,10 +140,10 @@
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.8.4",
|
||||
"unist-util-flatmap": "1.0.0",
|
||||
"vite": "3.1.8",
|
||||
"vitepress": "1.0.0-alpha.22",
|
||||
"vite": "3.2.2",
|
||||
"vitepress": "1.0.0-alpha.26",
|
||||
"vitepress-plugin-mermaid": "2.0.8",
|
||||
"vitepress-plugin-search": "1.0.4-alpha.11",
|
||||
"vitepress-plugin-search": "1.0.4-alpha.14",
|
||||
"vitest": "0.24.3"
|
||||
},
|
||||
"resolutions": {
|
||||
@ -157,6 +157,6 @@
|
||||
"**/*.scss"
|
||||
],
|
||||
"volta": {
|
||||
"node": "18.5.0"
|
||||
"node": "18.12.0"
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"concurrently": "7.4.0",
|
||||
"concurrently": "7.5.0",
|
||||
"rimraf": "3.0.2"
|
||||
},
|
||||
"resolutions": {
|
||||
|
@ -57,7 +57,7 @@
|
||||
"non-layered-tidy-tree-layout": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "7.4.0",
|
||||
"concurrently": "7.5.0",
|
||||
"rimraf": "3.0.2"
|
||||
},
|
||||
"resolutions": {
|
||||
|
@ -75,31 +75,31 @@
|
||||
"stylis": "^4.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-cypress": "3.27.5",
|
||||
"@applitools/eyes-cypress": "3.27.6",
|
||||
"@commitlint/cli": "17.1.2",
|
||||
"@commitlint/config-conventional": "17.1.0",
|
||||
"@types/d3": "7.4.0",
|
||||
"@types/dompurify": "2.3.4",
|
||||
"@types/eslint": "8.4.7",
|
||||
"@types/eslint": "8.4.9",
|
||||
"@types/express": "4.17.14",
|
||||
"@types/jsdom": "20.0.0",
|
||||
"@types/lodash": "4.14.186",
|
||||
"@types/prettier": "2.7.1",
|
||||
"@types/stylis": "4.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.40.1",
|
||||
"@typescript-eslint/parser": "5.40.1",
|
||||
"concurrently": "7.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.41.0",
|
||||
"@typescript-eslint/parser": "5.41.0",
|
||||
"concurrently": "7.5.0",
|
||||
"coveralls": "3.1.1",
|
||||
"cypress": "10.10.0",
|
||||
"cypress": "10.11.0",
|
||||
"cypress-image-snapshot": "4.0.1",
|
||||
"documentation": "13.2.5",
|
||||
"esbuild": "0.15.12",
|
||||
"eslint": "8.25.0",
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-cypress": "2.12.1",
|
||||
"eslint-plugin-html": "7.1.0",
|
||||
"eslint-plugin-jest": "27.1.3",
|
||||
"eslint-plugin-jsdoc": "39.3.23",
|
||||
"eslint-plugin-jsdoc": "39.4.0",
|
||||
"eslint-plugin-json": "3.1.0",
|
||||
"eslint-plugin-markdown": "3.0.0",
|
||||
"express": "4.18.2",
|
||||
@ -108,7 +108,7 @@
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"jison": "0.4.18",
|
||||
"js-base64": "3.7.2",
|
||||
"jsdom": "20.0.1",
|
||||
"jsdom": "20.0.2",
|
||||
"lint-staged": "13.0.3",
|
||||
"moment": "2.29.4",
|
||||
"path-browserify": "1.0.1",
|
||||
|
@ -297,6 +297,7 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
|
||||
sectionFontSize?: string | number;
|
||||
numberSectionStyles?: number;
|
||||
axisFormat?: string;
|
||||
tickInterval?: string;
|
||||
topAxis?: boolean;
|
||||
}
|
||||
|
||||
|
@ -661,6 +661,19 @@ const config: Partial<MermaidConfig> = {
|
||||
*/
|
||||
axisFormat: '%Y-%m-%d',
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | ------------ | ------------| ------ | -------- | ------- |
|
||||
* | tickInterval | axis ticks | string | Optional | string |
|
||||
*
|
||||
* **Notes:**
|
||||
*
|
||||
* Pattern is /^([1-9][0-9]*)(minute|hour|day|week|month)$/
|
||||
*
|
||||
* Default value: undefined
|
||||
*/
|
||||
tickInterval: undefined,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | ----------- | ----------- | ------- | -------- | ----------- |
|
||||
|
@ -109,13 +109,13 @@ g.classGroup line {
|
||||
}
|
||||
|
||||
#extensionStart, .extension {
|
||||
fill: ${options.lineColor} !important;
|
||||
fill: ${options.mainBkg} !important;
|
||||
stroke: ${options.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#extensionEnd, .extension {
|
||||
fill: ${options.lineColor} !important;
|
||||
fill: ${options.mainBkg} !important;
|
||||
stroke: ${options.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
@ -77,31 +77,27 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
// Add a text node for the attribute type
|
||||
const typeNode = groupNode
|
||||
.append('text')
|
||||
.attr('class', 'er entityLabel')
|
||||
.classed('er entityLabel', true)
|
||||
.attr('id', `${attrPrefix}-type`)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'left')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px'
|
||||
)
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('text-anchor', 'left')
|
||||
.style('font-family', getConfig().fontFamily)
|
||||
.style('font-size', attrFontSize + 'px')
|
||||
.text(attributeType);
|
||||
|
||||
// Add a text node for the attribute name
|
||||
const nameNode = groupNode
|
||||
.append('text')
|
||||
.attr('class', 'er entityLabel')
|
||||
.classed('er entityLabel', true)
|
||||
.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'
|
||||
)
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('text-anchor', 'left')
|
||||
.style('font-family', getConfig().fontFamily)
|
||||
.style('font-size', attrFontSize + 'px')
|
||||
.text(item.attributeName);
|
||||
|
||||
const attributeNode = {};
|
||||
@ -118,16 +114,14 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
if (hasKeyType) {
|
||||
const keyTypeNode = groupNode
|
||||
.append('text')
|
||||
.attr('class', 'er entityLabel')
|
||||
.classed('er entityLabel', true)
|
||||
.attr('id', `${attrPrefix}-key`)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'left')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px'
|
||||
)
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('text-anchor', 'left')
|
||||
.style('font-family', getConfig().fontFamily)
|
||||
.style('font-size', attrFontSize + 'px')
|
||||
.text(item.attributeKeyType || '');
|
||||
|
||||
attributeNode.kn = keyTypeNode;
|
||||
@ -139,16 +133,14 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
if (hasComment) {
|
||||
const commentNode = groupNode
|
||||
.append('text')
|
||||
.attr('class', 'er entityLabel')
|
||||
.classed('er entityLabel', true)
|
||||
.attr('id', `${attrPrefix}-comment`)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'left')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px'
|
||||
)
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('text-anchor', 'left')
|
||||
.style('font-family', getConfig().fontFamily)
|
||||
.style('font-size', attrFontSize + 'px')
|
||||
.text(item.attributeComment || '');
|
||||
|
||||
attributeNode.cn = commentNode;
|
||||
@ -217,10 +209,10 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
// Insert a rectangle for the type
|
||||
const typeRect = groupNode
|
||||
.insert('rect', '#' + attributeNode.tn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.classed(`er ${attribStyle}`, true)
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', 0)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxTypeWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@ -237,10 +229,10 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
// Insert a rectangle for the name
|
||||
const nameRect = groupNode
|
||||
.insert('rect', '#' + attributeNode.nn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.classed(`er ${attribStyle}`, true)
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', nameXOffset)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxNameWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@ -259,10 +251,10 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
// Insert a rectangle for the key type
|
||||
const keyTypeRect = groupNode
|
||||
.insert('rect', '#' + attributeNode.kn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.classed(`er ${attribStyle}`, true)
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', keyTypeAndCommentXOffset)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxKeyWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@ -282,10 +274,10 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
// Insert a rectangle for the comment
|
||||
groupNode
|
||||
.insert('rect', '#' + attributeNode.cn.node().id)
|
||||
.attr('class', `er ${attribStyle}`)
|
||||
.attr('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.classed(`er ${attribStyle}`, 'true')
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', keyTypeAndCommentXOffset)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxCommentWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@ -335,16 +327,14 @@ const drawEntities = function (svgNode, entities, graph) {
|
||||
const textId = 'text-' + entityId;
|
||||
const textNode = groupNode
|
||||
.append('text')
|
||||
.attr('class', 'er entityLabel')
|
||||
.classed('er entityLabel', true)
|
||||
.attr('id', textId)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px'
|
||||
)
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('text-anchor', 'middle')
|
||||
.style('font-family', getConfig().fontFamily)
|
||||
.style('font-size', conf.fontSize + 'px')
|
||||
.text(entityName);
|
||||
|
||||
const { width: entityWidth, height: entityHeight } = drawAttributes(
|
||||
@ -356,10 +346,10 @@ const drawEntities = function (svgNode, entities, graph) {
|
||||
// Draw the rectangle - insert it before the text so that the text is not obscured
|
||||
const rectNode = groupNode
|
||||
.insert('rect', '#' + textId)
|
||||
.attr('class', 'er entityBox')
|
||||
.classed('er entityBox', true)
|
||||
.style('fill', conf.fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.stroke)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('width', entityWidth)
|
||||
@ -460,10 +450,10 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
|
||||
// Insert the line at the right place
|
||||
const svgPath = svg
|
||||
.insert('path', '#' + insert)
|
||||
.attr('class', 'er relationshipLine')
|
||||
.classed('er relationshipLine', true)
|
||||
.attr('d', lineFunction(edge.points))
|
||||
.attr('stroke', conf.stroke)
|
||||
.attr('fill', 'none');
|
||||
.style('stroke', conf.stroke)
|
||||
.style('fill', 'none');
|
||||
|
||||
// ...and with dashes if necessary
|
||||
if (rel.relSpec.relType === diagObj.db.Identification.NON_IDENTIFYING) {
|
||||
@ -537,16 +527,14 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
|
||||
|
||||
const labelNode = svg
|
||||
.append('text')
|
||||
.attr('class', 'er relationshipLabel')
|
||||
.classed('er relationshipLabel', true)
|
||||
.attr('id', labelId)
|
||||
.attr('x', labelPoint.x)
|
||||
.attr('y', labelPoint.y)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px'
|
||||
)
|
||||
.style('text-anchor', 'middle')
|
||||
.style('dominant-baseline', 'middle')
|
||||
.style('font-family', getConfig().fontFamily)
|
||||
.style('font-size', conf.fontSize + 'px')
|
||||
.text(rel.roleA);
|
||||
|
||||
// Figure out how big the opaque 'container' rectangle needs to be
|
||||
@ -555,13 +543,13 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
|
||||
// Insert the opaque rectangle before the text label
|
||||
svg
|
||||
.insert('rect', '#' + labelId)
|
||||
.attr('class', 'er relationshipLabelBox')
|
||||
.classed('er relationshipLabelBox', true)
|
||||
.attr('x', labelPoint.x - labelBBox.width / 2)
|
||||
.attr('y', labelPoint.y - labelBBox.height / 2)
|
||||
.attr('width', labelBBox.width)
|
||||
.attr('height', labelBBox.height)
|
||||
.attr('fill', 'white')
|
||||
.attr('fill-opacity', '85%');
|
||||
.style('fill', 'white')
|
||||
.style('fill-opacity', '85%');
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
|
||||
let dateFormat = '';
|
||||
let axisFormat = '';
|
||||
let tickInterval = undefined;
|
||||
let todayMarker = '';
|
||||
let includes = [];
|
||||
let excludes = [];
|
||||
@ -47,6 +48,7 @@ export const clear = function () {
|
||||
rawTasks = [];
|
||||
dateFormat = '';
|
||||
axisFormat = '';
|
||||
tickInterval = undefined;
|
||||
todayMarker = '';
|
||||
includes = [];
|
||||
excludes = [];
|
||||
@ -65,6 +67,14 @@ export const getAxisFormat = function () {
|
||||
return axisFormat;
|
||||
};
|
||||
|
||||
export const setTickInterval = function (txt) {
|
||||
tickInterval = txt;
|
||||
};
|
||||
|
||||
export const getTickInterval = function () {
|
||||
return tickInterval;
|
||||
};
|
||||
|
||||
export const setTodayMarker = function (txt) {
|
||||
todayMarker = txt;
|
||||
};
|
||||
@ -647,6 +657,8 @@ export default {
|
||||
topAxisEnabled,
|
||||
setAxisFormat,
|
||||
getAxisFormat,
|
||||
setTickInterval,
|
||||
getTickInterval,
|
||||
setTodayMarker,
|
||||
getTodayMarker,
|
||||
setAccTitle,
|
||||
|
@ -10,6 +10,11 @@ import {
|
||||
axisBottom,
|
||||
axisTop,
|
||||
timeFormat,
|
||||
timeMinute,
|
||||
timeHour,
|
||||
timeDay,
|
||||
timeWeek,
|
||||
timeMonth,
|
||||
} from 'd3';
|
||||
import common from '../common/common';
|
||||
import { getConfig } from '../../config';
|
||||
@ -495,6 +500,33 @@ export const draw = function (text, id, version, diagObj) {
|
||||
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
|
||||
.tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
|
||||
|
||||
const reTickInterval = /^([1-9][0-9]*)(minute|hour|day|week|month)$/;
|
||||
const resultTickInterval = reTickInterval.exec(
|
||||
diagObj.db.getTickInterval() || conf.tickInterval
|
||||
);
|
||||
|
||||
if (resultTickInterval !== null) {
|
||||
const every = resultTickInterval[1];
|
||||
const interval = resultTickInterval[2];
|
||||
switch (interval) {
|
||||
case 'minute':
|
||||
bottomXAxis.ticks(timeMinute.every(every));
|
||||
break;
|
||||
case 'hour':
|
||||
bottomXAxis.ticks(timeHour.every(every));
|
||||
break;
|
||||
case 'day':
|
||||
bottomXAxis.ticks(timeDay.every(every));
|
||||
break;
|
||||
case 'week':
|
||||
bottomXAxis.ticks(timeWeek.every(every));
|
||||
break;
|
||||
case 'month':
|
||||
bottomXAxis.ticks(timeMonth.every(every));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
svg
|
||||
.append('g')
|
||||
.attr('class', 'grid')
|
||||
@ -512,6 +544,28 @@ export const draw = function (text, id, version, diagObj) {
|
||||
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
|
||||
.tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
|
||||
|
||||
if (resultTickInterval !== null) {
|
||||
const every = resultTickInterval[1];
|
||||
const interval = resultTickInterval[2];
|
||||
switch (interval) {
|
||||
case 'minute':
|
||||
topXAxis.ticks(timeMinute.every(every));
|
||||
break;
|
||||
case 'hour':
|
||||
topXAxis.ticks(timeHour.every(every));
|
||||
break;
|
||||
case 'day':
|
||||
topXAxis.ticks(timeDay.every(every));
|
||||
break;
|
||||
case 'week':
|
||||
topXAxis.ticks(timeWeek.every(every));
|
||||
break;
|
||||
case 'month':
|
||||
topXAxis.ticks(timeMonth.every(every));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
svg
|
||||
.append('g')
|
||||
.attr('class', 'grid')
|
||||
|
@ -82,6 +82,7 @@ that id.
|
||||
"inclusiveEndDates" return 'inclusiveEndDates';
|
||||
"topAxis" return 'topAxis';
|
||||
"axisFormat"\s[^#\n;]+ return 'axisFormat';
|
||||
"tickInterval"\s[^#\n;]+ return 'tickInterval';
|
||||
"includes"\s[^#\n;]+ return 'includes';
|
||||
"excludes"\s[^#\n;]+ return 'excludes';
|
||||
"todayMarker"\s[^\n;]+ return 'todayMarker';
|
||||
@ -125,6 +126,7 @@ statement
|
||||
| inclusiveEndDates {yy.enableInclusiveEndDates();$$=$1.substr(18);}
|
||||
| topAxis {yy.TopAxis();$$=$1.substr(8);}
|
||||
| axisFormat {yy.setAxisFormat($1.substr(11));$$=$1.substr(11);}
|
||||
| tickInterval {yy.setTickInterval($1.substr(13));$$=$1.substr(13);}
|
||||
| excludes {yy.setExcludes($1.substr(9));$$=$1.substr(9);}
|
||||
| includes {yy.setIncludes($1.substr(9));$$=$1.substr(9);}
|
||||
| todayMarker {yy.setTodayMarker($1.substr(12));$$=$1.substr(12);}
|
||||
|
@ -334,6 +334,31 @@ describe('when parsing a gitGraph', function () {
|
||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(2);
|
||||
});
|
||||
|
||||
it('should allow quoted branch names', function () {
|
||||
const str = `gitGraph:
|
||||
commit
|
||||
branch "branch"
|
||||
checkout "branch"
|
||||
commit
|
||||
checkout main
|
||||
merge "branch"
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
const commits = parser.yy.getCommits();
|
||||
expect(Object.keys(commits).length).toBe(3);
|
||||
expect(parser.yy.getCurrentBranch()).toBe('main');
|
||||
expect(parser.yy.getDirection()).toBe('LR');
|
||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(2);
|
||||
const commit1 = Object.keys(commits)[0];
|
||||
const commit2 = Object.keys(commits)[1];
|
||||
const commit3 = Object.keys(commits)[2];
|
||||
expect(commits[commit1].branch).toBe('main');
|
||||
expect(commits[commit2].branch).toBe('branch');
|
||||
expect(commits[commit3].branch).toBe('main');
|
||||
expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([{ name: 'main' }, { name: 'branch' }]);
|
||||
});
|
||||
|
||||
it('should allow _-./ characters in branch names', function () {
|
||||
const str = `gitGraph:
|
||||
commit
|
||||
|
@ -109,12 +109,12 @@ statement
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
||||
| branchStatement
|
||||
| CHECKOUT ID {yy.checkout($2)}
|
||||
| CHECKOUT ref {yy.checkout($2)}
|
||||
// | RESET reset_arg {yy.reset($2)}
|
||||
;
|
||||
branchStatement
|
||||
: BRANCH ID {yy.branch($2)}
|
||||
| BRANCH ID ORDER NUM {yy.branch($2, $4)}
|
||||
: BRANCH ref {yy.branch($2)}
|
||||
| BRANCH ref ORDER NUM {yy.branch($2, $4)}
|
||||
;
|
||||
|
||||
cherryPickStatement
|
||||
@ -126,22 +126,22 @@ cherryPickStatement
|
||||
;
|
||||
|
||||
mergeStatement
|
||||
: MERGE ID {yy.merge($2,'','','')}
|
||||
| MERGE ID COMMIT_ID STR {yy.merge($2, $4,'','')}
|
||||
| MERGE ID COMMIT_TYPE commitType {yy.merge($2,'', $4,'')}
|
||||
| MERGE ID COMMIT_TAG STR {yy.merge($2, '','',$4)}
|
||||
| MERGE ID COMMIT_TAG STR COMMIT_ID STR {yy.merge($2, $6,'', $4)}
|
||||
| MERGE ID COMMIT_TAG STR COMMIT_TYPE commitType {yy.merge($2, '',$6, $4)}
|
||||
| MERGE ID COMMIT_TYPE commitType COMMIT_TAG STR {yy.merge($2, '',$4, $6)}
|
||||
| MERGE ID COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $4, $6, '')}
|
||||
| MERGE ID COMMIT_ID STR COMMIT_TAG STR {yy.merge($2, $4, '', $6)}
|
||||
| MERGE ID COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $6,$4, '')}
|
||||
| MERGE ID COMMIT_ID STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.merge($2, $4, $6, $8)}
|
||||
| MERGE ID COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_ID STR {yy.merge($2, $8, $4, $6)}
|
||||
| MERGE ID COMMIT_ID STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.merge($2, $4, $8, $6)}
|
||||
| MERGE ID COMMIT_TYPE commitType COMMIT_ID STR COMMIT_TAG STR {yy.merge($2, $6, $4, $8)}
|
||||
| MERGE ID COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $8, $6, $4)}
|
||||
| MERGE ID COMMIT_TAG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $6, $8, $4)}
|
||||
: MERGE ref {yy.merge($2,'','','')}
|
||||
| MERGE ref COMMIT_ID STR {yy.merge($2, $4,'','')}
|
||||
| MERGE ref COMMIT_TYPE commitType {yy.merge($2,'', $4,'')}
|
||||
| MERGE ref COMMIT_TAG STR {yy.merge($2, '','',$4)}
|
||||
| MERGE ref COMMIT_TAG STR COMMIT_ID STR {yy.merge($2, $6,'', $4)}
|
||||
| MERGE ref COMMIT_TAG STR COMMIT_TYPE commitType {yy.merge($2, '',$6, $4)}
|
||||
| MERGE ref COMMIT_TYPE commitType COMMIT_TAG STR {yy.merge($2, '',$4, $6)}
|
||||
| MERGE ref COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $4, $6, '')}
|
||||
| MERGE ref COMMIT_ID STR COMMIT_TAG STR {yy.merge($2, $4, '', $6)}
|
||||
| MERGE ref COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $6,$4, '')}
|
||||
| MERGE ref COMMIT_ID STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.merge($2, $4, $6, $8)}
|
||||
| MERGE ref COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_ID STR {yy.merge($2, $8, $4, $6)}
|
||||
| MERGE ref COMMIT_ID STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.merge($2, $4, $8, $6)}
|
||||
| MERGE ref COMMIT_TYPE commitType COMMIT_ID STR COMMIT_TAG STR {yy.merge($2, $6, $4, $8)}
|
||||
| MERGE ref COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $8, $6, $4)}
|
||||
| MERGE ref COMMIT_TAG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $6, $8, $4)}
|
||||
;
|
||||
|
||||
commitStatement
|
||||
@ -261,6 +261,11 @@ closeDirective
|
||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'gitGraph'); }
|
||||
;
|
||||
|
||||
ref
|
||||
: ID
|
||||
| STR
|
||||
;
|
||||
|
||||
eol
|
||||
: NL
|
||||
| ';'
|
||||
|
189
packages/mermaid/src/diagrams/state/parser/state-style.spec.js
Normal file
189
packages/mermaid/src/diagrams/state/parser/state-style.spec.js
Normal file
@ -0,0 +1,189 @@
|
||||
import stateDb from '../stateDb';
|
||||
import stateDiagram from './stateDiagram';
|
||||
import { setConfig } from '../../../config';
|
||||
|
||||
setConfig({
|
||||
securityLevel: 'strict',
|
||||
});
|
||||
|
||||
describe('ClassDefs and classes when parsing a State diagram', () => {
|
||||
beforeEach(function () {
|
||||
stateDiagram.parser.yy = stateDb;
|
||||
stateDiagram.parser.yy.clear();
|
||||
});
|
||||
|
||||
describe('class for a state (classDef)', () => {
|
||||
describe('defining (classDef)', () => {
|
||||
it('has "classDef" as a keyword, an id, and can set a css style attribute', function () {
|
||||
stateDiagram.parser.parse('stateDiagram-v2\n classDef exampleClass background:#bbb;');
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const styleClasses = stateDb.getClasses();
|
||||
expect(styleClasses['exampleClass'].styles.length).toEqual(1);
|
||||
expect(styleClasses['exampleClass'].styles[0]).toEqual('background:#bbb');
|
||||
});
|
||||
|
||||
it('can define multiple attributes separated by commas', function () {
|
||||
stateDiagram.parser.parse(
|
||||
'stateDiagram-v2\n classDef exampleClass background:#bbb, font-weight:bold, font-style:italic;'
|
||||
);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const styleClasses = stateDb.getClasses();
|
||||
expect(styleClasses['exampleClass'].styles.length).toEqual(3);
|
||||
expect(styleClasses['exampleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(styleClasses['exampleClass'].styles[1]).toEqual('font-weight:bold');
|
||||
expect(styleClasses['exampleClass'].styles[2]).toEqual('font-style:italic');
|
||||
});
|
||||
|
||||
// need to look at what the lexer is doing
|
||||
it('an attribute can have a dot in the style', function () {
|
||||
stateDiagram.parser.parse(
|
||||
'stateDiagram-v2\n classDef exampleStyleClass background:#bbb,border:1.5px solid red;'
|
||||
);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toBe(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toBe('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toBe('border:1.5px solid red');
|
||||
});
|
||||
|
||||
it('an attribute can have a space in the style', function () {
|
||||
stateDiagram.parser.parse(
|
||||
'stateDiagram-v2\n classDef exampleStyleClass background: #bbb,border:1.5px solid red;'
|
||||
);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toBe(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toBe('background: #bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toBe('border:1.5px solid red');
|
||||
});
|
||||
});
|
||||
|
||||
describe('applying to states in the diagram', () => {
|
||||
it('can apply a class to a state', function () {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n' + '\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a --> b ';
|
||||
diagram += 'class a exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDb.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
const state_a = stateDb.getState('a');
|
||||
expect(state_a.classes.length).toEqual(1);
|
||||
expect(state_a.classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('can be applied to a state with an id containing _', function () {
|
||||
let diagram = '';
|
||||
|
||||
diagram += 'stateDiagram-v2\n' + '\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a_a --> b_b' + '\n';
|
||||
diagram += 'class a_a exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toBe(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toBe('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toBe('border:1px solid red');
|
||||
|
||||
const state_a_a = stateDiagram.parser.yy.getState('a_a');
|
||||
expect(state_a_a.classes.length).toEqual(1);
|
||||
expect(state_a_a.classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
describe('::: syntax', () => {
|
||||
it('can be applied to a state using ::: syntax', () => {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n' + '\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;' + '\n';
|
||||
diagram += 'a --> b:::exampleStyleClass' + '\n';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const states = stateDiagram.parser.yy.getStates();
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
expect(states['b'].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('can be applied to a [*] state', () => {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += '[*]:::exampleStyleClass --> b\n';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const states = stateDiagram.parser.yy.getStates();
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
expect(states['root_start'].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('can be applied to a comma separated list of states', function () {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a-->b\n';
|
||||
diagram += 'class a,b exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
let classes = stateDiagram.parser.yy.getClasses();
|
||||
let states = stateDiagram.parser.yy.getStates();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
expect(states['a'].classes[0]).toEqual('exampleStyleClass');
|
||||
expect(states['b'].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('a comma separated list of states may or may not have spaces after commas', function () {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a-->b\n';
|
||||
diagram += 'class a,b,c, d, e exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
const states = stateDiagram.parser.yy.getStates();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
const statesList = ['a', 'b', 'c', 'd', 'e'];
|
||||
statesList.forEach((stateId) => {
|
||||
expect(states[stateId].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -23,6 +23,10 @@
|
||||
%x acc_title
|
||||
%x acc_descr
|
||||
%x acc_descr_multiline
|
||||
%x CLASSDEF
|
||||
%x CLASSDEFID
|
||||
%x CLASS
|
||||
%x CLASS_STYLE
|
||||
%x NOTE
|
||||
%x NOTE_ID
|
||||
%x NOTE_TEXT
|
||||
@ -39,6 +43,8 @@
|
||||
|
||||
%%
|
||||
|
||||
"default" return 'DEFAULT';
|
||||
|
||||
.*direction\s+TB[^\n]* return 'direction_tb';
|
||||
.*direction\s+BT[^\n]* return 'direction_bt';
|
||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||
@ -69,6 +75,20 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
||||
<acc_descr_multiline>[\}] { this.popState(); }
|
||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
||||
|
||||
<INITIAL,struct>"classDef"\s+ { this.pushState('CLASSDEF'); return 'classDef'; }
|
||||
<CLASSDEF>DEFAULT\s+ { this.popState(); this.pushState('CLASSDEFID'); return 'DEFAULT_CLASSDEF_ID' }
|
||||
<CLASSDEF>\w+\s+ { this.popState(); this.pushState('CLASSDEFID'); return 'CLASSDEF_ID' }
|
||||
<CLASSDEFID>[^\n]* { this.popState(); return 'CLASSDEF_STYLEOPTS' }
|
||||
|
||||
<INITIAL,struct>"class"\s+ { this.pushState('CLASS'); return 'class'; }
|
||||
<CLASS>(\w+)+((","\s*\w+)*) { this.popState(); this.pushState('CLASS_STYLE'); return 'CLASSENTITY_IDS' }
|
||||
<CLASS_STYLE>[^\n]* { this.popState(); return 'STYLECLASS' }
|
||||
|
||||
"scale"\s+ { this.pushState('SCALE'); /* console.log('Got scale', yytext);*/ return 'scale'; }
|
||||
<SCALE>\d+ return 'WIDTH';
|
||||
<SCALE>\s+"width" {this.popState();}
|
||||
|
||||
|
||||
<INITIAL,struct>"state"\s+ { /*console.log('Starting STATE zxzx'+yy.getDirection());*/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';}
|
||||
@ -111,10 +131,12 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
||||
<INITIAL,struct>[^:\n\s\-\{]+ { /*console.log('=>ID=',yytext);*/ return 'ID';}
|
||||
// <INITIAL,struct>\s*":"[^\+\->:\n;]+ { yytext = yytext.trim(); /*console.log('Descr = ', yytext);*/ return 'DESCR'; }
|
||||
<INITIAL,struct>\s*":"[^:\n;]+ { yytext = yytext.trim(); /*console.log('Descr = ', yytext);*/ return 'DESCR'; }
|
||||
|
||||
<INITIAL,struct>"-->" return '-->';
|
||||
<struct>"--" return 'CONCURRENT';
|
||||
<<EOF>> return 'NL';
|
||||
. return 'INVALID';
|
||||
<struct>"--" return 'CONCURRENT';
|
||||
":::" return 'STYLE_SEPARATOR';
|
||||
<<EOF>> return 'NL';
|
||||
. return 'INVALID';
|
||||
|
||||
/lex
|
||||
|
||||
@ -124,20 +146,23 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
/* $$ is the value of the symbol being evaluated (= what is to the left of the : in the rule */
|
||||
|
||||
start
|
||||
: SPACE start
|
||||
| NL start
|
||||
| directive start
|
||||
| SD document { /*console.warn('Root document', $2);*/ yy.setRootDoc($2);return $2; }
|
||||
| SD document { /* console.log('--> Root document', $2); */ yy.setRootDoc($2); return $2; }
|
||||
;
|
||||
|
||||
document
|
||||
: /* empty */ { $$ = [] }
|
||||
: /* empty */ { /*console.log('empty document'); */ $$ = [] }
|
||||
| document line {
|
||||
if($2!='nl'){
|
||||
$1.push($2);$$ = $1
|
||||
if($2 !='nl'){
|
||||
/* console.log(' document: 1: ', $1, ' pushing 2: ', $2); */
|
||||
$1.push($2); $$ = $1
|
||||
}
|
||||
// console.warn('Got document',$1, $2);
|
||||
/* console.log('Got document',$1, $2); */
|
||||
}
|
||||
;
|
||||
|
||||
@ -148,24 +173,34 @@ line
|
||||
;
|
||||
|
||||
statement
|
||||
: idStatement { /*console.warn('got id and descr', $1);*/$$={ stmt: 'state', id: $1, type: 'default', description: ''};}
|
||||
| idStatement DESCR { /*console.warn('got id and descr', $1, $2.trim());*/$$={ stmt: 'state', id: $1, type: 'default', description: yy.trimColon($2)};}
|
||||
: classDefStatement
|
||||
| cssClassStatement
|
||||
| idStatement { /* console.log('got id', $1); */
|
||||
$$=$1;
|
||||
}
|
||||
| idStatement DESCR {
|
||||
const stateStmt = $1;
|
||||
stateStmt.description = yy.trimColon($2);
|
||||
$$ = stateStmt;
|
||||
}
|
||||
| idStatement '-->' idStatement
|
||||
{
|
||||
/*console.warn('got id', $1);yy.addRelation($1, $3);*/
|
||||
$$={ stmt: 'relation', state1: { stmt: 'state', id: $1, type: 'default', description: '' }, state2:{ stmt: 'state', id: $3 ,type: 'default', description: ''}};
|
||||
}
|
||||
{
|
||||
/* console.info('got ids: 1: ', $1, ' 2:', $2,' 3: ', $3); */
|
||||
// console.log(' idStatement --> idStatement : state1 =', $1, ' state2 =', $3);
|
||||
$$={ stmt: 'relation', state1: $1, state2: $3};
|
||||
}
|
||||
| idStatement '-->' idStatement DESCR
|
||||
{
|
||||
/*yy.addRelation($1, $3, $4.substr(1).trim());*/
|
||||
$$={ stmt: 'relation', state1: { stmt: 'state', id: $1, type: 'default', description: '' }, state2:{ stmt: 'state', id: $3 ,type: 'default', description: ''}, description: $4.substr(1).trim()};
|
||||
}
|
||||
{
|
||||
const relDescription = yy.trimColon($4);
|
||||
/* console.log(' idStatement --> idStatement DESCR : state1 =', $1, ' state2stmt =', $3, ' description: ', relDescription); */
|
||||
$$={ stmt: 'relation', state1: $1, state2: $3, description: relDescription};
|
||||
}
|
||||
| HIDE_EMPTY
|
||||
| scale WIDTH
|
||||
| COMPOSIT_STATE
|
||||
| COMPOSIT_STATE STRUCT_START document STRUCT_STOP
|
||||
{
|
||||
/* console.warn('Adding document for state without id ', $1);*/
|
||||
/* console.log('Adding document for state without id ', $1); */
|
||||
$$={ stmt: 'state', id: $1, type: 'default', description: '', doc: $3 }
|
||||
}
|
||||
| STATE_DESCR AS ID {
|
||||
@ -181,7 +216,7 @@ statement
|
||||
}
|
||||
| STATE_DESCR AS ID STRUCT_START document STRUCT_STOP
|
||||
{
|
||||
// console.warn('Adding document for state with id zxzx', $3, $4, yy.getDirection()); yy.addDocument($3);
|
||||
/* console.log('Adding document for state with id zxzx', $3, $4, yy.getDirection()); yy.addDocument($3);*/
|
||||
$$={ stmt: 'state', id: $3, type: 'default', description: $1, doc: $5 }
|
||||
}
|
||||
| FORK {
|
||||
@ -208,6 +243,23 @@ statement
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } ;
|
||||
|
||||
|
||||
classDefStatement
|
||||
: classDef CLASSDEF_ID CLASSDEF_STYLEOPTS {
|
||||
$$ = { stmt: 'classDef', id: $2.trim(), classes: $3.trim() };
|
||||
}
|
||||
| classDef DEFAULT CLASSDEF_STYLEOPTS {
|
||||
$$ = { stmt: 'classDef', id: $2.trim(), classes: $3.trim() };
|
||||
}
|
||||
;
|
||||
|
||||
cssClassStatement
|
||||
: class CLASSENTITY_IDS STYLECLASS {
|
||||
//console.log('apply class: id(s): ',$2, ' style class: ', $3);
|
||||
$$={ stmt: 'applyClass', id: $2.trim(), styleClass: $3.trim() };
|
||||
}
|
||||
;
|
||||
|
||||
directive
|
||||
: openDirective typeDirective closeDirective
|
||||
| openDirective typeDirective ':' argDirective closeDirective
|
||||
@ -229,8 +281,22 @@ eol
|
||||
;
|
||||
|
||||
idStatement
|
||||
: ID {$$=$1;}
|
||||
| EDGE_STATE {$$=$1;}
|
||||
: ID
|
||||
{ /* console.log('idStatement id: ', $1); */
|
||||
$$={ stmt: 'state', id: $1.trim(), type: 'default', description: '' };
|
||||
}
|
||||
| EDGE_STATE
|
||||
{ /* console.log('idStatement id: ', $1); */
|
||||
$$={ stmt: 'state', id: $1.trim(), type: 'default', description: '' };
|
||||
}
|
||||
| ID STYLE_SEPARATOR ID
|
||||
{ /*console.log('idStatement ID STYLE_SEPARATOR ID'); */
|
||||
$$={ stmt: 'state', id: $1.trim(), classes: [$3.trim()], type: 'default', description: '' };
|
||||
}
|
||||
| EDGE_STATE STYLE_SEPARATOR ID
|
||||
{ /*console.log('idStatement EDGE_STATE STYLE_SEPARATOR ID'); */
|
||||
$$={ stmt: 'state', id: $1.trim(), classes: [$3.trim()], type: 'default', description: '' };
|
||||
}
|
||||
;
|
||||
|
||||
notePosition
|
||||
|
32
packages/mermaid/src/diagrams/state/stateCommon.ts
Normal file
32
packages/mermaid/src/diagrams/state/stateCommon.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Constants common to all State Diagram code
|
||||
*/
|
||||
|
||||
// default diagram direction
|
||||
export const DEFAULT_DIAGRAM_DIRECTION = 'LR';
|
||||
|
||||
// default direction for any nested documents (composites)
|
||||
export const DEFAULT_NESTED_DOC_DIR = 'TB';
|
||||
|
||||
// parsed statement type for a state
|
||||
export const STMT_STATE = 'state';
|
||||
// parsed statement type for a relation
|
||||
export const STMT_RELATION = 'relation';
|
||||
// parsed statement type for a classDef
|
||||
export const STMT_CLASSDEF = 'classDef';
|
||||
// parsed statement type for applyClass
|
||||
export const STMT_APPLYCLASS = 'applyClass';
|
||||
|
||||
export const DEFAULT_STATE_TYPE = 'default';
|
||||
export const DIVIDER_TYPE = 'divider';
|
||||
|
||||
export default {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
DEFAULT_NESTED_DOC_DIR,
|
||||
STMT_STATE,
|
||||
STMT_RELATION,
|
||||
STMT_CLASSDEF,
|
||||
STMT_APPLYCLASS,
|
||||
DEFAULT_STATE_TYPE,
|
||||
DIVIDER_TYPE,
|
||||
};
|
@ -11,8 +11,58 @@ import {
|
||||
clear as commonClear,
|
||||
} from '../../commonDb';
|
||||
|
||||
const clone = (o) => JSON.parse(JSON.stringify(o));
|
||||
import {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
STMT_STATE,
|
||||
STMT_RELATION,
|
||||
STMT_CLASSDEF,
|
||||
STMT_APPLYCLASS,
|
||||
DEFAULT_STATE_TYPE,
|
||||
DIVIDER_TYPE,
|
||||
} from './stateCommon';
|
||||
|
||||
const START_NODE = '[*]';
|
||||
const START_TYPE = 'start';
|
||||
const END_NODE = START_NODE;
|
||||
const END_TYPE = 'end';
|
||||
|
||||
const COLOR_KEYWORD = 'color';
|
||||
const FILL_KEYWORD = 'fill';
|
||||
const BG_FILL = 'bgFill';
|
||||
const STYLECLASS_SEP = ',';
|
||||
|
||||
let direction = DEFAULT_DIAGRAM_DIRECTION;
|
||||
let rootDoc = [];
|
||||
let classes = []; // style classes defined by a classDef
|
||||
|
||||
const newDoc = () => {
|
||||
return {
|
||||
relations: [],
|
||||
states: {},
|
||||
documents: {},
|
||||
};
|
||||
};
|
||||
let documents = {
|
||||
root: newDoc(),
|
||||
};
|
||||
|
||||
let currentDocument = documents.root;
|
||||
let startEndCount = 0;
|
||||
let dividerCnt = 0;
|
||||
|
||||
export const lineType = {
|
||||
LINE: 0,
|
||||
DOTTED_LINE: 1,
|
||||
};
|
||||
|
||||
export const relationType = {
|
||||
AGGREGATION: 0,
|
||||
EXTENSION: 1,
|
||||
COMPOSITION: 2,
|
||||
DEPENDENCY: 3,
|
||||
};
|
||||
|
||||
const clone = (o) => JSON.parse(JSON.stringify(o));
|
||||
|
||||
export const parseDirective = function (statement, context, type) {
|
||||
mermaidAPI.parseDirective(this, statement, context, type);
|
||||
@ -27,11 +77,11 @@ const setRootDoc = (o) => {
|
||||
const getRootDoc = () => rootDoc;
|
||||
|
||||
const docTranslator = (parent, node, first) => {
|
||||
if (node.stmt === 'relation') {
|
||||
if (node.stmt === STMT_RELATION) {
|
||||
docTranslator(parent, node.state1, true);
|
||||
docTranslator(parent, node.state2, false);
|
||||
} else {
|
||||
if (node.stmt === 'state') {
|
||||
if (node.stmt === STMT_STATE) {
|
||||
if (node.id === '[*]') {
|
||||
node.id = first ? parent.id + '_start' : parent.id + '_end';
|
||||
node.start = first;
|
||||
@ -41,10 +91,10 @@ const docTranslator = (parent, node, first) => {
|
||||
if (node.doc) {
|
||||
const doc = [];
|
||||
// Check for concurrency
|
||||
let i = 0;
|
||||
let currentDoc = [];
|
||||
let i;
|
||||
for (i = 0; i < node.doc.length; i++) {
|
||||
if (node.doc[i].type === 'divider') {
|
||||
if (node.doc[i].type === DIVIDER_TYPE) {
|
||||
// debugger;
|
||||
const newNode = clone(node.doc[i]);
|
||||
newNode.doc = clone(currentDoc);
|
||||
@ -58,7 +108,7 @@ const docTranslator = (parent, node, first) => {
|
||||
// If any divider was encountered
|
||||
if (doc.length > 0 && currentDoc.length > 0) {
|
||||
const newNode = {
|
||||
stmt: 'state',
|
||||
stmt: STMT_STATE,
|
||||
id: generateId(),
|
||||
type: 'divider',
|
||||
doc: clone(currentDoc),
|
||||
@ -77,6 +127,17 @@ const getRootDocV2 = () => {
|
||||
// Here
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert all of the statements (stmts) that were parsed into states and relationships.
|
||||
* This is done because a state diagram may have nested sections,
|
||||
* where each section is a 'document' and has its own set of statements.
|
||||
* Ex: the section within a fork has its own statements, and incoming and outgoing statements
|
||||
* refer to the fork as a whole (document).
|
||||
* See the parser grammar: the definition of a document is a document then a 'line', where a line can be a statement.
|
||||
* This will push the statement into the the list of statements for the current document.
|
||||
*
|
||||
* @param _doc
|
||||
*/
|
||||
const extract = (_doc) => {
|
||||
// const res = { states: [], relations: [] };
|
||||
let doc;
|
||||
@ -95,48 +156,66 @@ const extract = (_doc) => {
|
||||
log.info('Extract', doc);
|
||||
|
||||
doc.forEach((item) => {
|
||||
if (item.stmt === 'state') {
|
||||
addState(item.id, item.type, item.doc, item.description, item.note);
|
||||
}
|
||||
if (item.stmt === 'relation') {
|
||||
addRelation(item.state1.id, item.state2.id, item.description);
|
||||
switch (item.stmt) {
|
||||
case STMT_STATE:
|
||||
addState(
|
||||
item.id,
|
||||
item.type,
|
||||
item.doc,
|
||||
item.description,
|
||||
item.note,
|
||||
item.classes,
|
||||
item.styles,
|
||||
item.textStyles
|
||||
);
|
||||
break;
|
||||
case STMT_RELATION:
|
||||
addRelation(item.state1, item.state2, item.description);
|
||||
break;
|
||||
case STMT_CLASSDEF:
|
||||
addStyleClass(item.id, item.classes);
|
||||
break;
|
||||
case STMT_APPLYCLASS:
|
||||
setCssClass(item.id, item.styleClass);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const newDoc = () => {
|
||||
return {
|
||||
relations: [],
|
||||
states: {},
|
||||
documents: {},
|
||||
};
|
||||
};
|
||||
|
||||
let documents = {
|
||||
root: newDoc(),
|
||||
};
|
||||
|
||||
let currentDocument = documents.root;
|
||||
|
||||
let startCnt = 0;
|
||||
|
||||
/**
|
||||
* Function called by parser when a node definition has been found.
|
||||
*
|
||||
* @param {any} id
|
||||
* @param {any} type
|
||||
* @param {any} doc
|
||||
* @param {any} descr
|
||||
* @param {any} note
|
||||
* @param {null | string} id
|
||||
* @param {null | string} type
|
||||
* @param {null | string} doc
|
||||
* @param {null | string | string[]} descr - description for the state. Can be a string or a list or strings
|
||||
* @param {null | string} note
|
||||
* @param {null | string | string[]} classes - class styles to apply to this state. Can be a string (1 style) or an array of styles. If it's just 1 class, convert it to an array of that 1 class.
|
||||
* @param {null | string | string[]} styles - styles to apply to this state. Can be a string (1 style) or an array of styles. If it's just 1 style, convert it to an array of that 1 style.
|
||||
* @param {null | string | string[]} textStyles - text styles to apply to this state. Can be a string (1 text test) or an array of text styles. If it's just 1 text style, convert it to an array of that 1 text style.
|
||||
*/
|
||||
export const addState = function (id, type, doc, descr, note) {
|
||||
export const addState = function (
|
||||
id,
|
||||
type = DEFAULT_STATE_TYPE,
|
||||
doc = null,
|
||||
descr = null,
|
||||
note = null,
|
||||
classes = null,
|
||||
styles = null,
|
||||
textStyles = null
|
||||
) {
|
||||
// add the state if needed
|
||||
if (typeof currentDocument.states[id] === 'undefined') {
|
||||
log.info('Adding state ', id, descr);
|
||||
currentDocument.states[id] = {
|
||||
id: id,
|
||||
descriptions: [],
|
||||
type,
|
||||
doc,
|
||||
note,
|
||||
classes: [],
|
||||
styles: [],
|
||||
textStyles: [],
|
||||
};
|
||||
} else {
|
||||
if (!currentDocument.states[id].doc) {
|
||||
@ -146,8 +225,9 @@ export const addState = function (id, type, doc, descr, note) {
|
||||
currentDocument.states[id].type = type;
|
||||
}
|
||||
}
|
||||
|
||||
if (descr) {
|
||||
log.info('Adding state ', id, descr);
|
||||
log.info('Setting state description', id, descr);
|
||||
if (typeof descr === 'string') {
|
||||
addDescription(id, descr.trim());
|
||||
}
|
||||
@ -164,6 +244,24 @@ export const addState = function (id, type, doc, descr, note) {
|
||||
configApi.getConfig()
|
||||
);
|
||||
}
|
||||
|
||||
if (classes) {
|
||||
log.info('Setting state classes', id, classes);
|
||||
const classesList = typeof classes === 'string' ? [classes] : classes;
|
||||
classesList.forEach((klass) => setCssClass(id, klass.trim()));
|
||||
}
|
||||
|
||||
if (styles) {
|
||||
log.info('Setting state styles', id, styles);
|
||||
const stylesList = typeof styles === 'string' ? [styles] : styles;
|
||||
stylesList.forEach((style) => setStyle(id, style.trim()));
|
||||
}
|
||||
|
||||
if (textStyles) {
|
||||
log.info('Setting state styles', id, styles);
|
||||
const textStylesList = typeof textStyles === 'string' ? [textStyles] : textStyles;
|
||||
textStylesList.forEach((textStyle) => setTextStyle(id, textStyle.trim()));
|
||||
}
|
||||
};
|
||||
|
||||
export const clear = function (saveCommon) {
|
||||
@ -174,7 +272,8 @@ export const clear = function (saveCommon) {
|
||||
|
||||
currentDocument = documents.root;
|
||||
|
||||
startCnt = 0;
|
||||
// number of start and end nodes; used to construct ids
|
||||
startEndCount = 0;
|
||||
classes = [];
|
||||
if (!saveCommon) {
|
||||
commonClear();
|
||||
@ -195,36 +294,134 @@ export const getRelations = function () {
|
||||
return currentDocument.relations;
|
||||
};
|
||||
|
||||
export const addRelation = function (_id1, _id2, title) {
|
||||
let id1 = _id1;
|
||||
let id2 = _id2;
|
||||
let type1 = 'default';
|
||||
let type2 = 'default';
|
||||
if (_id1 === '[*]') {
|
||||
startCnt++;
|
||||
id1 = 'start' + startCnt;
|
||||
type1 = 'start';
|
||||
/**
|
||||
* If the id is a start node ( [*] ), then return a new id constructed from
|
||||
* the start node name and the current start node count.
|
||||
* else return the given id
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {{id: string, type: string}} - the id and type that should be used
|
||||
*/
|
||||
function startIdIfNeeded(id = '') {
|
||||
let fixedId = id;
|
||||
if (id === START_NODE) {
|
||||
startEndCount++;
|
||||
fixedId = `${START_TYPE}${startEndCount}`;
|
||||
}
|
||||
if (_id2 === '[*]') {
|
||||
id2 = 'end' + startCnt;
|
||||
type2 = 'end';
|
||||
return fixedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the id is a start node ( [*] ), then return the start type ('start')
|
||||
* else return the given type
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {string} type
|
||||
* @returns {string} - the type that should be used
|
||||
*/
|
||||
function startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
|
||||
return id === START_NODE ? START_TYPE : type;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the id is an end node ( [*] ), then return a new id constructed from
|
||||
* the end node name and the current start_end node count.
|
||||
* else return the given id
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {{id: string, type: string}} - the id and type that should be used
|
||||
*/
|
||||
function endIdIfNeeded(id = '') {
|
||||
let fixedId = id;
|
||||
if (id === END_NODE) {
|
||||
startEndCount++;
|
||||
fixedId = `${END_TYPE}${startEndCount}`;
|
||||
}
|
||||
addState(id1, type1);
|
||||
addState(id2, type2);
|
||||
return fixedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the id is an end node ( [*] ), then return the end type
|
||||
* else return the given type
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {string} type
|
||||
* @returns {string} - the type that should be used
|
||||
*/
|
||||
function endTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
|
||||
return id === END_NODE ? END_TYPE : type;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param item1
|
||||
* @param item2
|
||||
* @param relationTitle
|
||||
*/
|
||||
export function addRelationObjs(item1, item2, relationTitle) {
|
||||
let id1 = startIdIfNeeded(item1.id);
|
||||
let type1 = startTypeIfNeeded(item1.id, item1.type);
|
||||
let id2 = startIdIfNeeded(item2.id);
|
||||
let type2 = startTypeIfNeeded(item2.id, item2.type);
|
||||
|
||||
addState(
|
||||
id1,
|
||||
type1,
|
||||
item1.doc,
|
||||
item1.description,
|
||||
item1.note,
|
||||
item1.classes,
|
||||
item1.styles,
|
||||
item1.textStyles
|
||||
);
|
||||
addState(
|
||||
id2,
|
||||
type2,
|
||||
item2.doc,
|
||||
item2.description,
|
||||
item2.note,
|
||||
item2.classes,
|
||||
item2.styles,
|
||||
item2.textStyles
|
||||
);
|
||||
|
||||
currentDocument.relations.push({
|
||||
id1,
|
||||
id2,
|
||||
title: common.sanitizeText(title, configApi.getConfig()),
|
||||
relationTitle: common.sanitizeText(relationTitle, configApi.getConfig()),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a relation between two items. The items may be full objects or just the string id of a state.
|
||||
*
|
||||
* @param {string | object} item1
|
||||
* @param {string | object} item2
|
||||
* @param {string} title
|
||||
*/
|
||||
export const addRelation = function (item1, item2, title) {
|
||||
if (typeof item1 === 'object') {
|
||||
addRelationObjs(item1, item2, title);
|
||||
} else {
|
||||
const id1 = startIdIfNeeded(item1);
|
||||
const type1 = startTypeIfNeeded(item1);
|
||||
const id2 = endIdIfNeeded(item2);
|
||||
const type2 = endTypeIfNeeded(item2);
|
||||
|
||||
addState(id1, type1);
|
||||
addState(id2, type2);
|
||||
currentDocument.relations.push({
|
||||
id1,
|
||||
id2,
|
||||
title: common.sanitizeText(title, configApi.getConfig()),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const addDescription = function (id, _descr) {
|
||||
export const addDescription = function (id, descr) {
|
||||
const theState = currentDocument.states[id];
|
||||
let descr = _descr;
|
||||
if (descr[0] === ':') {
|
||||
descr = descr.substr(1).trim();
|
||||
}
|
||||
theState.descriptions.push(common.sanitizeText(descr, configApi.getConfig()));
|
||||
const _descr = descr.startsWith(':') ? descr.replace(':', '').trim() : descr;
|
||||
theState.descriptions.push(common.sanitizeText(_descr, configApi.getConfig()));
|
||||
};
|
||||
|
||||
export const cleanupLabel = function (label) {
|
||||
@ -235,34 +432,105 @@ export const cleanupLabel = function (label) {
|
||||
}
|
||||
};
|
||||
|
||||
export const lineType = {
|
||||
LINE: 0,
|
||||
DOTTED_LINE: 1,
|
||||
};
|
||||
|
||||
let dividerCnt = 0;
|
||||
const getDividerId = () => {
|
||||
dividerCnt++;
|
||||
return 'divider-id-' + dividerCnt;
|
||||
};
|
||||
|
||||
let classes = [];
|
||||
/**
|
||||
* Called when the parser comes across a (style) class definition
|
||||
* @example classDef my-style fill:#f96;
|
||||
*
|
||||
* @param {string} id - the id of this (style) class
|
||||
* @param {string} styleAttributes - the string with 1 or more style attributes (each separated by a comma)
|
||||
*/
|
||||
export const addStyleClass = function (id, styleAttributes = '') {
|
||||
// create a new style class object with this id
|
||||
if (typeof classes[id] === 'undefined') {
|
||||
classes[id] = { id: id, styles: [], textStyles: [] };
|
||||
}
|
||||
const foundClass = classes[id];
|
||||
if (typeof styleAttributes !== 'undefined') {
|
||||
if (styleAttributes !== null) {
|
||||
styleAttributes.split(STYLECLASS_SEP).forEach((attrib) => {
|
||||
// remove any trailing ;
|
||||
const fixedAttrib = attrib.replace(/([^;]*);/, '$1').trim();
|
||||
|
||||
const getClasses = () => classes;
|
||||
// replace some style keywords
|
||||
if (attrib.match(COLOR_KEYWORD)) {
|
||||
const newStyle1 = fixedAttrib.replace(FILL_KEYWORD, BG_FILL);
|
||||
const newStyle2 = newStyle1.replace(COLOR_KEYWORD, FILL_KEYWORD);
|
||||
foundClass.textStyles.push(newStyle2);
|
||||
}
|
||||
foundClass.styles.push(fixedAttrib);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all of the style classes
|
||||
* @returns {{} | any | classes}
|
||||
*/
|
||||
export const getClasses = function () {
|
||||
return classes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a (style) class or css class to a state with the given id.
|
||||
* If the state isn't already in the list of known states, add it.
|
||||
* Might be called by parser when a style class or CSS class should be applied to a state
|
||||
*
|
||||
* @param {string | string[]} itemIds The id or a list of ids of the item(s) to apply the css class to
|
||||
* @param {string} cssClassName CSS class name
|
||||
*/
|
||||
export const setCssClass = function (itemIds, cssClassName) {
|
||||
itemIds.split(',').forEach(function (id) {
|
||||
let foundState = getState(id);
|
||||
if (typeof foundState === 'undefined') {
|
||||
const trimmedId = id.trim();
|
||||
addState(trimmedId);
|
||||
foundState = getState(trimmedId);
|
||||
}
|
||||
foundState.classes.push(cssClassName);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a style to a state with the given id.
|
||||
* @example style stateId fill:#f9f,stroke:#333,stroke-width:4px
|
||||
* where 'style' is the keyword
|
||||
* stateId is the id of a state
|
||||
* the rest of the string is the styleText (all of the attributes to be applied to the state)
|
||||
*
|
||||
* @param itemId The id of item to apply the style to
|
||||
* @param styleText - the text of the attributes for the style
|
||||
*/
|
||||
export const setStyle = function (itemId, styleText) {
|
||||
const item = getState(itemId);
|
||||
if (typeof item !== 'undefined') {
|
||||
item.textStyles.push(styleText);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a text style to a state with the given id
|
||||
*
|
||||
* @param itemId The id of item to apply the css class to
|
||||
* @param cssClassName CSS class name
|
||||
*/
|
||||
export const setTextStyle = function (itemId, cssClassName) {
|
||||
const item = getState(itemId);
|
||||
if (typeof item !== 'undefined') {
|
||||
item.textStyles.push(cssClassName);
|
||||
}
|
||||
};
|
||||
|
||||
let direction = 'TB';
|
||||
const getDirection = () => direction;
|
||||
const setDirection = (dir) => {
|
||||
direction = dir;
|
||||
};
|
||||
|
||||
export const relationType = {
|
||||
AGGREGATION: 0,
|
||||
EXTENSION: 1,
|
||||
COMPOSITION: 2,
|
||||
DEPENDENCY: 3,
|
||||
};
|
||||
|
||||
const trimColon = (str) => (str && str[0] === ':' ? str.substr(1).trim() : str.trim());
|
||||
|
||||
export default {
|
||||
@ -291,4 +559,7 @@ export default {
|
||||
setAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
addStyleClass,
|
||||
setCssClass,
|
||||
addDescription,
|
||||
};
|
||||
|
75
packages/mermaid/src/diagrams/state/stateDb.spec.js
Normal file
75
packages/mermaid/src/diagrams/state/stateDb.spec.js
Normal file
@ -0,0 +1,75 @@
|
||||
import stateDb from './stateDb';
|
||||
|
||||
describe('State Diagram stateDb', () => {
|
||||
beforeEach(() => {
|
||||
stateDb.clear();
|
||||
});
|
||||
|
||||
describe('addStyleClass', () => {
|
||||
it('is added to the list of style classes', () => {
|
||||
const newStyleClassId = 'newStyleClass';
|
||||
const newStyleClassAttribs = 'font-weight:bold, border:blue;';
|
||||
|
||||
stateDb.addStyleClass(newStyleClassId, newStyleClassAttribs);
|
||||
const styleClasses = stateDb.getClasses();
|
||||
expect(styleClasses[newStyleClassId].id).toEqual(newStyleClassId);
|
||||
expect(styleClasses[newStyleClassId].styles.length).toEqual(2);
|
||||
expect(styleClasses[newStyleClassId].styles[0]).toEqual('font-weight:bold');
|
||||
expect(styleClasses[newStyleClassId].styles[1]).toEqual('border:blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('addDescription to a state', () => {
|
||||
beforeEach(() => {
|
||||
stateDb.clear();
|
||||
stateDb.addState('state1');
|
||||
});
|
||||
|
||||
const testStateId = 'state1';
|
||||
|
||||
it('removes only the first leading :', () => {
|
||||
const restOfTheDescription = 'rest of the description';
|
||||
const oneLeadingColon = `:${restOfTheDescription}`;
|
||||
const twoLeadingColons = `::${restOfTheDescription}`;
|
||||
|
||||
stateDb.addDescription(testStateId, restOfTheDescription);
|
||||
let states = stateDb.getStates();
|
||||
expect(states[testStateId].descriptions[0]).toEqual(restOfTheDescription);
|
||||
|
||||
stateDb.addDescription(testStateId, oneLeadingColon);
|
||||
states = stateDb.getStates();
|
||||
expect(states[testStateId].descriptions[1]).toEqual(restOfTheDescription);
|
||||
|
||||
stateDb.addDescription(testStateId, twoLeadingColons);
|
||||
states = stateDb.getStates();
|
||||
expect(states[testStateId].descriptions[2]).toEqual(`:${restOfTheDescription}`);
|
||||
});
|
||||
|
||||
it('adds each description to the array of descriptions', () => {
|
||||
stateDb.addDescription(testStateId, 'description 0');
|
||||
stateDb.addDescription(testStateId, 'description 1');
|
||||
stateDb.addDescription(testStateId, 'description 2');
|
||||
|
||||
let states = stateDb.getStates();
|
||||
expect(states[testStateId].descriptions.length).toEqual(3);
|
||||
expect(states[testStateId].descriptions[0]).toEqual('description 0');
|
||||
expect(states[testStateId].descriptions[1]).toEqual('description 1');
|
||||
expect(states[testStateId].descriptions[2]).toEqual('description 2');
|
||||
});
|
||||
|
||||
it('sanitizes on the description', () => {
|
||||
stateDb.addDescription(
|
||||
testStateId,
|
||||
'desc outside the script <script>the description</script>'
|
||||
);
|
||||
let states = stateDb.getStates();
|
||||
expect(states[testStateId].descriptions[0]).toEqual('desc outside the script ');
|
||||
});
|
||||
|
||||
it('adds the description to the state with the given id', () => {
|
||||
stateDb.addDescription(testStateId, 'the description');
|
||||
let states = stateDb.getStates();
|
||||
expect(states[testStateId].descriptions[0]).toEqual('the description');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,10 +1,13 @@
|
||||
import { parser } from './parser/stateDiagram';
|
||||
import stateDb from './stateDb';
|
||||
import stateDiagram from './parser/stateDiagram.jison';
|
||||
|
||||
describe('state diagram, ', function () {
|
||||
describe('state diagram V2, ', function () {
|
||||
describe('when parsing an info graph it', function () {
|
||||
beforeEach(function () {
|
||||
parser.yy = stateDb;
|
||||
stateDiagram.parser.yy = stateDb;
|
||||
stateDiagram.parser.yy.clear();
|
||||
});
|
||||
|
||||
it('super simple', function () {
|
||||
@ -121,6 +124,30 @@ describe('state diagram, ', function () {
|
||||
parser.parse(str);
|
||||
});
|
||||
|
||||
describe('relationship labels', () => {
|
||||
it('simple states with : labels', () => {
|
||||
const diagram = `
|
||||
stateDiagram-v2
|
||||
[*] --> State1
|
||||
State1 --> State2 : Transition 1
|
||||
State1 --> State3 : Transition 2
|
||||
State1 --> State4 : Transition 3
|
||||
State1 --> [*]
|
||||
`;
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const rels = stateDb.getRelations();
|
||||
const rel_1_2 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State2');
|
||||
expect(rel_1_2.relationTitle).toEqual('Transition 1');
|
||||
const rel_1_3 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State3');
|
||||
expect(rel_1_3.relationTitle).toEqual('Transition 2');
|
||||
const rel_1_4 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State4');
|
||||
expect(rel_1_4.relationTitle).toEqual('Transition 3');
|
||||
});
|
||||
});
|
||||
|
||||
it('scale', function () {
|
||||
const str = `stateDiagram-v2\n
|
||||
scale 350 width
|
||||
@ -355,7 +382,7 @@ describe('state diagram, ', function () {
|
||||
|
||||
parser.parse(str);
|
||||
});
|
||||
it('should handle notes for composit states', function () {
|
||||
it('should handle notes for composite (nested) states', function () {
|
||||
const str = `stateDiagram-v2\n
|
||||
[*] --> NotShooting
|
||||
|
||||
@ -372,5 +399,28 @@ describe('state diagram, ', function () {
|
||||
|
||||
parser.parse(str);
|
||||
});
|
||||
|
||||
it('A composite state should be able to link to itself', () => {
|
||||
const diagram = `
|
||||
stateDiagram-v2
|
||||
state Active {
|
||||
Idle
|
||||
}
|
||||
Inactive --> Idle: ACT
|
||||
Active --> Active: LOG
|
||||
`;
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const states = stateDb.getStates();
|
||||
expect(states['Active'].doc[0].id).toEqual('Idle');
|
||||
|
||||
const rels = stateDb.getRelations();
|
||||
const rel_Inactive_Idle = rels.find((rel) => rel.id1 === 'Inactive' && rel.id2 === 'Idle');
|
||||
expect(rel_Inactive_Idle.relationTitle).toEqual('ACT');
|
||||
const rel_Active_Active = rels.find((rel) => rel.id1 === 'Active' && rel.id2 === 'Active');
|
||||
expect(rel_Active_Active.relationTitle).toEqual('LOG');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,8 +6,73 @@ import { log } from '../../logger';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox';
|
||||
import common from '../common/common';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
import {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
DEFAULT_NESTED_DOC_DIR,
|
||||
STMT_STATE,
|
||||
STMT_RELATION,
|
||||
DEFAULT_STATE_TYPE,
|
||||
DIVIDER_TYPE,
|
||||
} from './stateCommon';
|
||||
|
||||
// --------------------------------------
|
||||
// Shapes
|
||||
const SHAPE_STATE = 'rect';
|
||||
const SHAPE_STATE_WITH_DESC = 'rectWithTitle';
|
||||
const SHAPE_START = 'start';
|
||||
const SHAPE_END = 'end';
|
||||
const SHAPE_DIVIDER = 'divider';
|
||||
const SHAPE_GROUP = 'roundedWithTitle';
|
||||
const SHAPE_NOTE = 'note';
|
||||
const SHAPE_NOTEGROUP = 'noteGroup';
|
||||
|
||||
// --------------------------------------
|
||||
// CSS classes
|
||||
const CSS_DIAGRAM = 'statediagram';
|
||||
const CSS_STATE = 'state';
|
||||
const CSS_DIAGRAM_STATE = `${CSS_DIAGRAM}-${CSS_STATE}`;
|
||||
const CSS_EDGE = 'transition';
|
||||
const CSS_NOTE = 'note';
|
||||
const CSS_NOTE_EDGE = 'note-edge';
|
||||
const CSS_EDGE_NOTE_EDGE = `${CSS_EDGE} ${CSS_NOTE_EDGE}`;
|
||||
const CSS_DIAGRAM_NOTE = `${CSS_DIAGRAM}-${CSS_NOTE}`;
|
||||
const CSS_CLUSTER = 'cluster';
|
||||
const CSS_DIAGRAM_CLUSTER = `${CSS_DIAGRAM}-${CSS_CLUSTER}`;
|
||||
const CSS_CLUSTER_ALT = 'cluster-alt';
|
||||
const CSS_DIAGRAM_CLUSTER_ALT = `${CSS_DIAGRAM}-${CSS_CLUSTER_ALT}`;
|
||||
|
||||
// --------------------------------------
|
||||
// DOM and element IDs
|
||||
const PARENT = 'parent';
|
||||
const NOTE = 'note';
|
||||
const DOMID_STATE = 'state';
|
||||
const DOMID_TYPE_SPACER = '----';
|
||||
const NOTE_ID = `${DOMID_TYPE_SPACER}${NOTE}`;
|
||||
const PARENT_ID = `${DOMID_TYPE_SPACER}${PARENT}`;
|
||||
// --------------------------------------
|
||||
// Graph edge settings
|
||||
const G_EDGE_STYLE = 'fill:none';
|
||||
const G_EDGE_ARROWHEADSTYLE = 'fill: #333';
|
||||
const G_EDGE_LABELPOS = 'c';
|
||||
const G_EDGE_LABELTYPE = 'text';
|
||||
const G_EDGE_THICKNESS = 'normal';
|
||||
|
||||
// --------------------------------------
|
||||
// When information is parsed and processed (extracted) by stateDb.extract()
|
||||
// These are globals so the information can be accessed as needed (e.g. in setUpNode, etc.)
|
||||
let diagramStates = [];
|
||||
let diagramClasses = [];
|
||||
|
||||
// List of nodes created from the parsed diagram statement items
|
||||
let nodeDb = {};
|
||||
|
||||
let graphItemCount = 0; // used to construct ids, etc.
|
||||
|
||||
// Configuration
|
||||
const conf = {};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
export const setConf = function (cnf) {
|
||||
const keys = Object.keys(cnf);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
@ -15,223 +80,307 @@ export const setConf = function (cnf) {
|
||||
}
|
||||
};
|
||||
|
||||
let nodeDb = {};
|
||||
|
||||
/**
|
||||
* Returns the all the styles from classDef statements in the graph definition.
|
||||
*
|
||||
* @param {any} text
|
||||
* @param diag
|
||||
* @param {string} text - the diagram text to be parsed
|
||||
* @param {Diagram} diagramObj
|
||||
* @returns {object} ClassDef styles
|
||||
*/
|
||||
export const getClasses = function (text, diag) {
|
||||
export const getClasses = function (text, diagramObj) {
|
||||
log.trace('Extracting classes');
|
||||
diag.sb.clear();
|
||||
if (diagramClasses.length > 0) {
|
||||
return diagramClasses; // we have already extracted the classes
|
||||
}
|
||||
|
||||
// Parse the graph definition
|
||||
diag.parser.parse(text);
|
||||
return diag.sb.getClasses();
|
||||
diagramObj.db.clear();
|
||||
try {
|
||||
// Parse the graph definition
|
||||
diagramObj.parser.parse(text);
|
||||
// must run extract() to turn the parsed statements into states, relationships, classes, etc.
|
||||
diagramObj.db.extract(diagramObj.db.getRootDocV2());
|
||||
|
||||
return diagramObj.db.getClasses();
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
const setupNode = (g, parent, node, altFlag) => {
|
||||
// Add the node
|
||||
if (node.id !== 'root') {
|
||||
let shape = 'rect';
|
||||
if (node.start === true) {
|
||||
shape = 'start';
|
||||
/**
|
||||
* Get classes from the db info item.
|
||||
* If there aren't any or if dbInfoItem isn't defined, return an empty string.
|
||||
* Else create 1 string from the list of classes found
|
||||
*
|
||||
* @param {undefined | null | object} dbInfoItem
|
||||
* @returns {string}
|
||||
*/
|
||||
function getClassesFromDbInfo(dbInfoItem) {
|
||||
if (typeof dbInfoItem === 'undefined' || dbInfoItem === null) {
|
||||
return '';
|
||||
} else {
|
||||
if (dbInfoItem.classes) {
|
||||
return dbInfoItem.classes.join(' ');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
if (node.start === false) {
|
||||
shape = 'end';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a standard string for the dom ID of an item.
|
||||
* If a type is given, insert that before the counter, preceded by the type spacer
|
||||
*
|
||||
* @param itemId
|
||||
* @param counter
|
||||
* @param type
|
||||
* @param typeSpacer
|
||||
* @returns {string}
|
||||
*/
|
||||
export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOMID_TYPE_SPACER) {
|
||||
const typeStr = type !== null && type.length > 0 ? `${typeSpacer}${type}` : '';
|
||||
return `${DOMID_STATE}-${itemId}${typeStr}-${counter}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a graph node based on the statement information
|
||||
*
|
||||
* @param g - graph
|
||||
* @param {object} parent
|
||||
* @param {object} parsedItem - parsed statement item
|
||||
* @param {object} diagramDb
|
||||
* @param {boolean} altFlag - for clusters, add the "statediagram-cluster-alt" CSS class
|
||||
*/
|
||||
const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
|
||||
const itemId = parsedItem.id;
|
||||
const classStr = getClassesFromDbInfo(diagramStates[itemId]);
|
||||
|
||||
if (itemId !== 'root') {
|
||||
let shape = SHAPE_STATE;
|
||||
if (parsedItem.start === true) {
|
||||
shape = SHAPE_START;
|
||||
}
|
||||
if (node.type !== 'default') {
|
||||
shape = node.type;
|
||||
if (parsedItem.start === false) {
|
||||
shape = SHAPE_END;
|
||||
}
|
||||
if (parsedItem.type !== DEFAULT_STATE_TYPE) {
|
||||
shape = parsedItem.type;
|
||||
}
|
||||
|
||||
if (!nodeDb[node.id]) {
|
||||
nodeDb[node.id] = {
|
||||
id: node.id,
|
||||
// Add the node to our list (nodeDb)
|
||||
if (!nodeDb[itemId]) {
|
||||
nodeDb[itemId] = {
|
||||
id: itemId,
|
||||
shape,
|
||||
description: common.sanitizeText(node.id, getConfig()),
|
||||
classes: 'statediagram-state',
|
||||
description: common.sanitizeText(itemId, getConfig()),
|
||||
classes: `${classStr} ${CSS_DIAGRAM_STATE}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Build of the array of description strings according
|
||||
if (node.description) {
|
||||
if (Array.isArray(nodeDb[node.id].description)) {
|
||||
// There already is an array of strings,add to it
|
||||
nodeDb[node.id].shape = 'rectWithTitle';
|
||||
nodeDb[node.id].description.push(node.description);
|
||||
} else {
|
||||
if (nodeDb[node.id].description.length > 0) {
|
||||
// if there is a description already transform it to an array
|
||||
nodeDb[node.id].shape = 'rectWithTitle';
|
||||
if (nodeDb[node.id].description === node.id) {
|
||||
// If the previous description was the is, remove it
|
||||
nodeDb[node.id].description = [node.description];
|
||||
} else {
|
||||
nodeDb[node.id].description = [nodeDb[node.id].description, node.description];
|
||||
}
|
||||
} else {
|
||||
nodeDb[node.id].shape = 'rect';
|
||||
nodeDb[node.id].description = node.description;
|
||||
}
|
||||
}
|
||||
nodeDb[node.id].description = common.sanitizeTextOrArray(
|
||||
nodeDb[node.id].description,
|
||||
getConfig()
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
if (nodeDb[node.id].description.length === 1 && nodeDb[node.id].shape === 'rectWithTitle') {
|
||||
nodeDb[node.id].shape = 'rect';
|
||||
}
|
||||
const newNode = nodeDb[itemId];
|
||||
|
||||
// Save data for description and group so that for instance a statement without description overwrites
|
||||
// one with description
|
||||
// one with description @todo TODO What does this mean? If important, add a test for it
|
||||
|
||||
// group
|
||||
if (!nodeDb[node.id].type && node.doc) {
|
||||
log.info('Setting cluster for ', node.id, getDir(node));
|
||||
nodeDb[node.id].type = 'group';
|
||||
nodeDb[node.id].dir = getDir(node);
|
||||
nodeDb[node.id].shape = node.type === 'divider' ? 'divider' : 'roundedWithTitle';
|
||||
nodeDb[node.id].classes =
|
||||
nodeDb[node.id].classes +
|
||||
' ' +
|
||||
(altFlag ? 'statediagram-cluster statediagram-cluster-alt' : 'statediagram-cluster');
|
||||
// Build of the array of description strings
|
||||
if (parsedItem.description) {
|
||||
if (Array.isArray(newNode.description)) {
|
||||
// There already is an array of strings,add to it
|
||||
newNode.shape = SHAPE_STATE_WITH_DESC;
|
||||
newNode.description.push(parsedItem.description);
|
||||
} else {
|
||||
if (newNode.description.length > 0) {
|
||||
// if there is a description already transform it to an array
|
||||
newNode.shape = SHAPE_STATE_WITH_DESC;
|
||||
if (newNode.description === itemId) {
|
||||
// If the previous description was this, remove it
|
||||
newNode.description = [parsedItem.description];
|
||||
} else {
|
||||
newNode.description = [newNode.description, parsedItem.description];
|
||||
}
|
||||
} else {
|
||||
newNode.shape = SHAPE_STATE;
|
||||
newNode.description = parsedItem.description;
|
||||
}
|
||||
}
|
||||
newNode.description = common.sanitizeTextOrArray(newNode.description, getConfig());
|
||||
}
|
||||
|
||||
// If there's only 1 description entry, just use a regular state shape
|
||||
if (newNode.description.length === 1 && newNode.shape === SHAPE_STATE_WITH_DESC) {
|
||||
newNode.shape = SHAPE_STATE;
|
||||
}
|
||||
|
||||
// group
|
||||
if (!newNode.type && parsedItem.doc) {
|
||||
log.info('Setting cluster for ', itemId, getDir(parsedItem));
|
||||
newNode.type = 'group';
|
||||
newNode.dir = getDir(parsedItem);
|
||||
newNode.shape = parsedItem.type === DIVIDER_TYPE ? SHAPE_DIVIDER : SHAPE_GROUP;
|
||||
newNode.classes =
|
||||
newNode.classes +
|
||||
' ' +
|
||||
CSS_DIAGRAM_CLUSTER +
|
||||
' ' +
|
||||
(altFlag ? CSS_DIAGRAM_CLUSTER_ALT : '');
|
||||
}
|
||||
|
||||
// This is what will be added to the graph
|
||||
const nodeData = {
|
||||
labelStyle: '',
|
||||
shape: nodeDb[node.id].shape,
|
||||
labelText: nodeDb[node.id].description,
|
||||
// typeof nodeDb[node.id].description === 'object'
|
||||
// ? nodeDb[node.id].description[0]
|
||||
// : nodeDb[node.id].description,
|
||||
classes: nodeDb[node.id].classes, //classStr,
|
||||
shape: newNode.shape,
|
||||
labelText: newNode.description,
|
||||
// typeof newNode.description === 'object'
|
||||
// ? newNode.description[0]
|
||||
// : newNode.description,
|
||||
classes: newNode.classes,
|
||||
style: '', //styles.style,
|
||||
id: node.id,
|
||||
dir: nodeDb[node.id].dir,
|
||||
domId: 'state-' + node.id + '-' + cnt,
|
||||
type: nodeDb[node.id].type,
|
||||
id: itemId,
|
||||
dir: newNode.dir,
|
||||
domId: stateDomId(itemId, graphItemCount),
|
||||
type: newNode.type,
|
||||
padding: 15, //getConfig().flowchart.padding
|
||||
};
|
||||
|
||||
if (node.note) {
|
||||
if (parsedItem.note) {
|
||||
// Todo: set random id
|
||||
const noteData = {
|
||||
labelStyle: '',
|
||||
shape: 'note',
|
||||
labelText: node.note.text,
|
||||
classes: 'statediagram-note', //classStr,
|
||||
style: '', //styles.style,
|
||||
id: node.id + '----note-' + cnt,
|
||||
domId: 'state-' + node.id + '----note-' + cnt,
|
||||
type: nodeDb[node.id].type,
|
||||
shape: SHAPE_NOTE,
|
||||
labelText: parsedItem.note.text,
|
||||
classes: CSS_DIAGRAM_NOTE,
|
||||
style: '', // styles.style,
|
||||
id: itemId + NOTE_ID + '-' + graphItemCount,
|
||||
domId: stateDomId(itemId, graphItemCount, NOTE),
|
||||
type: newNode.type,
|
||||
padding: 15, //getConfig().flowchart.padding
|
||||
};
|
||||
const groupData = {
|
||||
labelStyle: '',
|
||||
shape: 'noteGroup',
|
||||
labelText: node.note.text,
|
||||
classes: nodeDb[node.id].classes, //classStr,
|
||||
style: '', //styles.style,
|
||||
id: node.id + '----parent',
|
||||
domId: 'state-' + node.id + '----parent-' + cnt,
|
||||
shape: SHAPE_NOTEGROUP,
|
||||
labelText: parsedItem.note.text,
|
||||
classes: newNode.classes,
|
||||
style: '', // styles.style,
|
||||
id: itemId + PARENT_ID,
|
||||
domId: stateDomId(itemId, graphItemCount, PARENT),
|
||||
type: 'group',
|
||||
padding: 0, //getConfig().flowchart.padding
|
||||
};
|
||||
cnt++;
|
||||
graphItemCount++;
|
||||
|
||||
g.setNode(node.id + '----parent', groupData);
|
||||
const parentNodeId = itemId + PARENT_ID;
|
||||
g.setNode(parentNodeId, groupData);
|
||||
|
||||
g.setNode(noteData.id, noteData);
|
||||
g.setNode(node.id, nodeData);
|
||||
g.setNode(itemId, nodeData);
|
||||
|
||||
g.setParent(node.id, node.id + '----parent');
|
||||
g.setParent(noteData.id, node.id + '----parent');
|
||||
g.setParent(itemId, parentNodeId);
|
||||
g.setParent(noteData.id, parentNodeId);
|
||||
|
||||
let from = node.id;
|
||||
let from = itemId;
|
||||
let to = noteData.id;
|
||||
|
||||
if (node.note.position === 'left of') {
|
||||
if (parsedItem.note.position === 'left of') {
|
||||
from = noteData.id;
|
||||
to = node.id;
|
||||
to = itemId;
|
||||
}
|
||||
g.setEdge(from, to, {
|
||||
arrowhead: 'none',
|
||||
arrowType: '',
|
||||
style: 'fill:none',
|
||||
style: G_EDGE_STYLE,
|
||||
labelStyle: '',
|
||||
classes: 'transition note-edge',
|
||||
arrowheadStyle: 'fill: #333',
|
||||
labelpos: 'c',
|
||||
labelType: 'text',
|
||||
thickness: 'normal',
|
||||
classes: CSS_EDGE_NOTE_EDGE,
|
||||
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
||||
labelpos: G_EDGE_LABELPOS,
|
||||
labelType: G_EDGE_LABELTYPE,
|
||||
thickness: G_EDGE_THICKNESS,
|
||||
});
|
||||
} else {
|
||||
g.setNode(node.id, nodeData);
|
||||
g.setNode(itemId, nodeData);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
if (parent.id !== 'root') {
|
||||
log.trace('Setting node ', node.id, ' to be child of its parent ', parent.id);
|
||||
g.setParent(node.id, parent.id);
|
||||
log.trace('Setting node ', itemId, ' to be child of its parent ', parent.id);
|
||||
g.setParent(itemId, parent.id);
|
||||
}
|
||||
}
|
||||
if (node.doc) {
|
||||
if (parsedItem.doc) {
|
||||
log.trace('Adding nodes children ');
|
||||
setupDoc(g, node, node.doc, !altFlag);
|
||||
setupDoc(g, parsedItem, parsedItem.doc, diagramDb, !altFlag);
|
||||
}
|
||||
};
|
||||
let cnt = 0;
|
||||
const setupDoc = (g, parent, doc, altFlag) => {
|
||||
// cnt = 0;
|
||||
|
||||
/**
|
||||
* Turn parsed statements (item.stmt) into nodes, relationships, etc. for a document.
|
||||
* (A document may be nested within others.)
|
||||
*
|
||||
* @param g
|
||||
* @param parentParsedItem - parsed Item that is the parent of this document (doc)
|
||||
* @param doc - the document to set up
|
||||
* @param diagramDb
|
||||
* @param altFlag
|
||||
* @todo This duplicates some of what is done in stateDb.js extract method
|
||||
*/
|
||||
const setupDoc = (g, parentParsedItem, doc, diagramDb, altFlag) => {
|
||||
// graphItemCount = 0;
|
||||
log.trace('items', doc);
|
||||
doc.forEach((item) => {
|
||||
if (item.stmt === 'state' || item.stmt === 'default') {
|
||||
setupNode(g, parent, item, altFlag);
|
||||
} else if (item.stmt === 'relation') {
|
||||
setupNode(g, parent, item.state1, altFlag);
|
||||
setupNode(g, parent, item.state2, altFlag);
|
||||
const edgeData = {
|
||||
id: 'edge' + cnt,
|
||||
arrowhead: 'normal',
|
||||
arrowTypeEnd: 'arrow_barb',
|
||||
style: 'fill:none',
|
||||
labelStyle: '',
|
||||
label: common.sanitizeText(item.description, getConfig()),
|
||||
arrowheadStyle: 'fill: #333',
|
||||
labelpos: 'c',
|
||||
labelType: 'text',
|
||||
thickness: 'normal',
|
||||
classes: 'transition',
|
||||
};
|
||||
let startId = item.state1.id;
|
||||
let endId = item.state2.id;
|
||||
|
||||
g.setEdge(startId, endId, edgeData, cnt);
|
||||
cnt++;
|
||||
switch (item.stmt) {
|
||||
case STMT_STATE:
|
||||
setupNode(g, parentParsedItem, item, diagramDb, altFlag);
|
||||
break;
|
||||
case DEFAULT_STATE_TYPE:
|
||||
setupNode(g, parentParsedItem, item, diagramDb, altFlag);
|
||||
break;
|
||||
case STMT_RELATION:
|
||||
{
|
||||
setupNode(g, parentParsedItem, item.state1, diagramDb, altFlag);
|
||||
setupNode(g, parentParsedItem, item.state2, diagramDb, altFlag);
|
||||
const edgeData = {
|
||||
id: 'edge' + graphItemCount,
|
||||
arrowhead: 'normal',
|
||||
arrowTypeEnd: 'arrow_barb',
|
||||
style: G_EDGE_STYLE,
|
||||
labelStyle: '',
|
||||
label: common.sanitizeText(item.description, getConfig()),
|
||||
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
||||
labelpos: G_EDGE_LABELPOS,
|
||||
labelType: G_EDGE_LABELTYPE,
|
||||
thickness: G_EDGE_THICKNESS,
|
||||
classes: CSS_EDGE,
|
||||
};
|
||||
g.setEdge(item.state1.id, item.state2.id, edgeData, graphItemCount);
|
||||
graphItemCount++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
const getDir = (nodes, defaultDir) => {
|
||||
let dir = defaultDir || 'TB';
|
||||
if (nodes.doc) {
|
||||
for (let i = 0; i < nodes.doc.length; i++) {
|
||||
const node = nodes.doc[i];
|
||||
if (node.stmt === 'dir') {
|
||||
dir = node.value;
|
||||
|
||||
/**
|
||||
* Get the direction from the statement items.
|
||||
* Look through all of the documents (docs) in the parsedItems
|
||||
* Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction.
|
||||
* @param {object[]} parsedItem - the parsed statement item to look through
|
||||
* @param [defaultDir=DEFAULT_NESTED_DOC_DIR] - the direction to use if none is found
|
||||
* @returns {string}
|
||||
*/
|
||||
const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
|
||||
let dir = defaultDir;
|
||||
if (parsedItem.doc) {
|
||||
for (let i = 0; i < parsedItem.doc.length; i++) {
|
||||
const parsedItemDoc = parsedItem.doc[i];
|
||||
if (parsedItemDoc.stmt === 'dir') {
|
||||
dir = parsedItemDoc.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||
* Draws a state diagram in the tag with id: id based on the graph definition in text.
|
||||
*
|
||||
* @param {any} text
|
||||
* @param {any} id
|
||||
@ -245,7 +394,7 @@ export const draw = function (text, id, _version, diag) {
|
||||
// Fetch the default direction, use TD if none was found
|
||||
let dir = diag.db.getDirection();
|
||||
if (typeof dir === 'undefined') {
|
||||
dir = 'LR';
|
||||
dir = DEFAULT_DIAGRAM_DIRECTION;
|
||||
}
|
||||
|
||||
const { securityLevel, state: conf } = getConfig();
|
||||
@ -253,9 +402,14 @@ export const draw = function (text, id, _version, diag) {
|
||||
const rankSpacing = conf.rankSpacing || 50;
|
||||
|
||||
log.info(diag.db.getRootDocV2());
|
||||
|
||||
// This parses the diagram text and sets the classes, relations, styles, classDefs, etc.
|
||||
diag.db.extract(diag.db.getRootDocV2());
|
||||
log.info(diag.db.getRootDocV2());
|
||||
|
||||
diagramStates = diag.db.getStates();
|
||||
diagramClasses = diag.db.getClasses();
|
||||
|
||||
// Create the input mermaid.graph
|
||||
const g = new graphlib.Graph({
|
||||
multigraph: true,
|
||||
@ -272,7 +426,7 @@ export const draw = function (text, id, _version, diag) {
|
||||
return {};
|
||||
});
|
||||
|
||||
setupNode(g, undefined, diag.db.getRootDocV2(), true);
|
||||
setupNode(g, undefined, diag.db.getRootDocV2(), diag.db, true);
|
||||
|
||||
// Set up an SVG group so that we can translate the final graph.
|
||||
let sandboxElement;
|
||||
@ -288,7 +442,7 @@ export const draw = function (text, id, _version, diag) {
|
||||
// Run the renderer. This is what draws the final graph.
|
||||
|
||||
const element = root.select('#' + id + ' g');
|
||||
render(element, g, ['barb'], 'statediagram', id);
|
||||
render(element, g, ['barb'], CSS_DIAGRAM, id);
|
||||
|
||||
const padding = 8;
|
||||
|
||||
@ -298,7 +452,7 @@ export const draw = function (text, id, _version, diag) {
|
||||
const height = bounds.height + padding * 2;
|
||||
|
||||
// Zoom in a bit
|
||||
svg.attr('class', 'statediagram');
|
||||
svg.attr('class', CSS_DIAGRAM);
|
||||
|
||||
const svgBounds = svg.node().getBBox();
|
||||
|
||||
@ -318,7 +472,7 @@ export const draw = function (text, id, _version, diag) {
|
||||
// Get dimensions of label
|
||||
const dim = label.getBBox();
|
||||
|
||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', SHAPE_STATE);
|
||||
rect.setAttribute('rx', 0);
|
||||
rect.setAttribute('ry', 0);
|
||||
rect.setAttribute('width', dim.width);
|
||||
|
@ -35,7 +35,7 @@ import { exec } from 'child_process';
|
||||
import { globby } from 'globby';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import type { Code, Root } from 'mdast';
|
||||
import { posix, dirname } from 'path';
|
||||
import { posix, dirname, relative } from 'path';
|
||||
import prettier from 'prettier';
|
||||
import { remark } from 'remark';
|
||||
// @ts-ignore No typescript declaration file
|
||||
@ -50,8 +50,6 @@ const MERMAID_MAJOR_VERSION = (
|
||||
const SOURCE_DOCS_DIR = 'packages/mermaid/src/docs';
|
||||
const FINAL_DOCS_DIR = 'docs';
|
||||
|
||||
const AUTOGENERATED_TEXT = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the corresponding file in ${SOURCE_DOCS_DIR}.`;
|
||||
|
||||
const LOGMSG_TRANSFORMED = 'transformed';
|
||||
const LOGMSG_TO_BE_TRANSFORMED = 'to be transformed';
|
||||
const LOGMSG_COPIED = `, and copied to ${FINAL_DOCS_DIR}`;
|
||||
@ -72,6 +70,15 @@ const prettierConfig: prettier.Config = {
|
||||
|
||||
let filesWereTransformed = false;
|
||||
|
||||
const generateHeader = (file: string): string => {
|
||||
// path from file in docs/* to repo root, e.g ../ or ../../ */
|
||||
const rootDirectory = relative(file, SOURCE_DOCS_DIR);
|
||||
return `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. Please edit the source file in [${file}](${posix.join(
|
||||
rootDirectory,
|
||||
file
|
||||
)}).`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a source file name and path, return the documentation destination full path and file name
|
||||
* Create the destination path if it does not already exist.
|
||||
@ -164,7 +171,7 @@ const transformMarkdown = (file: string) => {
|
||||
});
|
||||
|
||||
// Add the AUTOGENERATED_TEXT to the start of the file
|
||||
const transformed = `${AUTOGENERATED_TEXT}\n${remark.stringify(out)}`;
|
||||
const transformed = `${generateHeader(file)}\n${remark.stringify(out)}`;
|
||||
const formatted = prettier.format(transformed, {
|
||||
parser: 'markdown',
|
||||
...prettierConfig,
|
||||
@ -195,7 +202,7 @@ const transformHtml = (filename: string) => {
|
||||
);
|
||||
const jsdom = new JSDOM(fileContents);
|
||||
const htmlDoc = jsdom.window.document;
|
||||
const autoGeneratedComment = jsdom.window.document.createComment(AUTOGENERATED_TEXT);
|
||||
const autoGeneratedComment = jsdom.window.document.createComment(generateHeader(fileName));
|
||||
|
||||
const rootElement = htmlDoc.documentElement;
|
||||
rootElement.prepend(autoGeneratedComment);
|
||||
|
@ -174,6 +174,22 @@ The following formatting strings are supported:
|
||||
|
||||
More info in: https://github.com/mbostock/d3/wiki/Time-Formatting
|
||||
|
||||
### Axis ticks
|
||||
|
||||
The default output ticks are auto. You can custom your `tickInterval`, like `1day` or `1week`.
|
||||
|
||||
```
|
||||
tickInterval 1day
|
||||
```
|
||||
|
||||
The pattern is:
|
||||
|
||||
```
|
||||
/^([1-9][0-9]*)(minute|hour|day|week|month)$/
|
||||
```
|
||||
|
||||
More info in: [https://github.com/d3/d3-time#interval_every](https://github.com/d3/d3-time#interval_every)
|
||||
|
||||
## Comments
|
||||
|
||||
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax
|
||||
|
@ -114,7 +114,7 @@ In this example, we have given custom tags to the commits. Also, see how we have
|
||||
|
||||
### Create a new branch
|
||||
|
||||
In Mermaid, in-order to create a new branch, you make use of the `branch` keyword. You also need to provide a name of the new branch. The name has to be unique and cannot be that of an existing branch. Usage example: `branch develop`
|
||||
In Mermaid, in-order to create a new branch, you make use of the `branch` keyword. You also need to provide a name of the new branch. The name has to be unique and cannot be that of an existing branch. A branch name that could be confused for a keyword must be quoted within `""`. Usage examples: `branch develop`, `branch "cherry-pick"`
|
||||
|
||||
When Mermaid, reads the `branch` keyword, it creates a new branch and sets it as the current branch. Equivalent to you creating a new branch and checking it out in Git world.
|
||||
|
||||
|
@ -394,41 +394,6 @@ describe('mermaidAPI', function () {
|
||||
themeVariables: { fontFamily: 'serif' },
|
||||
};
|
||||
|
||||
//
|
||||
// export interface MermaidConfig {
|
||||
// lazyLoadedDiagrams?: string[];
|
||||
// theme?: string;
|
||||
// themeVariables?: any;
|
||||
// themeCSS?: string;
|
||||
// maxTextSize?: number;
|
||||
// darkMode?: boolean;
|
||||
// htmlLabels?: boolean;
|
||||
// fontFamily?: string;
|
||||
// altFontFamily?: string;
|
||||
// logLevel?: number;
|
||||
// securityLevel?: string;
|
||||
// startOnLoad?: boolean;
|
||||
// arrowMarkerAbsolute?: boolean;
|
||||
// secure?: string[];
|
||||
// deterministicIds?: boolean;
|
||||
// deterministicIDSeed?: string;
|
||||
// flowchart?: FlowchartDiagramConfig;
|
||||
// sequence?: SequenceDiagramConfig;
|
||||
// gantt?: GanttDiagramConfig;
|
||||
// journey?: JourneyDiagramConfig;
|
||||
// class?: ClassDiagramConfig;
|
||||
// state?: StateDiagramConfig;
|
||||
// er?: ErDiagramConfig;
|
||||
// pie?: PieDiagramConfig;
|
||||
// requirement?: RequirementDiagramConfig;
|
||||
// mindmap?: MindmapDiagramConfig;
|
||||
// gitGraph?: GitGraphDiagramConfig;
|
||||
// c4?: C4DiagramConfig;
|
||||
// dompurifyConfig?: DOMPurify.Config;
|
||||
// wrap?: boolean;
|
||||
// fontSize?: number;
|
||||
// }
|
||||
|
||||
const classDef1 = { id: 'classDef1', styles: ['style1-1'], textStyles: [] };
|
||||
|
||||
it('gets the css styles created', () => {
|
||||
|
@ -9,7 +9,6 @@
|
||||
* page or do something completely different.
|
||||
*
|
||||
* In addition to the render function, a number of behavioral configuration options are available.
|
||||
*
|
||||
*/
|
||||
import { select } from 'd3';
|
||||
import { compile, serialize, stringify } from 'stylis';
|
||||
@ -19,7 +18,6 @@ import * as configApi from './config';
|
||||
import { addDiagrams } from './diagram-api/diagram-orchestration';
|
||||
import classDb from './diagrams/class/classDb';
|
||||
import flowDb from './diagrams/flowchart/flowDb';
|
||||
import flowRenderer from './diagrams/flowchart/flowRenderer';
|
||||
import ganttDb from './diagrams/gantt/ganttDb';
|
||||
import Diagram, { getDiagramFromText, type ParseErrorFunction } from './Diagram';
|
||||
import errorRenderer from './diagrams/error/errorRenderer';
|
||||
@ -32,6 +30,9 @@ import DOMPurify from 'dompurify';
|
||||
import { MermaidConfig } from './config.type';
|
||||
import { evaluate } from './diagrams/common/common';
|
||||
|
||||
// diagram names that support classDef statements
|
||||
const CLASSDEF_DIAGRAMS = ['graph', 'flowchart', 'flowchart-v2', 'stateDiagram'];
|
||||
|
||||
const MAX_TEXTLENGTH_EXCEEDED_MSG =
|
||||
'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
|
||||
|
||||
@ -165,7 +166,7 @@ export const createCssStyles = (
|
||||
|
||||
// user provided theme CSS info
|
||||
// If you add more configuration driven data into the user styles make sure that the value is
|
||||
// sanitized by the santizeCSS function @todo TODO where is this method? what should be used to replace it? refactor so that it's always sanitized
|
||||
// sanitized by the sanitize CSS function TODO where is this method? what should be used to replace it? refactor so that it's always sanitized
|
||||
if (config.themeCSS !== undefined) {
|
||||
cssStyles += `\n${config.themeCSS}`;
|
||||
}
|
||||
@ -182,8 +183,8 @@ export const createCssStyles = (
|
||||
if (graphType === 'flowchart' || graphType === 'flowchart-v2' || graphType === 'graph') {
|
||||
const htmlLabels = config.htmlLabels || config.flowchart?.htmlLabels;
|
||||
|
||||
const cssHtmlElements = ['> *', 'span']; // @todo TODO make a constant
|
||||
const cssShapeElements = ['rect', 'polygon', 'ellipse', 'circle']; // @todo TODO make a constant
|
||||
const cssHtmlElements = ['> *', 'span']; // TODO make a constant
|
||||
const cssShapeElements = ['rect', 'polygon', 'ellipse', 'circle']; // TODO make a constant
|
||||
|
||||
const cssElements = htmlLabels ? cssHtmlElements : cssShapeElements;
|
||||
|
||||
@ -482,8 +483,8 @@ const render = async function (
|
||||
const rules = createUserStyles(
|
||||
config,
|
||||
graphType,
|
||||
// @ts-ignore convert flowRender to TS.
|
||||
flowRenderer.getClasses(text, diag),
|
||||
// @ts-ignore convert renderer to TS.
|
||||
diag.renderer.getClasses(text, diag),
|
||||
idSelector
|
||||
);
|
||||
|
||||
@ -496,7 +497,7 @@ const render = async function (
|
||||
try {
|
||||
await diag.renderer.draw(text, id, pkg.version, diag);
|
||||
} catch (e) {
|
||||
errorRenderer.draw(text, id, pkg.version);
|
||||
await errorRenderer.draw(text, id, pkg.version);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -542,7 +543,6 @@ const render = async function (
|
||||
} else {
|
||||
log.debug('CB = undefined!');
|
||||
}
|
||||
|
||||
attachFunctions();
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
|
1480
pnpm-lock.yaml
generated
1480
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user