Merge branch 'release/8.8.2'
This commit is contained in:
commit
acc4f31c3a
|
@ -3,7 +3,7 @@
|
|||
![banner](./img/header.png)
|
||||
**Edit this Page** [![N|Solid](./docs/assets/img/GitHub-Mark-32px.png)](https://github.com/mermaid-js/mermaid/blob/develop/docs/README.md)
|
||||
|
||||
:trophy: **Mermaid was nominated and won the [JS Open Source Awards (2019)](https://osawards.com/javascript/#nominees) in the category "The most exciting use of technology"!!!**
|
||||
:trophy: **Mermaid was nominated and won the [JS Open Source Awards (2019)](https://osawards.com/javascript/2019) in the category "The most exciting use of technology"!!!**
|
||||
|
||||
**Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintain the project 🙏**
|
||||
|
||||
|
@ -23,7 +23,7 @@ Even non-programmers can create diagrams through the [Mermaid Live Editor](https
|
|||
[Tutorials](./docs/getting-started/Tutorials.md) has video tutorials.
|
||||
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/overview/integrations.md).
|
||||
|
||||
For a more detailed introduction to Mermaid and some of it's more basic uses, look to the [Beginner's Guide](./docs/overview/n00b-overview.md) and [Usage](./docs/getting-started/usage.md).
|
||||
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/overview/n00b-overview.md) and [Usage](./docs/getting-started/usage.md).
|
||||
|
||||
🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) | 📜 [Changelog](./docs/tutorials-and-community/CHANGELOG.md)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
/* global cy */
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
export const mermaidUrl = (graphStr, options, api) => {
|
||||
|
|
|
@ -1,44 +1,89 @@
|
|||
/* eslint-env jest */
|
||||
describe('Interaction', () => {
|
||||
describe('Interaction - security level loose', () => {
|
||||
it('should handle a click on a node with a bound function', () => {
|
||||
it('Graph: should handle a click on a node with a bound function', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g#Function')
|
||||
.find('g#flowchart-Function-2')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
|
||||
});
|
||||
it('should handle a click on a node with a bound function where the node starts with a number', () => {
|
||||
it('Graph: should handle a click on a node with a bound function where the node starts with a number', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g[id="1Function"]')
|
||||
.find('g[id="flowchart-1Function-6"]')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
|
||||
});
|
||||
it('should handle a click on a node with a bound url', () => {
|
||||
it('Graph: should handle a click on a node with a bound url', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g#URL')
|
||||
.find('#flowchart-URL-3')
|
||||
.click();
|
||||
|
||||
cy.location().should(location => {
|
||||
expect(location.href).to.eq('http://localhost:9000/webpackUsage.html');
|
||||
});
|
||||
});
|
||||
it('should handle a click on a node with a bound url where the node starts with a number', () => {
|
||||
it('Graph: should handle a click on a node with a bound url where the node starts with a number', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g[id="2URL"]')
|
||||
.find('g[id="flowchart-2URL-7"]')
|
||||
.click();
|
||||
|
||||
cy.location().should(location => {
|
||||
expect(location.href).to.eq('http://localhost:9000/webpackUsage.html');
|
||||
});
|
||||
});
|
||||
|
||||
it('Flowchart-v2: should handle a click on a node with a bound function', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g#flowchart-Function-10')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
|
||||
});
|
||||
it('Flowchart-v2: should handle a click on a node with a bound function where the node starts with a number', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g[id="flowchart-1Function-14"]')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
|
||||
});
|
||||
it('Flowchart-v2: should handle a click on a node with a bound url', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('#flowchart-URL-11')
|
||||
.click();
|
||||
|
||||
cy.location().should(location => {
|
||||
expect(location.href).to.eq('http://localhost:9000/webpackUsage.html');
|
||||
});
|
||||
});
|
||||
it('Flowchart-v2: should handle a click on a node with a bound url where the node starts with a number', () => {
|
||||
const url = 'http://localhost:9000/click_security_loose.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g[id="flowchart-2URL-15"]')
|
||||
.click();
|
||||
|
||||
cy.location().should(location => {
|
||||
|
@ -120,7 +165,7 @@ describe('Interaction', () => {
|
|||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g#Function')
|
||||
.find('g#flowchart-Function-2')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
|
||||
|
@ -130,7 +175,7 @@ describe('Interaction', () => {
|
|||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g[id="1Function"]')
|
||||
.find('g[id="flowchart-1Function-6"]')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
|
||||
|
@ -140,7 +185,7 @@ describe('Interaction', () => {
|
|||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g#URL')
|
||||
.find('g#flowchart-URL-3')
|
||||
.click();
|
||||
|
||||
cy.location().should(location => {
|
||||
|
@ -152,7 +197,7 @@ describe('Interaction', () => {
|
|||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g[id="2URL"]')
|
||||
.find('g[id="flowchart-2URL-7"]')
|
||||
.click();
|
||||
|
||||
cy.location().should(location => {
|
||||
|
@ -208,31 +253,31 @@ describe('Interaction', () => {
|
|||
|
||||
describe('Interaction - security level other, missspelling', () => {
|
||||
it('should handle a click on a node with a bound function', () => {
|
||||
const url = 'http://localhost:9000/click_security_strict.html';
|
||||
const url = 'http://localhost:9000/click_security_other.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g#Function')
|
||||
.find('g#flowchart-Function-2')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
|
||||
});
|
||||
it('should handle a click on a node with a bound function where the node starts with a number', () => {
|
||||
const url = 'http://localhost:9000/click_security_strict.html';
|
||||
const url = 'http://localhost:9000/click_security_other.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g[id="1Function"]')
|
||||
.find('g[id="flowchart-1Function-6"]')
|
||||
.click();
|
||||
|
||||
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
|
||||
});
|
||||
it('should handle a click on a node with a bound url', () => {
|
||||
const url = 'http://localhost:9000/click_security_strict.html';
|
||||
const url = 'http://localhost:9000/click_security_other.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
.find('g#URL')
|
||||
.find('g#flowchart-URL-3')
|
||||
.click();
|
||||
|
||||
cy.location().should(location => {
|
||||
|
@ -241,7 +286,7 @@ describe('Interaction', () => {
|
|||
});
|
||||
|
||||
it('should handle a click on a task with a bound function', () => {
|
||||
const url = 'http://localhost:9000/click_security_strict.html';
|
||||
const url = 'http://localhost:9000/click_security_other.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
|
@ -251,7 +296,7 @@ describe('Interaction', () => {
|
|||
cy.get('.created-by-gant-click').should('not.have.text', 'Clicked By Gant cl2');
|
||||
});
|
||||
it('should handle a click on a task with a bound function', () => {
|
||||
const url = 'http://localhost:9000/click_security_strict.html';
|
||||
const url = 'http://localhost:9000/click_security_other.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('body')
|
||||
|
|
|
@ -11,12 +11,12 @@ describe('Rerendering', () => {
|
|||
const url = 'http://localhost:9000/rerender.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('#graph #A').should('have.text', 'XMas');
|
||||
cy.get('#graph [id^=flowchart-A]').should('have.text', 'XMas');
|
||||
|
||||
cy.get('body')
|
||||
.find('#rerender')
|
||||
.click({ force: true });
|
||||
|
||||
cy.get('#graph #A').should('have.text', 'Saturday');
|
||||
cy.get('#graph [id^=flowchart-A]').should('have.text', 'Saturday');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
describe('Class diagram V2', () => {
|
||||
|
||||
it('0: should render a simple class diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
|
||||
classA -- classB : Inheritance
|
||||
classA -- classC : link
|
||||
classC -- classD : link
|
||||
classB -- classD
|
||||
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('1: should render a simple class diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 *-- Class04
|
||||
Class05 o-- Class06
|
||||
Class07 .. Class08
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
Class12 <|.. Class08
|
||||
Class11 ..>Class12
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class01 : -int privateChimp
|
||||
Class01 : +int publicGorilla
|
||||
Class01 : #int protectedMarmoset
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('2: should render a simple class diagrams with cardinality', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01 "1" <|--|> "*" AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 "1" <--> "*" C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render a simple class diagram with different visibilities', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class01 : -privateMethod()
|
||||
Class01 : +publicMethod()
|
||||
Class01 : #protectedMethod()
|
||||
Class01 : -int privateChimp
|
||||
Class01 : +int publicGorilla
|
||||
Class01 : #int protectedMarmoset
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render multiple class diagrams', () => {
|
||||
imgSnapshotTest(
|
||||
[
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01 "1" <|--|> "*" AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 "1" <--> "*" C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01 "1" <|--|> "*" AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 "1" <--> "*" C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
],
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('4: should render a simple class diagram with comments', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
%% this is a comment
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 *-- Class04
|
||||
Class05 o-- Class06
|
||||
Class07 .. Class08
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('5: should render a simple class diagram with abstract method', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class01 : someMethod()*
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('6: should render a simple class diagram with static method', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class01 : someMethod()$
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('7: should render a simple class diagram with Generic class', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class01~T~
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10~T~ {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('8: should render a simple class diagram with Generic class and relations', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01~T~ <|-- AveryLongClass : Cool
|
||||
Class03~T~ *-- Class04~T~
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10~T~ {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('9: should render a simple class diagram with clickable link', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01~T~ <|-- AveryLongClass : Cool
|
||||
Class03~T~ *-- Class04~T~
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10~T~ {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
link Class01 "google.com" "A Tooltip"
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('10: should render a simple class diagram with clickable callback', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
Class01~T~ <|-- AveryLongClass : Cool
|
||||
Class03~T~ *-- Class04~T~
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10~T~ {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
callback Class01 "functionCall" "A Tooltip"
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('11: should render a simple class diagram with return type on method', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class10~T~ {
|
||||
int[] id
|
||||
test(int[] ids) bool
|
||||
testArray() bool[]
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('12: should render a simple class diagram with generic types', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class10~T~ {
|
||||
int[] id
|
||||
List~int~ ids
|
||||
test(List~int~ ids) List~bool~
|
||||
testArray() bool[]
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('13: should render a simple class diagram with css classes applied', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class10 {
|
||||
int[] id
|
||||
List~int~ ids
|
||||
test(List~int~ ids) List~bool~
|
||||
testArray() bool[]
|
||||
}
|
||||
|
||||
cssClass "Class10" exClass2
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('14: should render a simple class diagram with css classes applied directly', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class10:::exClass2 {
|
||||
int[] id
|
||||
List~int~ ids
|
||||
test(List~int~ ids) List~bool~
|
||||
testArray() bool[]
|
||||
}
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('15: should render a simple class diagram with css classes applied two multiple classes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class10
|
||||
class Class20
|
||||
|
||||
cssClass "Class10, class20" exClass2
|
||||
`,
|
||||
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
|
||||
|
||||
describe('Class diagram', () => {
|
||||
it('1: should render a simple class diagram', () => {
|
||||
|
@ -65,7 +65,7 @@ describe('Class diagram', () => {
|
|||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render a simple class diagram with different visibilities', () => {
|
||||
it('3: should render a simple class diagram with different visibilities', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
|
@ -83,59 +83,6 @@ describe('Class diagram', () => {
|
|||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render multiple class diagrams', () => {
|
||||
imgSnapshotTest(
|
||||
[
|
||||
`
|
||||
classDiagram
|
||||
Class01 "1" <|--|> "*" AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 "1" <--> "*" C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
`
|
||||
classDiagram
|
||||
Class01 "1" <|--|> "*" AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 "1" <--> "*" C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
],
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('4: should render a simple class diagram with comments', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@ -246,7 +193,7 @@ describe('Class diagram', () => {
|
|||
int id
|
||||
test()
|
||||
}
|
||||
link class01 "google.com" "A Tooltip"
|
||||
link Class01 "google.com" "A Tooltip"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
|
@ -268,7 +215,7 @@ describe('Class diagram', () => {
|
|||
int id
|
||||
test()
|
||||
}
|
||||
callback class01 "functionCall" "A Tooltip"
|
||||
callback Class01 "functionCall" "A Tooltip"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
|
@ -353,4 +300,107 @@ describe('Class diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('16: should render multiple class diagrams', () => {
|
||||
imgSnapshotTest(
|
||||
[
|
||||
`
|
||||
classDiagram
|
||||
Class01 "1" <|--|> "*" AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 "1" <--> "*" C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
`
|
||||
classDiagram
|
||||
Class01 "1" <|--|> "*" AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 "1" <--> "*" C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
`,
|
||||
],
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('17: should render a class diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
classDiagram
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class01 : -int privateChimp
|
||||
Class01 : +int publicGorilla
|
||||
Class01 : #int protectedMarmoset
|
||||
`,
|
||||
{ class: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height', '218');
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseInt(style.match(/[\d.]+/g).join(''));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(maxWidthValue).to.be.within(160 * .95, 160 * 1.05);
|
||||
});
|
||||
});
|
||||
|
||||
it('18: should render a class diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
classDiagram
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class01 : -int privateChimp
|
||||
Class01 : +int publicGorilla
|
||||
Class01 : #int protectedMarmoset
|
||||
`,
|
||||
{ class: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(width).to.be.within(160 * .95, 160 * 1.05);
|
||||
expect(svg).to.have.attr('height', '218');
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
|
||||
|
||||
describe('Entity Relationship Diagram', () => {
|
||||
it('should render a simple ER diagram', () => {
|
||||
|
@ -101,4 +101,44 @@ describe('Entity Relationship Diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render an ER diagrams when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE-ITEM : contains
|
||||
`,
|
||||
{ er: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height', '465');
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(maxWidthValue).to.be.within(140 * .95, 140 * 1.05);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render an ER when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE-ITEM : contains
|
||||
`,
|
||||
{ er: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(width).to.be.within(140 * .95, 140 * 1.05);
|
||||
expect(svg).to.have.attr('height', '465');
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
|
||||
|
||||
describe('Flowchart v2', () => {
|
||||
it('1: should render a simple flowchart', () => {
|
||||
|
@ -47,7 +47,7 @@ describe('Flowchart v2', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('Length of edges', () => {
|
||||
it('4: Length of edges', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
L1 --- L2
|
||||
|
@ -64,7 +64,7 @@ describe('Flowchart v2', () => {
|
|||
{ flowchart: { diagramPadding: 0 } }
|
||||
);
|
||||
});
|
||||
it('36: should render escaped without html labels', () => {
|
||||
it('5: should render escaped without html labels', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
a["<strong>Haiya</strong>"]---->b
|
||||
|
@ -72,13 +72,257 @@ describe('Flowchart v2', () => {
|
|||
{htmlLabels: false, flowchart: {htmlLabels: false}}
|
||||
);
|
||||
});
|
||||
it('37: should render non-escaped with html labels', () => {
|
||||
it('6: should render non-escaped with html labels', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
a["<strong>Haiya</strong>"]===>b
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
|
||||
);
|
||||
});
|
||||
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{ flowchart: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.be.within(446 * .95, 446 * 1.05);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.be.within(300 * .95-1, 300 * 1.05);
|
||||
});
|
||||
});
|
||||
it('8: should render a flowchart when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{ flowchart: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(height).to.be.within(446 * .95, 446 * 1.05);
|
||||
expect(width).to.be.within(300 * .95-1, 300 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
|
||||
it('V2 - 16: Render Stadium shape', () => {
|
||||
imgSnapshotTest(
|
||||
` flowchart TD
|
||||
A([stadium shape test])
|
||||
A -->|Get money| B([Go shopping])
|
||||
B --> C([Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?])
|
||||
C -->|One| D([Laptop])
|
||||
C -->|Two| E([iPhone])
|
||||
C -->|Three| F([Car<br/>wroom wroom])
|
||||
click A "index.html#link-clicked" "link test"
|
||||
click B testClick "click test"
|
||||
classDef someclass fill:#f96;
|
||||
class A someclass;
|
||||
class C someclass;
|
||||
`,
|
||||
{ flowchart: { htmlLabels: false } , fontFamily: 'courier'}
|
||||
);
|
||||
});
|
||||
|
||||
it('50: handle nested subgraphs in reverse order', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart LR
|
||||
a -->b
|
||||
subgraph A
|
||||
B
|
||||
end
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
it('51: handle nested subgraphs in reverse order', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart LR
|
||||
a -->b
|
||||
subgraph A
|
||||
B
|
||||
end
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
it('52: handle nested subgraphs in several levels', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
b-->B
|
||||
a-->c
|
||||
subgraph O
|
||||
A
|
||||
end
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
subgraph A
|
||||
a
|
||||
b
|
||||
B
|
||||
end
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
it('53: handle nested subgraphs with edges in and out', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
internet
|
||||
nat
|
||||
routeur
|
||||
lb1
|
||||
lb2
|
||||
compute1
|
||||
compute2
|
||||
subgraph project
|
||||
routeur
|
||||
nat
|
||||
subgraph subnet1
|
||||
compute1
|
||||
lb1
|
||||
end
|
||||
subgraph subnet2
|
||||
compute2
|
||||
lb2
|
||||
end
|
||||
end
|
||||
internet --> routeur
|
||||
routeur --> subnet1 & subnet2
|
||||
subnet1 & subnet2 --> nat --> internet
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
it('54: handle nested subgraphs with outgoing links', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
subgraph main
|
||||
subgraph subcontainer
|
||||
subcontainer-child
|
||||
end
|
||||
subcontainer-child--> subcontainer-sibling
|
||||
end
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
it('55: handle nested subgraphs with outgoing links 2', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
|
||||
subgraph one[One]
|
||||
subgraph sub_one[Sub One]
|
||||
_sub_one
|
||||
end
|
||||
subgraph sub_two[Sub Two]
|
||||
_sub_two
|
||||
end
|
||||
_one
|
||||
end
|
||||
|
||||
%% here, either the first or the second one
|
||||
sub_one --> sub_two
|
||||
_one --> b
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('56: handle nested subgraphs with outgoing links 3', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
subgraph container_Beta
|
||||
process_C-->Process_D
|
||||
end
|
||||
subgraph container_Alpha
|
||||
process_A-->process_B
|
||||
process_A-->|messages|process_C
|
||||
end
|
||||
process_B-->|via_AWSBatch|container_Beta
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
it('57: handle nested subgraphs with outgoing links 4', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart LR
|
||||
subgraph A
|
||||
a -->b
|
||||
end
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('57: handle nested subgraphs with outgoing links 2', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
c1-->a2
|
||||
subgraph one
|
||||
a1-->a2
|
||||
end
|
||||
subgraph two
|
||||
b1-->b2
|
||||
end
|
||||
subgraph three
|
||||
c1-->c2
|
||||
end
|
||||
one --> two
|
||||
three --> two
|
||||
two --> c2
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
it('58: handle styling with style expressions', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
flowchart LR
|
||||
id1(Start)-->id2(Stop)
|
||||
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
|
||||
|
||||
describe('Flowchart', () => {
|
||||
it('1: should render a simple flowchart no htmlLabels', () => {
|
||||
|
@ -731,4 +731,61 @@ describe('Flowchart', () => {
|
|||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
it('38: should render a flowchart when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`graph TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{ flowchart: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.be.within(446 * .95, 446 * 1.05);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.be.within(300 * .95, 300 * 1.05);
|
||||
});
|
||||
});
|
||||
it('39: should render a flowchart when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`graph TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{ flowchart: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(height).to.be.within(446 * .95, 446 * 1.05);
|
||||
expect(width).to.be.within(300 * .95, 300 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
it('58: handle styling with style expressions', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
graph LR
|
||||
id1(Start)-->id2(Stop)
|
||||
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
|
||||
`,
|
||||
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util.js';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
|
||||
|
||||
describe('Gantt diagram', () => {
|
||||
beforeEach(()=>{
|
||||
|
@ -163,4 +163,99 @@ describe('Gantt diagram', () => {
|
|||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %d/%m
|
||||
title Adding GANTT diagram to mermaid
|
||||
excludes weekdays 2014-01-10
|
||||
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
|
||||
section Critical tasks
|
||||
Completed task in the critical line :crit, done, 2014-01-06,24h
|
||||
Implement parser and jison :crit, done, after des1, 2d
|
||||
Create tests for parser :crit, active, 3d
|
||||
Future task in critical line :crit, 5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid :1d
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :active, a1, after des1, 3d
|
||||
Add gantt diagram to demo page :after a1 , 20h
|
||||
Add another diagram to demo page :doc1, after a1 , 48h
|
||||
|
||||
section Last section
|
||||
Describe gantt syntax :after doc1, 3d
|
||||
Add gantt diagram to demo page : 20h
|
||||
Add another diagram to demo page : 48h
|
||||
`,
|
||||
{ gantt: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.be.within(484 * .95, 484 * 1.05);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.be.within(984 * .95, 984 * 1.05);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a gantt diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %d/%m
|
||||
title Adding GANTT diagram to mermaid
|
||||
excludes weekdays 2014-01-10
|
||||
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
|
||||
section Critical tasks
|
||||
Completed task in the critical line :crit, done, 2014-01-06,24h
|
||||
Implement parser and jison :crit, done, after des1, 2d
|
||||
Create tests for parser :crit, active, 3d
|
||||
Future task in critical line :crit, 5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid :1d
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :active, a1, after des1, 3d
|
||||
Add gantt diagram to demo page :after a1 , 20h
|
||||
Add another diagram to demo page :doc1, after a1 , 48h
|
||||
|
||||
section Last section
|
||||
Describe gantt syntax :after doc1, 3d
|
||||
Add gantt diagram to demo page : 20h
|
||||
Add another diagram to demo page : 48h
|
||||
`,
|
||||
{ gantt: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(height).to.be.within(484 * .95, 484 * 1.05);
|
||||
expect(width).to.be.within(984 * .95, 984 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util.js';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
|
||||
|
||||
describe('User journey diagram', () => {
|
||||
it('Simple test', () => {
|
||||
|
@ -28,4 +28,43 @@ section Order from website
|
|||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a user journey diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`journey
|
||||
title Adding journey diagram functionality to mermaid
|
||||
section Order from website
|
||||
`,
|
||||
{ journey: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.eq(20);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.eq(400);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a user journey diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`journey
|
||||
title Adding journey diagram functionality to mermaid
|
||||
section Order from website
|
||||
`,
|
||||
{ journey: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(height).to.eq(20);
|
||||
expect(width).to.eq(400);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util.js';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
|
||||
|
||||
describe('Pie Chart', () => {
|
||||
it('should render a simple pie diagram', () => {
|
||||
|
@ -37,4 +37,45 @@ describe('Pie Chart', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render a pie diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
pie title Sports in Sweden
|
||||
"Bandy" : 40
|
||||
"Ice-Hockey" : 80
|
||||
"Football" : 90
|
||||
`,
|
||||
{ pie: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.eq(450);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.eq(984);
|
||||
});
|
||||
});
|
||||
it('should render a pie diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
pie title Sports in Sweden
|
||||
"Bandy" : 40
|
||||
"Ice-Hockey" : 80
|
||||
"Football" : 90
|
||||
`,
|
||||
{ pie: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(height).to.eq(450);
|
||||
expect(width).to.eq(984);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/// <reference types="Cypress" />
|
||||
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
|
||||
|
||||
context('Sequence diagram', () => {
|
||||
it('should render a simple sequence diagram', () => {
|
||||
|
@ -505,7 +505,7 @@ context('Sequence diagram', () => {
|
|||
});
|
||||
});
|
||||
context('directives', () => {
|
||||
it('should overide config with directive settings', () => {
|
||||
it('should override config with directive settings', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
%%{init: { "config": { "mirrorActors": true }}}%%
|
||||
|
@ -517,7 +517,7 @@ context('Sequence diagram', () => {
|
|||
{ logLevel:0, sequence: { mirrorActors: false, noteFontSize: 18, noteFontFamily: 'Arial' } }
|
||||
);
|
||||
});
|
||||
it('should overide config with directive settings', () => {
|
||||
it('should override config with directive settings', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
%%{init: { "config": { "mirrorActors": false, "wrap": true }}}%%
|
||||
|
@ -530,4 +530,85 @@ context('Sequence diagram', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
context('svg size', () => {
|
||||
it('should render a sequence diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant John as John<br/>Second Line
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
Bob--x Alice: I am good thanks!
|
||||
Bob-x John: I am good thanks!
|
||||
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
|
||||
Bob-->Alice: Checking with John...
|
||||
alt either this
|
||||
Alice->>John: Yes
|
||||
else or this
|
||||
Alice->>John: No
|
||||
else or this will happen
|
||||
Alice->John: Maybe
|
||||
end
|
||||
par this happens in parallel
|
||||
Alice -->> Bob: Parallel message 1
|
||||
and
|
||||
Alice -->> John: Parallel message 2
|
||||
end
|
||||
`,
|
||||
{ sequence: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.eq(920);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(maxWidthValue).to.be.within(820 * .95, 820 * 1.05);
|
||||
});
|
||||
});
|
||||
it('should render a sequence diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant John as John<br/>Second Line
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
Bob--x Alice: I am good thanks!
|
||||
Bob-x John: I am good thanks!
|
||||
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
|
||||
Bob-->Alice: Checking with John...
|
||||
alt either this
|
||||
Alice->>John: Yes
|
||||
else or this
|
||||
Alice->>John: No
|
||||
else or this will happen
|
||||
Alice->John: Maybe
|
||||
end
|
||||
par this happens in parallel
|
||||
Alice -->> Bob: Parallel message 1
|
||||
and
|
||||
Alice -->> John: Parallel message 2
|
||||
end
|
||||
`,
|
||||
{ sequence: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(height).to.eq(920);
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(width).to.be.within(820 * .95, 820 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
|
||||
|
||||
describe('State diagram', () => {
|
||||
it('v2 should render a simple info', () => {
|
||||
|
@ -47,7 +47,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('v2 should render a single state with short descr', () => {
|
||||
it('v2 should render a single state with short descriptions', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
@ -58,7 +58,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('v2 should render a transition descrions with new lines', () => {
|
||||
it('v2 should render a transition descriptions with new lines', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
@ -201,7 +201,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('v2 should render composit states', () => {
|
||||
it('v2 should render composite states', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
@ -220,7 +220,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('v2 should render multiple composit states', () => {
|
||||
it('v2 should render multiple composite states', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
@ -249,7 +249,7 @@ describe('State diagram', () => {
|
|||
{ logLevel: 0, fontFamily: 'courier' }
|
||||
);
|
||||
});
|
||||
it('v2 should render forks in composit states', () => {
|
||||
it('v2 should render forks in composite states', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
@ -329,7 +329,7 @@ describe('State diagram', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
it('v2 Simplest composit state', () => {
|
||||
it('v2 Simplest composite state', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
@ -354,5 +354,47 @@ describe('State diagram', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
it('v2 should render a state diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
||||
[*] --> State1
|
||||
State1 --> [*]
|
||||
`,
|
||||
{ state: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.eq(177);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(maxWidthValue).to.be.within(135 * .95, 135 * 1.05);
|
||||
});
|
||||
});
|
||||
it('v2 should render a state diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
stateDiagram-v2
|
||||
|
||||
[*] --> State1
|
||||
State1 --> [*]
|
||||
`,
|
||||
{ state: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(height).to.eq(177);
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(width).to.be.within(135 * .95, 135 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
|
||||
|
||||
describe('State diagram', () => {
|
||||
it('should render a simple state diagrams', () => {
|
||||
|
@ -37,7 +37,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render a single state with short descr', () => {
|
||||
it('should render a single state with short descriptions', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
|
@ -48,7 +48,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render a transition descrions with new lines', () => {
|
||||
it('should render a transition descriptions with new lines', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
|
@ -191,7 +191,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render composit states', () => {
|
||||
it('should render composite states', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
|
@ -280,7 +280,7 @@ describe('State diagram', () => {
|
|||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render conurrency states', () => {
|
||||
it('should render concurrency states', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
|
@ -319,7 +319,7 @@ describe('State diagram', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
it('Simplest composit state', () => {
|
||||
it('Simplest composite state', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
|
@ -344,5 +344,45 @@ describe('State diagram', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a state diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
stateDiagram
|
||||
[*] --> State1
|
||||
State1 --> [*]
|
||||
`,
|
||||
{ state: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
expect(svg).to.have.attr('height');
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
expect(height).to.eq(139);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(maxWidthValue).to.be.within(112 * .95, 112 * 1.05);
|
||||
});
|
||||
});
|
||||
it('should render a state diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
stateDiagram
|
||||
[*] --> State1
|
||||
State1 --> [*]
|
||||
`,
|
||||
{ state: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg')
|
||||
.should((svg) => {
|
||||
const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(height).to.eq(139);
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
expect(width).to.be.within(112 * .95, 112 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,19 +16,79 @@
|
|||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
.customCss > rect, .customCss{
|
||||
fill:#FF0000 !important;
|
||||
stroke:#FFFF00 !important;
|
||||
stroke-width:4px !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>info below</h1>
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
%%{init: {'theme': 'base', 'fontFamily': 'arial', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
|
||||
classDiagram-v2
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
|
||||
classDiagram
|
||||
class BankAccount{
|
||||
+String owner
|
||||
+BigDecimal balance
|
||||
+deposit(amount) bool
|
||||
+withdrawl(amount) int
|
||||
}
|
||||
cssClass "BankAccount" customCss
|
||||
|
||||
</div>
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
|
||||
classDiagram-v2
|
||||
class BankAccount{
|
||||
+String owner
|
||||
+BigDecimal balance
|
||||
+deposit(amount) bool
|
||||
+withdrawl(amount) int
|
||||
}
|
||||
cssClass "BankAccount" customCss
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
|
||||
classDiagram
|
||||
class BankAccount{
|
||||
+String owner
|
||||
+BigDecimal balance
|
||||
+deposit(amount) bool
|
||||
+withdrawl(amount) int
|
||||
}
|
||||
}
|
||||
Class01~T~ <|-- AveryLongClass : Cool
|
||||
Class03~T~ *-- Class04~T~
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10~T~ {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
callback Class01 "callback" "A Tooltip"
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
flowchart TB
|
||||
a_a(Aftonbladet) --> b_b[gorilla]:::apa --> c_c{chimp}:::apa -->a_a
|
||||
a_a --> c --> d_d --> c_c
|
||||
classDef apa fill:#f9f,stroke:#333,stroke-width:4px;
|
||||
class a_a apa;
|
||||
click a_a "http://www.aftonbladet.se" "apa"
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
classDiagram-v2
|
||||
|
||||
classA -- classB : Inheritance
|
||||
classA -- classC : link
|
||||
classC -- classD : link
|
||||
classB -- classD
|
||||
classA --|> classB : Inheritance
|
||||
classC --* classD : Composition
|
||||
classE --o classF : Aggregation
|
||||
|
@ -42,15 +102,15 @@
|
|||
classA : method1()
|
||||
<<interface>> classB
|
||||
classB : method2() int
|
||||
</div>
|
||||
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
classDiagram-v2
|
||||
Customer "1" --> "*" Ticket
|
||||
Student "1" --> "1..*" Course
|
||||
Galaxy --> "many" Star : Contains
|
||||
<<interface>> Customer
|
||||
|
||||
class Shape
|
||||
callback Shape "callbackFunction" "This is a tooltip for a callback"
|
||||
|
||||
classA -- classB : Inheritance
|
||||
classA -- classC : link
|
||||
classC -- classD : link
|
||||
classB -- classD
|
||||
</div>
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
|
@ -62,7 +122,7 @@
|
|||
// arrowMarkerAbsolute: true,
|
||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||
logLevel: 0,
|
||||
flowchart: { curve: 'linear', "htmlLabels": false },
|
||||
flowchart: { curve: 'linear', "htmlLabels": true },
|
||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorMargin: 50, showSequenceNumbers: true },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
|
|
|
@ -5,8 +5,14 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Mermaid Quick Test Page</title>
|
||||
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
|
||||
<style>
|
||||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="display: flex">
|
||||
<div id="FirstLine" class="mermaid">
|
||||
graph TB
|
||||
Function-->URL
|
||||
|
@ -15,9 +21,38 @@
|
|||
</div>
|
||||
<div id="FirstLine" class="mermaid">
|
||||
graph TB
|
||||
1Function-->2URL
|
||||
1Function--->2URL
|
||||
click 1Function clickByFlow "Add a div"
|
||||
click 2URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
|
||||
</div>
|
||||
<div id="FirstLine" class="mermaid">
|
||||
flowchart TB
|
||||
Function-->URL
|
||||
click Function clickByFlow "Add a div"
|
||||
click URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>" _self
|
||||
</div>
|
||||
<div id="FirstLine" class="mermaid">
|
||||
flowchart TB
|
||||
1Function--->2URL
|
||||
click 1Function clickByFlow "Add a div"
|
||||
click 2URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>" _self
|
||||
</div>
|
||||
|
||||
<div id="FirstLine" class="mermaid">
|
||||
classDiagram
|
||||
class ShapeLink
|
||||
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
|
||||
class ShapeCallback
|
||||
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
|
||||
</div>
|
||||
<div id="FirstLine" class="mermaid">
|
||||
classDiagram-v2
|
||||
class ShapeLink2
|
||||
link ShapeLink2 "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
|
||||
class ShapeCallback2
|
||||
callback ShapeCallback2 "clickByClass" "This is a tooltip for a callback"
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mermaid">
|
||||
|
@ -82,6 +117,14 @@
|
|||
|
||||
document.getElementsByTagName('body')[0].appendChild(div)
|
||||
}
|
||||
function clickByClass() {
|
||||
const div = document.createElement('div')
|
||||
div.className = 'created-by-class-click'
|
||||
div.style = 'padding: 20px; background: purple; color: white;'
|
||||
div.innerText = 'Clicked By Class'
|
||||
|
||||
document.getElementsByTagName('body')[0].appendChild(div)
|
||||
}
|
||||
mermaid.initialize({ startOnLoad: true, securityLevel: 'loose', logLevel: 1 });
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -74,26 +74,93 @@ stateDiagram-v2
|
|||
A --> D: asd123
|
||||
</div>
|
||||
</div>
|
||||
<div class="mermaid" style="width: 50%; height: 20%;">
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
|
||||
classDiagram
|
||||
Animal <|-- Duck
|
||||
<div class="mermaid" style="width: 50%; height: 40%;">
|
||||
%% this does not produce the desired result
|
||||
flowchart TB
|
||||
subgraph container_Beta
|
||||
process_C-->Process_D
|
||||
end
|
||||
subgraph container_Alpha
|
||||
process_A-->process_B
|
||||
process_B-->|via_AWSBatch|container_Beta
|
||||
process_A-->|messages|process_C
|
||||
end
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
<div class="mermaid" style="width: 50%; height: 40%;">
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
|
||||
flowchart TB
|
||||
b-->B
|
||||
a-->c
|
||||
subgraph O
|
||||
A
|
||||
end
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
subgraph A
|
||||
a
|
||||
b
|
||||
B
|
||||
end
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
sequenceDiagram
|
||||
Alice->Bob: Hello Bob, how are you?
|
||||
Note over Alice,Bob: Looks
|
||||
Note over Bob,Alice: Looks back
|
||||
<div class="mermaid" style="width: 50%; height: 50%;">
|
||||
flowchart TB
|
||||
internet
|
||||
nat
|
||||
routeur
|
||||
lb1
|
||||
lb2
|
||||
compute1
|
||||
compute2
|
||||
subgraph project
|
||||
routeur
|
||||
nat
|
||||
subgraph subnet1
|
||||
compute1
|
||||
lb1
|
||||
end
|
||||
subgraph subnet2
|
||||
compute2
|
||||
lb2
|
||||
end
|
||||
end
|
||||
internet --> routeur
|
||||
routeur --> subnet1 & subnet2
|
||||
subnet1 & subnet2 --> nat --> internet
|
||||
</div>
|
||||
<div class="mermaid" style="width: 50%; height: 50%;">
|
||||
flowchart TD
|
||||
|
||||
subgraph one[One]
|
||||
subgraph sub_one[Sub One]
|
||||
_sub_one
|
||||
end
|
||||
end
|
||||
|
||||
subgraph two[Two]
|
||||
_two
|
||||
end
|
||||
|
||||
sub_one --> two
|
||||
</div>
|
||||
<div class="mermaid" style="width: 50%; height: 50%;">
|
||||
flowchart TD
|
||||
|
||||
subgraph one[One]
|
||||
subgraph sub_one[Sub One]
|
||||
_sub_one
|
||||
end
|
||||
subgraph sub_two[Sub Two]
|
||||
_sub_two
|
||||
end
|
||||
_one
|
||||
end
|
||||
|
||||
%% here, either the first or the second one
|
||||
sub_one --> sub_two
|
||||
_one --> b
|
||||
</div>
|
||||
|
||||
<script src="./mermaid.js"></script>
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
<html>
|
||||
<head>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
/* background:#333; */
|
||||
font-family: 'Arial';
|
||||
}
|
||||
h1 { color: grey;}
|
||||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>info below</h1>
|
||||
<div class="flex">
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
flowchart BT
|
||||
subgraph two
|
||||
b1
|
||||
end
|
||||
subgraph three
|
||||
c1-->c2
|
||||
end
|
||||
c1 --apa apa apa--> b1
|
||||
two --> c2
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 200px;">
|
||||
sequenceDiagram
|
||||
Alice->>Bob:Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 200px;">
|
||||
%%{init: {'securityLevel': 'loose'}}%%
|
||||
graph TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{{Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?}}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[Car]
|
||||
click A "index.html#link-clicked" "link test"
|
||||
click B callback "click test"
|
||||
classDef someclass fill:#f96;
|
||||
class A someclass;
|
||||
class C someclass;
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 200px;">
|
||||
|
||||
flowchart BT
|
||||
subgraph a
|
||||
b1 -- ok --> b2
|
||||
end
|
||||
a -- sert --> c
|
||||
c --> d
|
||||
b1 --> d
|
||||
a --asd123 --> d
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
stateDiagram-v2
|
||||
state A {
|
||||
B1 --> B2: ok
|
||||
}
|
||||
A --> C: sert
|
||||
C --> D
|
||||
B1 --> D
|
||||
A --> D: asd123
|
||||
</div>
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
|
||||
flowchart LR
|
||||
a -->b
|
||||
subgraph A
|
||||
B
|
||||
end
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
|
||||
</div>
|
||||
<div class="mermaid" style="width: 50%; height: 20%;">
|
||||
flowchart TB
|
||||
subgraph A
|
||||
b-->B
|
||||
a-->c
|
||||
end
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
sequenceDiagram
|
||||
Alice->Bob: Hello Bob, how are you?
|
||||
Note over Alice,Bob: Looks
|
||||
Note over Bob,Alice: Looks back
|
||||
</div>
|
||||
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
// theme: 'forest',
|
||||
// themeVariables:{primaryColor: '#ff0000'},
|
||||
// arrowMarkerAbsolute: true,
|
||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||
logLevel: 0,
|
||||
flowchart: { curve: 'cardinal', "htmlLabels": false },
|
||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorMargin: 50, showSequenceNumbers: true },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
fontFamily: '"arial", sans-serif',
|
||||
curve: 'cardinal',
|
||||
securityLevel: 'strict'
|
||||
});
|
||||
function callback(){alert('It worked');}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -27,6 +27,9 @@
|
|||
svg {
|
||||
border: 2px solid darkred;
|
||||
}
|
||||
.exClass2 > rect, .exClass {
|
||||
fill: greenyellow !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Mermaid Quick Test Page</title>
|
||||
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
|
||||
<style>
|
||||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="display: flex">
|
||||
<div id="FirstLine" class="mermaid">
|
||||
graph TB
|
||||
Function-->URL
|
||||
click Function clickByFlow "Add a div"
|
||||
click URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
|
||||
</div>
|
||||
<div id="FirstLine" class="mermaid2">
|
||||
graph TB
|
||||
1Function-->2URL
|
||||
click 1Function clickByFlow "Add a div"
|
||||
click 2URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
|
||||
</div>
|
||||
|
||||
<div id="FirstLine" class="mermaid2">
|
||||
classDiagram
|
||||
class Test
|
||||
class ShapeLink
|
||||
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
|
||||
class ShapeCallback
|
||||
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
|
||||
</div>
|
||||
<div id="FirstLine" class="mermaid">
|
||||
classDiagram-v2
|
||||
class ShapeCallback
|
||||
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
|
||||
</div>
|
||||
<div id="FirstLine" class="mermaid">
|
||||
classDiagram-v2
|
||||
class ShapeLink
|
||||
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mermaid2">
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %d/%m
|
||||
title Adding GANTT diagram to mermaid
|
||||
excludes weekdays 2014-01-10
|
||||
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
|
||||
section Critical tasks
|
||||
Completed task in the critical line :crit, done, 2014-01-06,24h
|
||||
Implement parser and jison :crit, done, after des1, 2d
|
||||
Create tests for parser :crit, active, 3d
|
||||
Future task in critical line :crit, 5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid :1d
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :active, a1, after des1, 3d
|
||||
Add gantt diagram to demo page :after a1 , 20h
|
||||
Add another diagram to demo page :doc1, after a1 , 48h
|
||||
|
||||
section Clickable
|
||||
Visit mermaidjs :active, cl1, 2014-01-07,2014-01-10
|
||||
Calling a Callback (look at the console log) :cl2, after cl1, 3d
|
||||
Calling a Callback with args :cl3, after cl1, 3d
|
||||
|
||||
click cl1 href "http://localhost:9000/webpackUsage.html"
|
||||
click cl2 call clickByGantt()
|
||||
click cl3 call clickByGantt("test1", test2, test3)
|
||||
|
||||
section Last section
|
||||
Describe gantt syntax :after doc1, 3d
|
||||
Add gantt diagram to demo page : 20h
|
||||
Add another diagram to demo page : 48h
|
||||
</div>
|
||||
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
function clickByFlow(elemName) {
|
||||
const div = document.createElement('div')
|
||||
div.className = 'created-by-click'
|
||||
div.style = 'padding: 20px; background: green; color: white;'
|
||||
div.innerText = 'Clicked By Flow'
|
||||
|
||||
document.getElementsByTagName('body')[0].appendChild(div)
|
||||
}
|
||||
function clickByGantt(arg1, arg2, arg3) {
|
||||
const div = document.createElement('div')
|
||||
div.className = 'created-by-gant-click'
|
||||
div.style = 'padding: 20px; background: green; color: white;'
|
||||
div.innerText = 'Clicked By Gant'
|
||||
if (arg1) div.innerText += ' ' + arg1;
|
||||
if (arg2) div.innerText += ' ' + arg2;
|
||||
if (arg3) div.innerText += ' ' + arg3;
|
||||
|
||||
document.getElementsByTagName('body')[0].appendChild(div)
|
||||
}
|
||||
function clickByClass() {
|
||||
const div = document.createElement('div')
|
||||
div.className = 'created-by-class-click'
|
||||
div.style = 'padding: 20px; background: purple; color: white;'
|
||||
div.innerText = 'Clicked By Class'
|
||||
|
||||
document.getElementsByTagName('body')[0].appendChild(div)
|
||||
}
|
||||
mermaid.initialize({ startOnLoad: true, securityLevel: 'loose', logLevel: 1 });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -22,19 +22,36 @@
|
|||
<body>
|
||||
<h1>info below</h1>
|
||||
<div class="flex">
|
||||
<div class="mermaid" style="width: 50%; height: 400px;">
|
||||
graph TD
|
||||
a["<strong>Haiya</strong>"]-->b
|
||||
<div class="mermaid2" style="width: 50%; height: 400px;">
|
||||
flowchart TD
|
||||
subgraph main
|
||||
subgraph subcontainer
|
||||
subcontainer-child
|
||||
end
|
||||
subcontainer-child--> subcontainer-sibling
|
||||
end
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
%%{init: { 'theme': 'base', 'themeVariables':{ 'primaryColor': '#ff0000'}}}%%
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
<div class="mermaid" style="width: 50%; height: 400px;">
|
||||
flowchart TB
|
||||
b-->B
|
||||
a-->c
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
subgraph A
|
||||
a
|
||||
b
|
||||
B
|
||||
end
|
||||
</div>
|
||||
<div class="mermaid" style="width: 50%; height: 20%;">
|
||||
flowchart TB
|
||||
subgraph A
|
||||
a -->b
|
||||
end
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
%%{init: {"fontFamily": "arial2"}}%%
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 134 KiB |
Binary file not shown.
After Width: | Height: | Size: 152 KiB |
Binary file not shown.
After Width: | Height: | Size: 167 KiB |
Binary file not shown.
After Width: | Height: | Size: 162 KiB |
Binary file not shown.
After Width: | Height: | Size: 132 KiB |
Binary file not shown.
After Width: | Height: | Size: 127 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,5 @@
|
|||
remote_theme: false
|
||||
|
||||
baseurl: /mermaid
|
||||
|
||||
theme: jekyll-rtd-theme
|
|
@ -1,5 +1,5 @@
|
|||
default:
|
||||
@bundle install
|
||||
@gem install jekyll bundler && bundle install
|
||||
|
||||
update:
|
||||
@bundle update
|
||||
|
@ -7,8 +7,8 @@ update:
|
|||
clean:
|
||||
@bundle exec jekyll clean
|
||||
|
||||
server: clean
|
||||
@bundle exec jekyll server
|
||||
|
||||
build: clean
|
||||
@bundle exec jekyll build --profile
|
||||
@bundle exec jekyll build --profile --config _config.yml,.debug.yml
|
||||
|
||||
server: clean
|
||||
@bundle exec jekyll server --livereload --config _config.yml,.debug.yml
|
||||
|
|
|
@ -9,29 +9,31 @@
|
|||
## About
|
||||
|
||||
<!-- <Main description> -->
|
||||
Mermaid is a Javascript based diagramming and charting tool that uses Markdown-inspired text definitions and a renderer to create and modify complex diagrams. The main purpose of Mermaid is to help documentation catch up with development.
|
||||
Mermaid simplifies complex diagrams. It is a Javascript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically. The main purpose of Mermaid is to help Documentation catch up with Development.
|
||||
|
||||
> Doc-Rot is a Catch-22 that Mermaid helps to solve.
|
||||
|
||||
Diagramming and documentation costs precious developer time and gets outdated quickly.
|
||||
But not having diagrams or docs ruins productivity and hurts organizational learning. <br/>
|
||||
Mermaid addresses this problem by cutting the time, effort and tooling that is required to create modifiable diagrams and charts, for smarter and more reusable content.
|
||||
The text definitions for Mermaid diagrams allows for it to be updated easily, it can also be made part of production scripts (and other pieces of code).
|
||||
So less time needs to be spent on documenting, as a separate and laborious task. <br/>
|
||||
Mermaid addresses this Catch-22 by cutting the time, effort and tooling that is required to create modifiable diagrams and charts, for smarter and more reusable content.
|
||||
Mermaid, as a text-based diagramming tool allows for quick and easy updates, it can also be made part of production scripts (and other pieces of code), to make documentation much easier.
|
||||
With Mermaid less time needs to be spent on making diagrams, as a separate documentation task. <br/>
|
||||
|
||||
Even non-programmers can create diagrams through the [Mermaid Live Editor](https://github.com/mermaidjs/mermaid-live-editor), visit [Mermaid Overview](./n00b-overview.md) for the video tutorials.
|
||||
> Mermaid is a Diagramming tool for everyone.
|
||||
|
||||
Want to see what can be built with mermaid, or what applications already support it? Read the [Integrations and Usages for Mermaid](./integrations.md).
|
||||
Even non-programmers can create diagrams through the [Mermaid Live Editor](https://github.com/mermaidjs/mermaid-live-editor), Visit the [Tutorials Page](href='https://github.com/mermaid-js/mermaid/blob/develop/docs/Tutorials.md) for the Live Editor video tutorials.
|
||||
|
||||
For a more detailed introduction to Mermaid and some of it's more basic uses, look to the [Beginner's Guide](./n00b-overview.md) and [Usage](./usage.md).
|
||||
Want to see what can be built with mermaid, or what applications already support it? Read the [Integrations and Usages for Mermaid](overview/integrations.md).
|
||||
|
||||
🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) | 📜 [Version Log](./CHANGELOG.md)
|
||||
For a more detailed introduction to Mermaid and some of it's more basic uses, look to the [Beginner's Guide](overview/n00b-overview.md) and [Usage](getting-started/usage.md).
|
||||
|
||||
🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](https://github.com/mermaid-js/mermaid/blob/develop/docs/tutorials-and-community/development.md) | 📜 [Version Log](tutorials-and-community/CHANGELOG.md)
|
||||
|
||||
> 🖖 Keep a steady pulse: mermaid needs more Collaborators, [Read More](https://github.com/knsv/mermaid/issues/866).
|
||||
|
||||
# Diagrams that mermaid can render:
|
||||
|
||||
### [Flowchart](https://mermaid-js.github.io/mermaid/#/flowchart)
|
||||
### [Flowchart](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/flowchart.html)
|
||||
|
||||
```
|
||||
graph TD;
|
||||
|
@ -43,7 +45,7 @@ graph TD;
|
|||
|
||||
![Flowchart](assets/img/flow.png)
|
||||
|
||||
### [Sequence diagram](https://mermaid-js.github.io/mermaid/#/sequenceDiagram)
|
||||
### [Sequence diagram](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/sequenceDiagram.html)
|
||||
|
||||
```
|
||||
sequenceDiagram
|
||||
|
@ -61,7 +63,7 @@ sequenceDiagram
|
|||
|
||||
![Sequence diagram](assets/img/sequence.png)
|
||||
|
||||
### [Gantt diagram](https://mermaid-js.github.io/mermaid/#/gantt)
|
||||
### [Gantt diagram](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/gantt.html)
|
||||
|
||||
```
|
||||
gantt
|
||||
|
@ -78,7 +80,7 @@ Future task2 : des4, after des3, 5d
|
|||
|
||||
![Gantt diagram](assets/img/gantt.png)
|
||||
|
||||
### [Class diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/#/classDiagram)
|
||||
### [Class diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/classDiagram.html)
|
||||
|
||||
```
|
||||
classDiagram
|
||||
|
@ -122,7 +124,7 @@ merge newbranch
|
|||
```
|
||||
![Git graph](assets/img/git.png)
|
||||
|
||||
### [Entity Relationship Diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/#/entityRelationshipDiagram)
|
||||
### [Entity Relationship Diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/entityRelationshipDiagram.html)
|
||||
|
||||
```
|
||||
erDiagram
|
||||
|
@ -134,7 +136,7 @@ erDiagram
|
|||
|
||||
![ER diagram](assets/img/simple-er.png)
|
||||
|
||||
### [User Journey Diagram](https://mermaid-js.github.io/mermaid/#/user-journey)
|
||||
### [User Journey Diagram](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/user-journey.html)
|
||||
|
||||
```markdown
|
||||
journey
|
||||
|
@ -150,9 +152,9 @@ journey
|
|||
![Journey diagram](assets/img/user-journey.png)
|
||||
|
||||
# Installation
|
||||
## In depth guides and examples can be found in [Getting Started](./n00b-gettingStarted.md) and [Usage](./usage.md).
|
||||
## In depth guides and examples can be found in [Getting Started](getting-started/n00b-gettingStarted.md) and [Usage](getting-started/usage.md).
|
||||
|
||||
## It would also be helpful to learn more about mermaid's [Syntax](./n00b-syntaxReference.md).
|
||||
## It would also be helpful to learn more about mermaid's [Syntax](diagrams-and-syntax-and-examples/n00b-syntaxReference.md).
|
||||
|
||||
### CDN
|
||||
|
||||
|
@ -166,7 +168,7 @@ Replace `<version>` with the desired version number.
|
|||
|
||||
Alternatively, you can also adjust the version number in the page itself.
|
||||
|
||||
Latest Version: [https://unpkg.com/browse/mermaid@8.6.0/](https://unpkg.com/browse/mermaid@8.6.0/)
|
||||
Latest Version: [https://unpkg.com/browse/mermaid@8.8.0/](https://unpkg.com/browse/mermaid@8.8.0/)
|
||||
|
||||
## Incorporating mermaid to a website
|
||||
To support mermaid on your website, all you have to do is add Mermaid’s JavaScript package
|
||||
|
@ -191,7 +193,7 @@ To support mermaid on your website, all you have to do is add Mermaid’s JavaSc
|
|||
```
|
||||
## Doing so will command the mermaid parser to look for the `<div>` tags with `class="mermaid"` in your HTML Document. From these tags mermaid will try to read the diagram/chart definitons and render them as svg charts.
|
||||
|
||||
## Examples can be found in [Getting Started](./n00b-gettingStarted.md)
|
||||
## Examples can be found in [Getting Started](getting-started/n00b-gettingStarted.md)
|
||||
|
||||
# Sibling projects
|
||||
- [Mermaid Live Editor](https://github.com/mermaidjs/mermaid-live-editor)
|
||||
|
|
|
@ -2,13 +2,13 @@ title: mermaid
|
|||
lang: en
|
||||
description: Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.
|
||||
|
||||
# theme: jekyll-rtd-theme
|
||||
remote_theme: rundocs/jekyll-rtd-theme
|
||||
|
||||
mermaid:
|
||||
custom: https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js
|
||||
|
||||
copyright:
|
||||
since: 2014
|
||||
revision: true
|
||||
|
||||
edit: true
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
- Contributions and Community
|
||||
|
||||
<!-- - [Development and Contribution](development.md)
|
||||
- [Mermaid Versions](versionUpdates.md)
|
||||
- [Changelog](CHANGELOG.md) -->
|
||||
<!-- - [Development and Contribution](development.md)-->
|
||||
<!-- - [Adding Diagrams](newDiagram.md)-->
|
||||
<!-- - [Changelog](CHANGELOG.md) -->
|
||||
|
||||
|
||||
- Diagrams and Syntax and Examples
|
||||
|
|
|
@ -612,7 +612,7 @@ The main styling of the class diagram is done with a preset number of css classe
|
|||
|
||||
#### Sample stylesheet
|
||||
|
||||
```css
|
||||
```scss
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ sort: 10
|
|||
|
||||
## Directives
|
||||
|
||||
## Directives were added in [Version 8.6.0](/8.6.0_docs.md). Please Read it for more information.
|
||||
## Directives were added in [Version 8.6.0](../getting-started/8.6.0_docs.md). Please Read it for more information.
|
||||
|
||||
## Directives
|
||||
With this version, directives are supported. Directives are divided in two sets, by priority. the first set, containing 'init' or 'initialize' directives take priority. While the other set, containing all other kinds of directives are considered only after 'init' and the graph-type declared.
|
||||
|
|
|
@ -6,9 +6,9 @@ sort: 2
|
|||
|
||||
This page contains a collection of examples of diagrams and charts that can be created through mermaid and its myriad applications.
|
||||
|
||||
## If you wish to learn how to support mermaid on your webpage, read the [Beginner's Guide](https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted).
|
||||
## If you wish to learn how to support mermaid on your webpage, read the [Beginner's Guide](../getting-started/n00b-gettingStarted.md).
|
||||
|
||||
## If you wish to learn about mermaid's syntax, Read the [Diagram Syntax](https://mermaid-js.github.io/mermaid/#/n00b-syntaxReference) section.
|
||||
## If you wish to learn about mermaid's syntax, Read the [Diagram Syntax](n00b-syntaxReference.md) section.
|
||||
|
||||
## Basic Pie Chart
|
||||
|
||||
|
|
|
@ -609,7 +609,7 @@ click nodeId callback
|
|||
|
||||
Examples of tooltip usage below:
|
||||
|
||||
```
|
||||
```html
|
||||
<script>
|
||||
var callback = function(){
|
||||
alert('A callback was triggered');
|
||||
|
@ -654,7 +654,7 @@ graph LR;
|
|||
```
|
||||
|
||||
Beginners tip, a full example using interactive links in a html context:
|
||||
```
|
||||
```html
|
||||
<body>
|
||||
<div class="mermaid">
|
||||
graph LR;
|
||||
|
|
|
@ -354,7 +354,7 @@ click taskId href URL
|
|||
* callback is the name of a javascript function defined on the page displaying the graph, the function will be called with the taskId as the parameter if no other arguments are specified..
|
||||
|
||||
Beginners tip, a full example using interactive links in an html context:
|
||||
```
|
||||
```html
|
||||
<body>
|
||||
<div class="mermaid">
|
||||
gantt
|
||||
|
|
|
@ -5,12 +5,12 @@ title: Diagram syntax intro
|
|||
|
||||
## Diagram syntax
|
||||
|
||||
If you are new to mermaid, read the [Getting Started](n00b-gettingStarted.md) and [Overview](n00b-overview.md) sections, to learn the basics of mermaid.
|
||||
If you are new to mermaid, read the [Getting Started](../getting-started/n00b-gettingStarted.md) and [Overview](../overview/n00b-overview.md) sections, to learn the basics of mermaid.
|
||||
Video Tutorials can be found at the bottom of the Overview Section.
|
||||
|
||||
This section is a list of diagram types supported by mermaid. Below is a list of links to aricles that explain the syntax of the diagrams or charts that 0can be called.
|
||||
This section is a list of diagram types supported by mermaid. Below is a list of links to aricles that explain the syntax of the diagrams or charts that can be called.
|
||||
|
||||
They also detail how diagrams can be defined, or described in the manner with which the diagram is to be rendered by the renderer.
|
||||
They also detail how diagrams can be defined, or described in the manner with which the diagram is to be rendered by the renderer.
|
||||
|
||||
### The benefits of text based diagramming are its speed and modifiability. mermaid allows for easy maintenance and modification of diagrams. This means your diagrams will always be up to date and closely follow your code and improve your documentation.
|
||||
|
||||
|
@ -30,12 +30,4 @@ These definitions can also be entered into the [mermaid live editor](https://mer
|
|||
This would then offer
|
||||
|
||||
|
||||
- [Flowchart](flowchart.md)
|
||||
- [Sequence diagram](sequenceDiagram.md)
|
||||
- [Class Diagram](classDiagram.md)
|
||||
- [State Diagram](stateDiagram.md)
|
||||
- [Gantt](gantt.md)
|
||||
- [Pie Chart](pie.md)
|
||||
- [Entity Relationship Diagram](entityRelationshipDiagram.md)
|
||||
- [User Journey Diagram](user-journey.md)
|
||||
- [Directives](directives.md)
|
||||
{% include list.liquid %}
|
||||
|
|
|
@ -20,9 +20,11 @@ sequenceDiagram
|
|||
John-->>Alice: Great!
|
||||
```
|
||||
|
||||
## A note on nodes, the word "end" could potentially break the diagram, due to the way that the mermaid language is scripted.
|
||||
## If unavoidable, one must use parentheses(), quotation marks "", or brackets {},[], to enclose the word "end". i.e : (end), [end], {end}.
|
||||
```note
|
||||
A note on nodes, the word "end" could potentially break the diagram, due to the way that the mermaid language is scripted.
|
||||
|
||||
If unavoidable, one must use parentheses(), quotation marks "", or brackets {},[], to enclose the word "end". i.e : (end), [end], {end}.
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ sort: 6
|
|||
|
||||
Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make it easier for users to share diagrams between mermaid and plantUml.
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
|
@ -86,7 +86,7 @@ Transitions are path/edges when one state passes into another. This is represent
|
|||
|
||||
When you define a transition between two states and the states are not already defined the undefined states are defined with the id from the transition. You can later add descriptions to states defined this way.
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
s1 --> s2
|
||||
```
|
||||
|
@ -98,7 +98,7 @@ stateDiagram-v2
|
|||
|
||||
It is possible to add text to a transition. To describe what it represents.
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
s1 --> s2: A transition
|
||||
```
|
||||
|
@ -112,7 +112,7 @@ stateDiagram-v2
|
|||
|
||||
There are two special states indicating the start and stop of the diagram. These are written with the [\*] syntax and the direction of the transition to it defines it either as a start or a stop state.
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
[*] --> s1
|
||||
s1 --> [*]
|
||||
|
@ -131,7 +131,7 @@ have several internal states. These are called composite states in this terminol
|
|||
|
||||
In order to define a composite state you need to use the state keyword followed by and id and the body of the composite state between \{\}. See the example below:
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
[*] --> First
|
||||
state First {
|
||||
|
@ -151,7 +151,7 @@ stateDiagram-v2
|
|||
|
||||
You can do this in several layers:
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
[*] --> First
|
||||
|
||||
|
@ -190,7 +190,7 @@ stateDiagram-v2
|
|||
|
||||
You can also define transitions also between composite states:
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
[*] --> First
|
||||
First --> Second
|
||||
|
@ -236,7 +236,7 @@ stateDiagram-v2
|
|||
|
||||
It is possible to specify a fork in the diagram using <<fork>> <<join>>.
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
state fork_state <<fork>>
|
||||
[*] --> fork_state
|
||||
|
@ -271,7 +271,7 @@ Sometimes nothing says it better then a Post-it note. That is also the case in s
|
|||
|
||||
Here you can choose to put the note to the *right of* or to the *left of* a node.
|
||||
|
||||
```markdown
|
||||
```
|
||||
stateDiagram-v2
|
||||
State1: The state with a note
|
||||
note right of State1
|
||||
|
@ -298,42 +298,42 @@ Here you can choose to put the note to the *right of* or to the *left of* a node
|
|||
|
||||
As in plantUml you can specify concurrency using the -- symbol.
|
||||
|
||||
```markdown
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
```
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
|
||||
state Active {
|
||||
[*] --> NumLockOff
|
||||
NumLockOff --> NumLockOn : EvNumLockPressed
|
||||
NumLockOn --> NumLockOff : EvNumLockPressed
|
||||
--
|
||||
[*] --> CapsLockOff
|
||||
CapsLockOff --> CapsLockOn : EvCapsLockPressed
|
||||
CapsLockOn --> CapsLockOff : EvCapsLockPressed
|
||||
--
|
||||
[*] --> ScrollLockOff
|
||||
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
|
||||
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
|
||||
}
|
||||
state Active {
|
||||
[*] --> NumLockOff
|
||||
NumLockOff --> NumLockOn : EvNumLockPressed
|
||||
NumLockOn --> NumLockOff : EvNumLockPressed
|
||||
--
|
||||
[*] --> CapsLockOff
|
||||
CapsLockOff --> CapsLockOn : EvCapsLockPressed
|
||||
CapsLockOn --> CapsLockOff : EvCapsLockPressed
|
||||
--
|
||||
[*] --> ScrollLockOff
|
||||
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
|
||||
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
|
||||
}
|
||||
```
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
|
||||
state Active {
|
||||
[*] --> NumLockOff
|
||||
NumLockOff --> NumLockOn : EvNumLockPressed
|
||||
NumLockOn --> NumLockOff : EvNumLockPressed
|
||||
--
|
||||
[*] --> CapsLockOff
|
||||
CapsLockOff --> CapsLockOn : EvCapsLockPressed
|
||||
CapsLockOn --> CapsLockOff : EvCapsLockPressed
|
||||
--
|
||||
[*] --> ScrollLockOff
|
||||
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
|
||||
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
|
||||
}
|
||||
state Active {
|
||||
[*] --> NumLockOff
|
||||
NumLockOff --> NumLockOn : EvNumLockPressed
|
||||
NumLockOn --> NumLockOff : EvNumLockPressed
|
||||
--
|
||||
[*] --> CapsLockOff
|
||||
CapsLockOff --> CapsLockOn : EvCapsLockPressed
|
||||
CapsLockOn --> CapsLockOff : EvCapsLockPressed
|
||||
--
|
||||
[*] --> ScrollLockOff
|
||||
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
|
||||
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
|
||||
}
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
|
|
@ -79,15 +79,15 @@ When deployed within code, init is called before the graph/diagram description.
|
|||
|
||||
**for example**:
|
||||
```
|
||||
%%{init: {"theme": "default", "logLevel": 1 }}%%
|
||||
graph LR
|
||||
a-->b
|
||||
b-->c
|
||||
c-->d
|
||||
d-->e
|
||||
e-->f
|
||||
f-->g
|
||||
g-->
|
||||
%%{init: {"theme": "default", "logLevel": 1 }}%%
|
||||
graph LR
|
||||
a-->b
|
||||
b-->c
|
||||
c-->d
|
||||
d-->e
|
||||
e-->f
|
||||
f-->g
|
||||
g-->
|
||||
```
|
||||
# Wrap
|
||||
|
||||
|
@ -214,4 +214,4 @@ Ensures options parameter does not attempt to override siteConfig secure keys.
|
|||
default: current siteConfig (optional, default `getSiteConfig()`)
|
||||
```
|
||||
|
||||
## For more information, read [Setup](https://mermaid-js.github.io/mermaid/#/Setup).
|
||||
## For more information, read [Setup](Setup.md).
|
||||
|
|
|
@ -146,9 +146,8 @@ mermaidAPI.initialize({
|
|||
|
||||
## mermaidAPI configuration defaults
|
||||
|
||||
<pre>
|
||||
|
||||
<script>
|
||||
```html
|
||||
<script>
|
||||
var config = {
|
||||
theme:'default',
|
||||
logLevel:'fatal',
|
||||
|
@ -203,7 +202,7 @@ mermaidAPI.initialize({
|
|||
}
|
||||
};
|
||||
mermaid.initialize(config);
|
||||
</script>
|
||||
</pre>
|
||||
</script>
|
||||
```
|
||||
|
||||
[1]: Setup.md?id=render
|
||||
|
|
|
@ -5,7 +5,7 @@ This is list of publicly available Tutorials in Mermaid. This is intended as a
|
|||
For the purposes, the Live-Editor is capable of taking care of all diagramming needs, and these are the most common use cases for it.
|
||||
|
||||
|
||||
# Live-Editor Tutorials
|
||||
## Live-Editor Tutorials
|
||||
The definitions that can be generated the Live-Editor are also backwards-compatible as of version 8.7.0.
|
||||
|
||||
https://www.youtube.com/watch?v=SQ9QmuTHuSI&t=438s
|
||||
|
@ -20,8 +20,8 @@ https://www.youtube.com/watch?v=7_2IroEs6Is&t=207s
|
|||
|
||||
https://www.youtube.com/watch?v=9HZzKkAqrX8
|
||||
|
||||
# Mermaid with HTML:
|
||||
Examples are provided in [Gettting Started](./docs/n00b-gettingStarted.md)
|
||||
## Mermaid with HTML:
|
||||
Examples are provided in [Gettting Started](n00b-gettingStarted.md)
|
||||
|
||||
**CodePen Examples:**
|
||||
|
||||
|
@ -35,4 +35,3 @@ https://codepen.io/janzeteachesit/pen/OWWZKN
|
|||
## Mermaid with Text Area:
|
||||
|
||||
https://codepen.io/Ryuno-Ki/pen/LNxwgR
|
||||
|
||||
|
|
|
@ -14,13 +14,13 @@ This then renders a diagram based on that code in SVG, alternatively it
|
|||
Most web browsers, such as Firefox, Chrome and Safari, can render mermaid, Internet Explorer however cannot.
|
||||
|
||||
## For beginners, there are four relatively easy ways you can use mermaid:
|
||||
1. Using the mermaid [live editor](https://mermaid-js.github.io/mermaid-live-editor/). For some popular video tutorials on the live editor go to [Overview](./n00b-overview.md).
|
||||
2. Using one of the many [mermaid plugins](https://mermaid-js.github.io/mermaid/#/integrations).
|
||||
1. Using the mermaid [live editor](https://mermaid-js.github.io/mermaid-live-editor/). For some popular video tutorials on the live editor go to [Overview](../overview/n00b-overview.md).
|
||||
2. Using one of the many [mermaid plugins](../overview/integrations.md).
|
||||
3. Hosting mermaid on a webpage, with an absolute link.
|
||||
4. Downloading mermaid and hosting it on your Web Page.
|
||||
|
||||
**Notes**: More in depth information can be found on [Usage](./usage.md).
|
||||
.
|
||||
|
||||
# Following any of these examples, you can get started with creating your own diagrams using mermaid code.
|
||||
|
||||
## 1. The mermaid live editor:
|
||||
|
@ -31,7 +31,7 @@ In the `Code` section one can write or edit raw mermaid code, and instantly `Pre
|
|||
|
||||
**This is a great way to learn how to define a mermaid diagram.**
|
||||
|
||||
For some popular video tutorials on the live editor go to [Overview](./n00b-overview.md).
|
||||
For some popular video tutorials on the live editor go to [Overview](../overview/n00b-overview.md).
|
||||
|
||||
![Flowchart](../assets/img/n00b-liveEditor.png)
|
||||
|
||||
|
@ -41,12 +41,12 @@ You can also click "Copy Markdown" to copy the markdown code for the diagram, th
|
|||
|
||||
![Flowchart](../assets/img/liveEditorOptions.png)
|
||||
|
||||
The `Mermaid configuration` is for controlling mermaid behaviour. An easy introduction to mermaid configuration is found in the [Advanced usage](n00b-advanced.md) section. A complete configuration reference cataloguing default values is found on the [mermaidAPI](https://mermaid-js.github.io/mermaid/#/Setup) page.
|
||||
The `Mermaid configuration` is for controlling mermaid behaviour. An easy introduction to mermaid configuration is found in the [Advanced usage](n00b-advanced.md) section. A complete configuration reference cataloguing default values is found on the [mermaidAPI](Setup.md) page.
|
||||
|
||||
|
||||
## 2. Using mermaid plugins:
|
||||
|
||||
Thanks to the growing popularity of mermaid, many plugins already exist which incorporate a mermaid renderer. An extensive list can be found [here](./integrations.md).
|
||||
Thanks to the growing popularity of mermaid, many plugins already exist which incorporate a mermaid renderer. An extensive list can be found [here](../overview/integrations.md).
|
||||
|
||||
One example in the list is the [Atlassian Confluence mermaid plugin](https://marketplace.atlassian.com/apps/1214124/mermaid-plugin-for-confluence?hosting=server&tab=overview)
|
||||
|
||||
|
@ -75,11 +75,11 @@ When the mermaid plugin is installed on a Confluence server, one can insert a me
|
|||
|
||||
- Save the page and the diagram appears.
|
||||
|
||||
![Flowchart](../../assets/img/n00b-Confluence4.png)
|
||||
![Flowchart](../assets/img/n00b-Confluence4.png)
|
||||
|
||||
---
|
||||
## The following are two ways of hosting mermaid on a webpage.
|
||||
**This is covered in greater detail in the [Usage section](https://mermaid-js.github.io/mermaid/#/usage)**
|
||||
**This is covered in greater detail in the [Usage section](usage.md)**
|
||||
|
||||
## 3. Using the Mermaid API: The quick and dirty way of deploying mermaid
|
||||
|
||||
|
@ -106,7 +106,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
|
|||
|
||||
### a. A reference to the address of the `mermaid.js` or the `mermaid.min.js` file has to be contained in a `<script src>` tag like so:
|
||||
|
||||
```
|
||||
```html
|
||||
<body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
</body>
|
||||
|
@ -114,7 +114,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
|
|||
|
||||
### b. The embedded mermaid diagram definition needs to be contained inside a `<div>` tag that signifies that it is a mermaid diagram:
|
||||
|
||||
```
|
||||
```html
|
||||
<body>
|
||||
Here is a mermaid diagram:
|
||||
<div class="mermaid">
|
||||
|
@ -131,7 +131,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
|
|||
|
||||
`mermaid.initialize()` calls take all the definitions contained in `<div class="mermaid">` tags it can find in the html body and starts to render them one by one. It is called this way:
|
||||
|
||||
```
|
||||
```html
|
||||
<body>
|
||||
<script>mermaid.initialize({startOnLoad:true});</script>
|
||||
</body>
|
||||
|
@ -142,7 +142,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
|
|||
### If the three steps mentioned are followed you will end up with something like this:
|
||||
|
||||
|
||||
```
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
|
@ -190,14 +190,14 @@ This method is similar to 3, if only a little more involved. The difference may
|
|||
c. select copy as path from the options.
|
||||
|
||||
6. Paste it within the `script` tag as the `src`.
|
||||
```
|
||||
```html
|
||||
<script src="Paste the mermaid.min.js file address here"></script>
|
||||
<script>mermaid.initialize({startOnLoad:true});</script>
|
||||
<script>mermaid.initialize({startOnLoad:true});</script>
|
||||
```
|
||||
7. It should look something like this
|
||||
```
|
||||
<script src="C:\Users\myPC\mermaid\dist\mermaid.js"></script>
|
||||
<script>mermaid.initialize({startOnLoad:true});</script>
|
||||
```html
|
||||
<script src="C:\Users\myPC\mermaid\dist\mermaid.js"></script>
|
||||
<script>mermaid.initialize({startOnLoad:true});</script>
|
||||
```
|
||||
8. Add the graph and diagram definitions as you would in number 3.
|
||||
a. be mindful of the `div` tags.
|
||||
|
@ -208,7 +208,7 @@ This method is similar to 3, if only a little more involved. The difference may
|
|||
**Note** placing the HTML file on the same folder the `mermaid` file you've downloaded is a good practice and allows you to shorten the address on the `src` section.
|
||||
|
||||
**As seen here, in this full example:**
|
||||
```
|
||||
```html
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
|
|
@ -47,23 +47,24 @@ This topic explored in greater depth in the [User Guide for Beginners](./n00b-ge
|
|||
The easiest way to integrate mermaid on a web page requires three elements:
|
||||
|
||||
1. Inclusion of the mermaid address in the html page using a `script` tag, in the `src` section.Example:
|
||||
|
||||
`<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>`
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
```
|
||||
|
||||
2. The `mermaidAPI` call, in a separate `script` tag. Example:
|
||||
|
||||
`<script>mermaid.initialize({startOnLoad:true});</script>`
|
||||
```html
|
||||
<script>mermaid.initialize({startOnLoad:true});</script>
|
||||
```
|
||||
|
||||
3. A graph definition, inside `<div>` tags labeled `class=mermaid`. Example:
|
||||
|
||||
```html
|
||||
<div class="mermaid">
|
||||
graph LR
|
||||
A --- B
|
||||
B-->C[fa:fa-ban forbidden]
|
||||
B-->D(fa:fa-spinner);
|
||||
</div>
|
||||
```
|
||||
```html
|
||||
<div class="mermaid">
|
||||
graph LR
|
||||
A --- B
|
||||
B-->C[fa:fa-ban forbidden]
|
||||
B-->D(fa:fa-spinner);
|
||||
</div>
|
||||
```
|
||||
|
||||
**If these things are in place mermaid starts at the page load event and when fired (when the page has loaded) it will
|
||||
locate the graph definitions inside the `div` tags with `class="mermaid"` on the page and transform them to svg charts or diagrams.**
|
||||
|
@ -125,9 +126,9 @@ This changes the default behaviour of mermaid so that after upgrade to 8.2, if t
|
|||
## To chage `securityLevel` with `mermaidAPI.initialize`:
|
||||
|
||||
```javascript
|
||||
mermaidAPI.initialize({
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
mermaidAPI.initialize({
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
@ -153,11 +154,13 @@ $(document).ready(function() {
|
|||
Not doing so will most likely result in mermaid rendering graphs that have labels out of bounds. The default integration in mermaid uses the window.load event to start rendering.
|
||||
|
||||
If your page has other fonts in its body those might be used instead of the mermaid font. Specifying the font in your styling is a workaround for this.
|
||||
|
||||
```css
|
||||
div.mermaid {
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
}
|
||||
```
|
||||
div.mermaid {
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
}
|
||||
```
|
||||
|
||||
# This likely requires a `script.js` file, separate from the `HTML`.
|
||||
|
||||
### Calling `mermaid.init`
|
||||
|
@ -335,7 +338,7 @@ setting the options in mermaid.
|
|||
4. Instantiation of the configuration using the **mermaid.init** call
|
||||
|
||||
The list above has two ways too many of doing this. Three are deprecated and will eventually be removed. The list of
|
||||
configuration objects are described [in the mermaidAPI documentation](mermaidAPI.html).
|
||||
configuration objects are described [in the mermaidAPI documentation](Setup.md).
|
||||
|
||||
|
||||
## Using the `mermaidAPI.initialize`/`mermaid.initialize` call
|
||||
|
|
|
@ -66,7 +66,9 @@ They also serve as proof of concept, for the variety of things that cen be built
|
|||
|
||||
## Wikis
|
||||
|
||||
- [Media Wiki](https://www.mediawiki.org/wiki/Extension:Mermaid)
|
||||
- [MediaWiki](https://www.mediawiki.org)
|
||||
- [Mermaid Extension](https://www.mediawiki.org/wiki/Extension:Mermaid)
|
||||
- [Flex Diagrams Extension](https://www.mediawiki.org/wiki/Extension:Flex_Diagrams)
|
||||
- [Semantic Media Wiki](https://semantic-mediawiki.org)
|
||||
- [Mermaid Plugin](https://github.com/SemanticMediaWiki/Mermaid)
|
||||
- [FosWiki](https://foswiki.org)
|
||||
|
|
|
@ -28,14 +28,15 @@ mermaid seeks to change that. mermaid is a javascript based tool that utilizes a
|
|||
**Mermaid definitions**
|
||||
|
||||
>These are the instrunctions for how the diagram is to rendered, written in mermaid, which is based on Markdown. These can be found inside `<div>` tags, with the `class=mermaid`.
|
||||
```
|
||||
|
||||
```html
|
||||
<div class="mermaid">
|
||||
graph TD
|
||||
A[Client] --> B[Load Balancer]
|
||||
B --> C[Server01]
|
||||
B --> D[Server02]
|
||||
</div>
|
||||
```
|
||||
```
|
||||
|
||||
**render**
|
||||
|
||||
|
@ -55,7 +56,7 @@ mermaid seeks to change that. mermaid is a javascript based tool that utilizes a
|
|||
|
||||
## The catch-22 of Diagrams and Charts:
|
||||
|
||||
**Diagramming and charting is a gigantic waste of developer time, but not having diagrams ruins productivity. **
|
||||
**Diagramming and charting is a gigantic waste of developer time, but not having diagrams ruins productivity.**
|
||||
|
||||
mermaid solves this by cutting the time, effort and tooling that is required to create diagrams and charts.
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ Here is the list of the newest versions in Descending Order, beginning from the
|
|||
This version brings with it a system for [dynamic and integrated configuration of the diagram themes](./theming.md).
|
||||
The objective of this is to increase the customizability of mermaid and the ease of Styling, with the customization of themes through the `%%init%%` directive and `initialize` calls.
|
||||
|
||||
Themes follow and build upon the Levels of Configuration and employ `directives` to modify and create custom configurations, as they were introduced in Version [8.6.0](./8.6.0_docs.md).
|
||||
Themes follow and build upon the Levels of Configuration and employ `directives` to modify and create custom configurations, as they were introduced in Version [8.6.0](../getting-started/8.6.0_docs.md).
|
||||
|
||||
**These Theming Configurations, similar to directives, will also be made applicable in the Live-Editor, for easier styling.**
|
||||
|
||||
|
@ -30,9 +30,9 @@ Site-wide themes are still declared via `initialize` by site owners.
|
|||
Example of `Initalize` call setting `theme` to `base`:
|
||||
|
||||
```js
|
||||
mermaidAPI.initialize({
|
||||
'securityLevel': 'loose', 'theme': 'base'
|
||||
});
|
||||
mermaidAPI.initialize({
|
||||
'securityLevel': 'loose', 'theme': 'base'
|
||||
});
|
||||
```
|
||||
|
||||
**Notes**: Only site owners can use the `mermaidAPI.initialize` call, to set values. Site-Users will have to use `%%init%%` to modify or create the theme for their diagrams.
|
||||
|
@ -86,7 +86,7 @@ Leaving it empty will set all variable values to default.
|
|||
|
||||
🔖 [Release Notes](https://github.com/mermaid-js/mermaid/releases/tag/8.6.0)
|
||||
|
||||
[Version 8.6.0](./8.6.0_docs.md) introduces New Configuration Protocols and Directives and a Beta for the [New Mermaid Live-Editor](https://mermaid-js.github.io/docs/mermaid-live-editor-beta/#/edit/eyJjb2RlIjoiJSV7aW5pdDoge1widGhlbWVcIjogXCJmb3Jlc3RcIiwgXCJsb2dMZXZlbFwiOiAxIH19JSVcbmdyYXBoIFREXG4gIEFbQ2hyaXN0bWFzXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgQyAtLT58T25lfCBEW0xhcHRvcF1cbiAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cblx0XHQiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGFyayJ9fQ)
|
||||
[Version 8.6.0](../getting-started/8.6.0_docs.md) introduces New Configuration Protocols and Directives and a Beta for the [New Mermaid Live-Editor](https://mermaid-js.github.io/docs/mermaid-live-editor-beta/#/edit/eyJjb2RlIjoiJSV7aW5pdDoge1widGhlbWVcIjogXCJmb3Jlc3RcIiwgXCJsb2dMZXZlbFwiOiAxIH19JSVcbmdyYXBoIFREXG4gIEFbQ2hyaXN0bWFzXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgQyAtLT58T25lfCBEW0xhcHRvcF1cbiAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cblx0XHQiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGFyayJ9fQ)
|
||||
|
||||
**With version 8.6.0 comes the release of directives for mermaid, a new system for modifying configurations, with the aim of establishing centralized, sane defaults and simple implementation.**
|
||||
|
||||
|
@ -990,4 +990,3 @@ By doing this clicks and tags are again allowed.
|
|||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.1.0...0.1.1)
|
||||
|
||||
## [0.1.0](https://github.com/knsv/mermaid/tree/0.1.0) (2014-11-16)
|
||||
|
||||
|
|
|
@ -3,14 +3,125 @@ sort: 1
|
|||
title: Development and Contribution
|
||||
---
|
||||
|
||||
# Development
|
||||
# Development 🙌
|
||||
|
||||
## Updating the documentation
|
||||
|
||||
Please continue writing documentation at [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs).
|
||||
So you want to help? That's great!
|
||||
|
||||
We publish documentation using GitHub Pages.
|
||||
![Image of happy people jumping with excitement](https://media.giphy.com/media/BlVnrxJgTGsUw/giphy.gif)
|
||||
|
||||
Here are a few things to know to get you started on the right path.
|
||||
|
||||
**All the documents displayed in the github.io page are listed in [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/assets/_sidebar.md)**
|
||||
|
||||
**Note:You will have to edit the document to see its contents. Commits and PR's should be directed to the develop branch.**
|
||||
|
||||
## Branching
|
||||
|
||||
Going forward we will use a **Git Flow** inspired approach to branching. So development is done in the `develop` branch.
|
||||
|
||||
Once development is done we branch a release branch from develop for testing.
|
||||
|
||||
Once the release happens we merge the release branch to master and kill the release branch.
|
||||
|
||||
This means... **you should branch off your pull request from develop** and direct all Pull Requests to it.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
We make all changes via pull requests. As we have many pull requests from developers new to mermaid, we have put in place a process, wherein *knsv, Knut Sveidqvist* is the primary reviewer of changes and merging pull requests. It is as follows:
|
||||
|
||||
* Large changes reviewed by knsv or other developer asked to review by knsv
|
||||
* Smaller low-risk changes like dependecies, documentation etc can be merged by active collaborators
|
||||
* documentation (updates to the docs folder are enocouraged and also allowed via direct commits)
|
||||
|
||||
When you commit code, create a branch, let it start with the type like feature or bug followed by the issue number for reference and text that describes the issue.
|
||||
|
||||
|
||||
**One example:**
|
||||
|
||||
`feature/945_state_diagrams`
|
||||
|
||||
**Another:**
|
||||
|
||||
`bug/123_nasty_bug_branch`
|
||||
|
||||
|
||||
## Contributing to documentation
|
||||
If it is not in the documentation, it's like it never happened. Wouldn't that be sad? With all the effort that was put into the feature?
|
||||
|
||||
The docs are located in the `docs` folder and are written in MarkDown. Just pick the right section and start typing. If you want to propose changes to the structure of the documentation:
|
||||
|
||||
**All the documents displayed in the github.io page are listed in [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/assets/_sidebar.md). Click edit it to see them.
|
||||
|
||||
The contents of http://mermaid-js.github.io/mermaid/ are based on the docs from **Master** Branch.
|
||||
|
||||
## How to Contribute to Docs
|
||||
|
||||
We are a little less strict here, it is OK to commit directly in the `develop` branch if you are a collaborator.
|
||||
|
||||
The documentation is located in the `docs` directory and organized according to relevant subfolder.
|
||||
|
||||
We encourage contributions to the documentation at [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs). We publish documentation using GitHub Pages with [jekyll-rtd-theme](https://github.com/rundocs/jekyll-rtd-theme).
|
||||
|
||||
## Preview
|
||||
If you want to preview the documentation site on your machine, you will need to install [Ruby development environment](https://jekyllrb.com/docs/installation/):
|
||||
|
||||
1. Install a full [Ruby development environment](https://jekyllrb.com/docs/installation/)
|
||||
2. Change into docs directory
|
||||
```sh
|
||||
make
|
||||
```
|
||||
3. Build the site and make it available on a local server
|
||||
```sh
|
||||
make server
|
||||
```
|
||||
4. Browse to [http://localhost:4000/mermaid/](http://localhost:4000/mermaid/)
|
||||
|
||||
### **Add unit tests for the parsing part**
|
||||
|
||||
This is important so that, if someone else does a change to the grammar that does not know about this great feature, gets notified early on when that change breaks the parser. Another important aspect is that without proper parsing tests refactoring is pretty much impossible.
|
||||
|
||||
### **Add e2e tests**
|
||||
|
||||
This tests the rendering and visual apearance of the diagram. This ensures that the rendering of that feature in the e2e will be reviewed in the release process going forward. Less chance that it breaks!
|
||||
|
||||
To start working with the e2e tests, run `yarn dev` to start the dev server, after that start cypress by running `cypress open` in the mermaid folder. (Make sure you have path to cypress in order, the binary is located in node_modules/.bin).
|
||||
|
||||
The rendering tests are very straightforward to create. There is a function imgSnapshotTest. This function takes a diagram in text form, the mermaid options and renders that diagram in cypress.
|
||||
|
||||
When running in ci it will take a snapshot of the rendered diagram and compare it with the snapshot from last build and flag for review it if it differs.
|
||||
|
||||
This is what a rendering test looks like:
|
||||
```
|
||||
it('should render forks and joins', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
state fork_state <<fork>>
|
||||
[*] --> fork_state
|
||||
fork_state --> State2
|
||||
fork_state --> State3
|
||||
|
||||
state join_state <<join>>
|
||||
State2 --> join_state
|
||||
State3 --> join_state
|
||||
join_state --> State4
|
||||
State4 --> [*]
|
||||
`,
|
||||
{ logLevel: 0 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### **Docs or it Didn't Happen**
|
||||
|
||||
Finally, if it is not in the documentation, no one will know about it and then **no one will use it**. Wouldn't that be sad? With all the effort that was put into the feature?
|
||||
|
||||
The docs are located in the docs folder and are ofc written in markdown. Just pick the right section and start typing. If you want to add to the structure as in adding a new section and new file you do that via the _navbar.md.
|
||||
|
||||
The changes in master is reflected in http://mermaid-js.github.io/mermaid/ once released the updates are commited to https://mermaid-js.github.io/#/
|
||||
|
||||
### Questions and/or suggestions ?
|
||||
After logging in at [GitHub.com](https://www.github.com), open or append to an issue [using the GitHub issue tracker of the mermaid-js repository](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Area%3A+Documentation%22).
|
||||
|
@ -33,115 +144,11 @@ If you don't have such editor on your computer, you may follow these steps:
|
|||
* Submit your changes by clicking the button "Propose file change" at the bottom (by automatic creation of a fork and a new branch).
|
||||
* Create a pull request of your newly forked branch, by clicking the green "Create pull request" button.
|
||||
|
||||
## Last words
|
||||
|
||||
## How to add a new diagram type
|
||||
Don't get daunted if it is hard in the beginning. We have a great community with only encouraging words. So if you get stuck, ask for help and hints in the slack forum. If you want to show off something good, show it off there.
|
||||
|
||||
[Join our slack community if you want closer contact!](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
|
||||
|
||||
### Step 1: Grammar & Parsing
|
||||
|
||||
|
||||
#### Grammar
|
||||
|
||||
This would be to define a jison grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
|
||||
|
||||
For instance:
|
||||
|
||||
* the flowchart starts with the keyword graph.
|
||||
* the sequence diagram starts with the keyword sequenceDiagram
|
||||
|
||||
|
||||
#### Store data found during parsing
|
||||
|
||||
There are some jison specific sub steps here where the parser stores the data encountered when parsing the diagram, this data is later used by the renderer. You can during the parsing call a object provided to the parser by the user of the parser. This object can be called during parsing for storing data.
|
||||
|
||||
```
|
||||
statement
|
||||
: 'participant' actor { $$='actor'; }
|
||||
| signal { $$='signal'; }
|
||||
| note_statement { $$='note'; }
|
||||
| 'title' message { yy.setTitle($2); }
|
||||
;
|
||||
```
|
||||
|
||||
In the extract of the grammar above, it is defined that a call to the setTitle method in the data object will be done when parsing and the title keyword is encountered.
|
||||
|
||||
```tip
|
||||
Make sure that the `parseError` function for the parser is defined and calling `mermaidPAI.parseError`. This way a common way of detecting parse errors is provided for the end-user.
|
||||
```
|
||||
|
||||
For more info look in the example diagram type:
|
||||
|
||||
The `yy` object has the following function:
|
||||
|
||||
```javascript
|
||||
exports.parseError = function(err, hash){
|
||||
mermaidAPI.parseError(err, hash)
|
||||
};
|
||||
```
|
||||
|
||||
when parsing the `yy` object is initialized as per below:
|
||||
|
||||
```javascript
|
||||
var parser
|
||||
parser = exampleParser.parser
|
||||
parser.yy = db
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Rendering
|
||||
|
||||
Write a renderer that given the data found during parsing renders the diagram. To look at an example look at sequendeRenderer.js rather then the flowchart renderer as this is a more generic example.
|
||||
|
||||
Place the renderer in the diagram folder.
|
||||
|
||||
|
||||
### Step 3: Detection of the new diagram type
|
||||
|
||||
The second thing to do is to add the capability to detect the new new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
|
||||
|
||||
|
||||
### Step 4: The final piece - triggering the rendering
|
||||
|
||||
At this point when mermaid is trying to render the diagram, it will detect it as being of the new type but there will be no match when trying to render the diagram. To fix this add a new case in the switch statement in main.js:init this should match the diagram type returned from step #2. The code in this new case statement should call the renderer for the diagram type with the data found by the parser as an argument.
|
||||
|
||||
|
||||
## Usage of the parser as a separate module
|
||||
|
||||
|
||||
### Setup
|
||||
|
||||
```javascript
|
||||
var graph = require('./graphDb')
|
||||
var flow = require('./parser/flow')
|
||||
flow.parser.yy = graph
|
||||
```
|
||||
|
||||
|
||||
### Parsing
|
||||
|
||||
```javascript
|
||||
flow.parser.parse(text)
|
||||
```
|
||||
|
||||
|
||||
### Data extraction
|
||||
|
||||
```javascript
|
||||
graph.getDirection()
|
||||
graph.getVertices()
|
||||
graph.getEdges()
|
||||
```
|
||||
|
||||
The parser is also exposed in the mermaid api by calling:
|
||||
|
||||
```javascript
|
||||
var parser = mermaid.getParser()
|
||||
```
|
||||
|
||||
Note that the parse needs a graph object to store the data as per:
|
||||
|
||||
```javascript
|
||||
flow.parser.yy = graph
|
||||
```
|
||||
|
||||
Look at `graphDb.js` for more details on that object.
|
||||
![Image of superhero wishing you good luck](https://media.giphy.com/media/l49JHz7kJvl6MCj3G/giphy.gif)
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
sort: 2
|
||||
title: Adding a New Diagram/Chart
|
||||
---
|
||||
|
||||
# Adding a New Diagram/Chart 📊
|
||||
|
||||
|
||||
### Step 1: Grammar & Parsing
|
||||
|
||||
|
||||
#### Grammar
|
||||
|
||||
This would be to define a jison grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
|
||||
|
||||
For instance:
|
||||
|
||||
* the flowchart starts with the keyword graph.
|
||||
* the sequence diagram starts with the keyword sequenceDiagram
|
||||
|
||||
|
||||
#### Store data found during parsing
|
||||
|
||||
There are some jison specific sub steps here where the parser stores the data encountered when parsing the diagram, this data is later used by the renderer. You can during the parsing call a object provided to the parser by the user of the parser. This object can be called during parsing for storing data.
|
||||
|
||||
```
|
||||
statement
|
||||
: 'participant' actor { $$='actor'; }
|
||||
| signal { $$='signal'; }
|
||||
| note_statement { $$='note'; }
|
||||
| 'title' message { yy.setTitle($2); }
|
||||
;
|
||||
```
|
||||
|
||||
In the extract of the grammar above, it is defined that a call to the setTitle method in the data object will be done when parsing and the title keyword is encountered.
|
||||
|
||||
```tip
|
||||
Make sure that the `parseError` function for the parser is defined and calling `mermaidPAI.parseError`. This way a common way of detecting parse errors is provided for the end-user.
|
||||
```
|
||||
|
||||
For more info look in the example diagram type:
|
||||
|
||||
The `yy` object has the following function:
|
||||
|
||||
```javascript
|
||||
exports.parseError = function(err, hash){
|
||||
mermaidAPI.parseError(err, hash)
|
||||
};
|
||||
```
|
||||
|
||||
when parsing the `yy` object is initialized as per below:
|
||||
|
||||
```javascript
|
||||
var parser
|
||||
parser = exampleParser.parser
|
||||
parser.yy = db
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Rendering
|
||||
|
||||
Write a renderer that given the data found during parsing renders the diagram. To look at an example look at sequendeRenderer.js rather then the flowchart renderer as this is a more generic example.
|
||||
|
||||
Place the renderer in the diagram folder.
|
||||
|
||||
|
||||
### Step 3: Detection of the new diagram type
|
||||
|
||||
The second thing to do is to add the capability to detect the new new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
|
||||
|
||||
|
||||
### Step 4: The final piece - triggering the rendering
|
||||
|
||||
At this point when mermaid is trying to render the diagram, it will detect it as being of the new type but there will be no match when trying to render the diagram. To fix this add a new case in the switch statement in main.js:init this should match the diagram type returned from step #2. The code in this new case statement should call the renderer for the diagram type with the data found by the parser as an argument.
|
||||
|
||||
|
||||
## Usage of the parser as a separate module
|
||||
|
||||
|
||||
### Setup
|
||||
|
||||
```javascript
|
||||
var graph = require('./graphDb')
|
||||
var flow = require('./parser/flow')
|
||||
flow.parser.yy = graph
|
||||
```
|
||||
|
||||
|
||||
### Parsing
|
||||
|
||||
```javascript
|
||||
flow.parser.parse(text)
|
||||
```
|
||||
|
||||
|
||||
### Data extraction
|
||||
|
||||
```javascript
|
||||
graph.getDirection()
|
||||
graph.getVertices()
|
||||
graph.getEdges()
|
||||
```
|
||||
|
||||
The parser is also exposed in the mermaid api by calling:
|
||||
|
||||
```javascript
|
||||
var parser = mermaid.getParser()
|
||||
```
|
||||
|
||||
Note that the parse needs a graph object to store the data as per:
|
||||
|
||||
```javascript
|
||||
flow.parser.yy = graph
|
||||
```
|
||||
|
||||
Look at `graphDb.js` for more details on that object.
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mermaid",
|
||||
"version": "8.8.0",
|
||||
"version": "8.8.2",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid.core.js",
|
||||
"keywords": [
|
||||
|
@ -28,7 +28,7 @@
|
|||
"dev": "webpack-dev-server --config webpack.config.e2e.js",
|
||||
"test": "yarn lint && jest src/.*",
|
||||
"test:watch": "jest --watch src",
|
||||
"prepublishOnly": "yarn build && yarn test && yarn e2e",
|
||||
"prepublishOnly": "yarn build && yarn test",
|
||||
"prepare": "yarn build"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
@ -6,9 +6,11 @@ import utils from '../utils';
|
|||
// import { calcLabelPosition } from '../utils';
|
||||
|
||||
let edgeLabels = {};
|
||||
let terminalLabels = {};
|
||||
|
||||
export const clear = () => {
|
||||
edgeLabels = {};
|
||||
terminalLabels = {};
|
||||
};
|
||||
|
||||
export const insertEdgeLabel = (elem, edge) => {
|
||||
|
@ -39,17 +41,129 @@ export const insertEdgeLabel = (elem, edge) => {
|
|||
// Update the abstract data of the edge with the new information about its width and height
|
||||
edge.width = bbox.width;
|
||||
edge.height = bbox.height;
|
||||
|
||||
if (edge.startLabelLeft) {
|
||||
// Create the actual text element
|
||||
const startLabelElement = createLabel(edge.startLabelLeft, edge.labelStyle);
|
||||
const startEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
|
||||
const inner = startEdgeLabelLeft.insert('g').attr('class', 'inner');
|
||||
inner.node().appendChild(startLabelElement);
|
||||
const slBox = startLabelElement.getBBox();
|
||||
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
|
||||
if (!terminalLabels[edge.id]) {
|
||||
terminalLabels[edge.id] = {};
|
||||
}
|
||||
terminalLabels[edge.id].startLeft = startEdgeLabelLeft;
|
||||
}
|
||||
if (edge.startLabelRight) {
|
||||
// Create the actual text element
|
||||
const startLabelElement = createLabel(edge.startLabelRight, edge.labelStyle);
|
||||
const startEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
|
||||
const inner = startEdgeLabelRight.insert('g').attr('class', 'inner');
|
||||
startEdgeLabelRight.node().appendChild(startLabelElement);
|
||||
inner.node().appendChild(startLabelElement);
|
||||
const slBox = startLabelElement.getBBox();
|
||||
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
|
||||
|
||||
if (!terminalLabels[edge.id]) {
|
||||
terminalLabels[edge.id] = {};
|
||||
}
|
||||
terminalLabels[edge.id].startRight = startEdgeLabelRight;
|
||||
}
|
||||
if (edge.endLabelLeft) {
|
||||
// Create the actual text element
|
||||
const endLabelElement = createLabel(edge.endLabelLeft, edge.labelStyle);
|
||||
const endEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
|
||||
const inner = endEdgeLabelLeft.insert('g').attr('class', 'inner');
|
||||
inner.node().appendChild(endLabelElement);
|
||||
const slBox = endLabelElement.getBBox();
|
||||
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
|
||||
|
||||
endEdgeLabelLeft.node().appendChild(endLabelElement);
|
||||
if (!terminalLabels[edge.id]) {
|
||||
terminalLabels[edge.id] = {};
|
||||
}
|
||||
terminalLabels[edge.id].endLeft = endEdgeLabelLeft;
|
||||
}
|
||||
if (edge.endLabelRight) {
|
||||
// Create the actual text element
|
||||
const endLabelElement = createLabel(edge.endLabelRight, edge.labelStyle);
|
||||
const endEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
|
||||
const inner = endEdgeLabelRight.insert('g').attr('class', 'inner');
|
||||
|
||||
inner.node().appendChild(endLabelElement);
|
||||
const slBox = endLabelElement.getBBox();
|
||||
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
|
||||
|
||||
endEdgeLabelRight.node().appendChild(endLabelElement);
|
||||
if (!terminalLabels[edge.id]) {
|
||||
terminalLabels[edge.id] = {};
|
||||
}
|
||||
terminalLabels[edge.id].endRight = endEdgeLabelRight;
|
||||
}
|
||||
};
|
||||
|
||||
export const positionEdgeLabel = (edge, points) => {
|
||||
export const positionEdgeLabel = (edge, paths) => {
|
||||
logger.info('Moving label', edge.id, edge.label, edgeLabels[edge.id]);
|
||||
let path = paths.updatedPath ? paths.updatedPath : paths.originalPath;
|
||||
if (edge.label) {
|
||||
const el = edgeLabels[edge.id];
|
||||
let x = edge.x;
|
||||
let y = edge.y;
|
||||
if (points) {
|
||||
if (path) {
|
||||
// // debugger;
|
||||
const pos = utils.calcLabelPosition(path);
|
||||
logger.info('Moving label from (', x, ',', y, ') to (', pos.x, ',', pos.y, ')');
|
||||
// x = pos.x;
|
||||
// y = pos.y;
|
||||
}
|
||||
el.attr('transform', 'translate(' + x + ', ' + y + ')');
|
||||
}
|
||||
|
||||
//let path = paths.updatedPath ? paths.updatedPath : paths.originalPath;
|
||||
if (edge.startLabelLeft) {
|
||||
const el = terminalLabels[edge.id].startLeft;
|
||||
let x = edge.x;
|
||||
let y = edge.y;
|
||||
if (path) {
|
||||
// debugger;
|
||||
const pos = utils.calcLabelPosition(points);
|
||||
const pos = utils.calcTerminalLabelPosition(0, 'start_left', path);
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
}
|
||||
el.attr('transform', 'translate(' + x + ', ' + y + ')');
|
||||
}
|
||||
if (edge.startLabelRight) {
|
||||
const el = terminalLabels[edge.id].startRight;
|
||||
let x = edge.x;
|
||||
let y = edge.y;
|
||||
if (path) {
|
||||
// debugger;
|
||||
const pos = utils.calcTerminalLabelPosition(0, 'start_right', path);
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
}
|
||||
el.attr('transform', 'translate(' + x + ', ' + y + ')');
|
||||
}
|
||||
if (edge.endLabelLeft) {
|
||||
const el = terminalLabels[edge.id].endLeft;
|
||||
let x = edge.x;
|
||||
let y = edge.y;
|
||||
if (path) {
|
||||
// debugger;
|
||||
const pos = utils.calcTerminalLabelPosition(0, 'end_left', path);
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
}
|
||||
el.attr('transform', 'translate(' + x + ', ' + y + ')');
|
||||
}
|
||||
if (edge.endLabelRight) {
|
||||
const el = terminalLabels[edge.id].endRight;
|
||||
let x = edge.x;
|
||||
let y = edge.y;
|
||||
if (path) {
|
||||
// debugger;
|
||||
const pos = utils.calcTerminalLabelPosition(0, 'end_right', path);
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
}
|
||||
|
@ -354,8 +468,10 @@ export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph)
|
|||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
let paths = {};
|
||||
if (pointsHasChanged) {
|
||||
return points;
|
||||
paths.updatedPath = points;
|
||||
}
|
||||
paths.originalPath = edge.points;
|
||||
return paths;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
clear as clearGraphlib,
|
||||
clusterDb,
|
||||
adjustClustersAndEdges,
|
||||
findNonClusterChild
|
||||
findNonClusterChild,
|
||||
sortNodesByHierarchy
|
||||
} from './mermaid-graphlib';
|
||||
import { insertNode, positionNode, clear as clearNodes, setNodeElem } from './nodes';
|
||||
import { insertCluster, clear as clearClusters } from './clusters';
|
||||
|
@ -14,7 +15,7 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f
|
|||
import { logger as log } from '../logger';
|
||||
|
||||
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
log.info('Graph in recursive render:', graphlib.json.write(graph), parentCluster);
|
||||
log.info('Graph in recursive render: XXX', graphlib.json.write(graph), parentCluster);
|
||||
const dir = graph.graph().rankdir;
|
||||
log.warn('Dir in recursive render - dir:', dir);
|
||||
|
||||
|
@ -22,7 +23,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||
if (!graph.nodes()) {
|
||||
log.info('No nodes found for', graph);
|
||||
} else {
|
||||
log.info('Recursive render', graph.nodes());
|
||||
log.info('Recursive render XXX', graph.nodes());
|
||||
}
|
||||
if (graph.edges().length > 0) {
|
||||
log.info('Recursive edges', graph.edge(graph.edges()[0]));
|
||||
|
@ -39,11 +40,14 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||
if (typeof parentCluster !== 'undefined') {
|
||||
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
|
||||
// data.clusterPositioning = true;
|
||||
log.info('Setting data for cluster', data);
|
||||
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
|
||||
graph.setNode(parentCluster.id, data);
|
||||
graph.setParent(v, parentCluster.id, data);
|
||||
if (!graph.parent(v)) {
|
||||
log.warn('Setting parent', v, parentCluster.id);
|
||||
graph.setParent(v, parentCluster.id, data);
|
||||
}
|
||||
}
|
||||
log.info('(Insert) Node ' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||
log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||
if (node && node.clusterNode) {
|
||||
// const children = graph.children(v);
|
||||
log.info('Cluster identified', v, node, graph.node(v));
|
||||
|
@ -56,7 +60,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||
if (graph.children(v).length > 0) {
|
||||
// This is a cluster but not to be rendered recusively
|
||||
// Render as before
|
||||
log.info('Cluster - the non recursive path', v, node.id, node, graph);
|
||||
log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
|
||||
log.info(findNonClusterChild(node.id, graph));
|
||||
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
|
||||
// insertCluster(clusters, graph.node(v));
|
||||
|
@ -91,7 +95,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||
dagre.layout(graph);
|
||||
log.info('Graph after layout:', graphlib.json.write(graph));
|
||||
// Move the nodes to the correct place
|
||||
graph.nodes().forEach(function(v) {
|
||||
sortNodesByHierarchy(graph).forEach(function(v) {
|
||||
const node = graph.node(v);
|
||||
log.info('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||
log.info(
|
||||
|
@ -124,8 +128,8 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||
const edge = graph.edge(e);
|
||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
||||
|
||||
const updatedPath = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
|
||||
positionEdgeLabel(edge, updatedPath);
|
||||
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
|
||||
positionEdgeLabel(edge, paths);
|
||||
});
|
||||
|
||||
return elem;
|
||||
|
@ -138,10 +142,10 @@ export const render = (elem, graph, markers, diagramtype, id) => {
|
|||
clearClusters();
|
||||
clearGraphlib();
|
||||
|
||||
log.warn('Graph before:', graphlib.json.write(graph));
|
||||
log.warn('Graph at first:', graphlib.json.write(graph));
|
||||
adjustClustersAndEdges(graph);
|
||||
log.warn('Graph after:', graphlib.json.write(graph));
|
||||
log.warn('Graph ever after:', graph.graph());
|
||||
// log.warn('Graph ever after:', graphlib.json.write(graph.node('A').graph));
|
||||
recursiveRender(elem, graph, diagramtype);
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ const edgeInCluster = (edge, clusterId) => {
|
|||
};
|
||||
|
||||
const copy = (clusterId, graph, newGraph, rootId) => {
|
||||
log.info(
|
||||
log.warn(
|
||||
'Copying children of ',
|
||||
clusterId,
|
||||
'root',
|
||||
|
@ -68,7 +68,7 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
|||
nodes.push(clusterId);
|
||||
}
|
||||
|
||||
log.debug('Copying (nodes) clusterId', clusterId, 'nodes', nodes);
|
||||
log.warn('Copying (nodes) clusterId', clusterId, 'nodes', nodes);
|
||||
|
||||
nodes.forEach(node => {
|
||||
if (graph.children(node).length > 0) {
|
||||
|
@ -77,8 +77,8 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
|||
const data = graph.node(node);
|
||||
log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); //,node, data, ' parent is ', clusterId);
|
||||
newGraph.setNode(node, data);
|
||||
log.debug('Setting parent', node, graph.parent(node));
|
||||
if (rootId !== graph.parent(node)) {
|
||||
log.warn('Setting parent', node, graph.parent(node));
|
||||
newGraph.setParent(node, graph.parent(node));
|
||||
}
|
||||
|
||||
|
@ -245,40 +245,51 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||
|
||||
// d1 xor d2 - if either d1 is true and d2 is false or the other way around
|
||||
if (d1 ^ d2) {
|
||||
log.debug('Edge: ', edge, ' leaves cluster ', id);
|
||||
log.debug('Decendants of ', id, ': ', decendants[id]);
|
||||
log.warn('Edge: ', edge, ' leaves cluster ', id);
|
||||
log.warn('Decendants of XXX ', id, ': ', decendants[id]);
|
||||
clusterDb[id].externalConnections = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.debug('Not a cluster ', id, decendants);
|
||||
}
|
||||
});
|
||||
|
||||
extractor(graph, 0);
|
||||
|
||||
// For clusters with incoming and/or outgoing edges translate those edges to a real node
|
||||
// in the cluster inorder to fake the edge
|
||||
graph.edges().forEach(function(e) {
|
||||
const edge = graph.edge(e);
|
||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
|
||||
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
|
||||
|
||||
let v = e.v;
|
||||
let w = e.w;
|
||||
// Check if link is either from or to a cluster
|
||||
log.trace('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
|
||||
log.warn(
|
||||
'Fix XXX',
|
||||
clusterDb,
|
||||
'ids:',
|
||||
e.v,
|
||||
e.w,
|
||||
'Translateing: ',
|
||||
clusterDb[e.v],
|
||||
' --- ',
|
||||
clusterDb[e.w]
|
||||
);
|
||||
if (clusterDb[e.v] || clusterDb[e.w]) {
|
||||
log.warn('Fixing and trixing - removing', e.v, e.w, e.name);
|
||||
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
||||
v = getAnchorId(e.v);
|
||||
w = getAnchorId(e.w);
|
||||
graph.removeEdge(e.v, e.w, e.name);
|
||||
if (v !== e.v) edge.fromCluster = e.v;
|
||||
if (w !== e.w) edge.toCluster = e.w;
|
||||
log.warn('Replacing with', v, w, e.name);
|
||||
log.warn('Fix Replacing with XXX', v, w, e.name);
|
||||
graph.setEdge(v, w, edge, e.name);
|
||||
}
|
||||
});
|
||||
log.warn('Adjusted Graph', graphlib.json.write(graph));
|
||||
extractor(graph, 0);
|
||||
|
||||
log.trace(clusterDb);
|
||||
|
||||
|
@ -291,7 +302,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||
};
|
||||
|
||||
export const extractor = (graph, depth) => {
|
||||
log.debug('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
|
||||
log.warn('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
|
||||
if (depth > 10) {
|
||||
log.error('Bailing out');
|
||||
return;
|
||||
|
@ -336,11 +347,11 @@ export const extractor = (graph, depth) => {
|
|||
// break;
|
||||
} else if (
|
||||
!clusterDb[node].externalConnections &&
|
||||
!graph.parent(node) &&
|
||||
// !graph.parent(node) &&
|
||||
graph.children(node) &&
|
||||
graph.children(node).length > 0
|
||||
) {
|
||||
log.debug(
|
||||
log.warn(
|
||||
'Cluster without external connections, without a parent and with children',
|
||||
node,
|
||||
depth
|
||||
|
@ -364,7 +375,7 @@ export const extractor = (graph, depth) => {
|
|||
return {};
|
||||
});
|
||||
|
||||
log.debug('Old graph before copy', graphlib.json.write(graph));
|
||||
log.warn('Old graph before copy', graphlib.json.write(graph));
|
||||
copy(node, graph, clusterGraph, node);
|
||||
graph.setNode(node, {
|
||||
clusterNode: true,
|
||||
|
@ -373,10 +384,10 @@ export const extractor = (graph, depth) => {
|
|||
labelText: clusterDb[node].labelText,
|
||||
graph: clusterGraph
|
||||
});
|
||||
log.debug('New graph after copy', graphlib.json.write(clusterGraph));
|
||||
log.warn('New graph after copy node: (', node, ')', graphlib.json.write(clusterGraph));
|
||||
log.debug('Old graph after copy', graphlib.json.write(graph));
|
||||
} else {
|
||||
log.debug(
|
||||
log.warn(
|
||||
'Cluster ** ',
|
||||
node,
|
||||
' **not meeting the criteria !externalConnections:',
|
||||
|
@ -393,13 +404,27 @@ export const extractor = (graph, depth) => {
|
|||
}
|
||||
|
||||
nodes = graph.nodes();
|
||||
log.debug('New list of nodes', nodes);
|
||||
log.warn('New list of nodes', nodes);
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
const data = graph.node(node);
|
||||
log.debug(' Now next leveö', node, data);
|
||||
log.warn(' Now next level', node, data);
|
||||
if (data.clusterNode) {
|
||||
extractor(data.graph, depth + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sorter = (graph, nodes) => {
|
||||
if (nodes.length === 0) return [];
|
||||
let result = Object.assign(nodes);
|
||||
nodes.forEach(node => {
|
||||
const children = graph.children(node);
|
||||
const sorted = sorter(graph, children);
|
||||
result = result.concat(sorted);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const sortNodesByHierarchy = graph => sorter(graph, graph.children());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import graphlib from 'graphlib';
|
||||
import dagre from 'dagre';
|
||||
import { validate, adjustClustersAndEdges, extractDecendants } from './mermaid-graphlib';
|
||||
import { validate, adjustClustersAndEdges, extractDecendants, sortNodesByHierarchy } from './mermaid-graphlib';
|
||||
import { setLogLevel, logger } from '../logger';
|
||||
|
||||
describe('Graphlib decorations', () => {
|
||||
|
@ -43,8 +43,6 @@ describe('Graphlib decorations', () => {
|
|||
g.setEdge('a', 'b');
|
||||
g.setEdge('C1', 'C2');
|
||||
|
||||
console.log(g.nodes())
|
||||
|
||||
expect(validate(g)).toBe(false);
|
||||
});
|
||||
it('Validate should not detect edges between clusters after adjustment', function () {
|
||||
|
@ -66,7 +64,6 @@ describe('Graphlib decorations', () => {
|
|||
g.setEdge('a', 'b');
|
||||
g.setEdge('C1', 'C2');
|
||||
|
||||
console.log(g.nodes())
|
||||
adjustClustersAndEdges(g);
|
||||
logger.info(g.edges())
|
||||
expect(validate(g)).toBe(true);
|
||||
|
@ -373,6 +370,31 @@ describe('Graphlib decorations', () => {
|
|||
});
|
||||
|
||||
});
|
||||
it('adjustClustersAndEdges should handle nesting GLB77', function () {
|
||||
/*
|
||||
flowchart TB
|
||||
subgraph A
|
||||
b-->B
|
||||
a-->c
|
||||
end
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
*/
|
||||
|
||||
const exportedGraph = JSON.parse('{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"class":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","classes":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","classes":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}');
|
||||
const gr = graphlib.json.read(exportedGraph)
|
||||
|
||||
logger.info('Graph before', graphlib.json.write(gr))
|
||||
adjustClustersAndEdges(gr);
|
||||
const aGraph = gr.node('A').graph;
|
||||
const bGraph = aGraph.node('B').graph;
|
||||
logger.info('Graph after', graphlib.json.write(aGraph));
|
||||
// logger.trace('Graph after', graphlib.json.write(g))
|
||||
expect(aGraph.parent('c')).toBe('B');
|
||||
expect(aGraph.parent('B')).toBe(undefined);
|
||||
});
|
||||
|
||||
});
|
||||
describe('extractDecendants', function () {
|
||||
let g;
|
||||
|
@ -426,3 +448,57 @@ describe('extractDecendants', function () {
|
|||
expect(d3).toEqual(['c']);
|
||||
});
|
||||
});
|
||||
describe('sortNodesByHierarchy', function () {
|
||||
let g;
|
||||
beforeEach(function () {
|
||||
setLogLevel(1);
|
||||
g = new graphlib.Graph({
|
||||
multigraph: true,
|
||||
compound: true
|
||||
});
|
||||
g.setGraph({
|
||||
rankdir: 'TB',
|
||||
nodesep: 10,
|
||||
ranksep: 10,
|
||||
marginx: 8,
|
||||
marginy: 8
|
||||
});
|
||||
g.setDefaultEdgeLabel(function () {
|
||||
return {};
|
||||
});
|
||||
});
|
||||
it('it should sort proper en nodes are in reverse order', function () {
|
||||
/*
|
||||
a -->b
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
subgraph A
|
||||
B
|
||||
end
|
||||
*/
|
||||
g.setNode('a', { data: 1 });
|
||||
g.setNode('b', { data: 2 });
|
||||
g.setParent('b', 'B');
|
||||
g.setParent('B', 'A');
|
||||
g.setEdge('a', 'b', '1');
|
||||
expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']);
|
||||
});
|
||||
it('it should sort proper en nodes are in correct order', function () {
|
||||
/*
|
||||
a -->b
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
subgraph A
|
||||
B
|
||||
end
|
||||
*/
|
||||
g.setNode('a', { data: 1 });
|
||||
g.setParent('B', 'A');
|
||||
g.setParent('b', 'B');
|
||||
g.setNode('b', { data: 2 });
|
||||
g.setEdge('a', 'b', '1');
|
||||
expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@ import { getConfig } from '../config';
|
|||
import intersect from './intersect/index.js';
|
||||
import createLabel from './createLabel';
|
||||
import note from './shapes/note';
|
||||
import { parseMember } from '../diagrams/class/svgDraw';
|
||||
|
||||
const question = (parent, node) => {
|
||||
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
|
||||
|
@ -260,6 +261,7 @@ const rect = (parent, node) => {
|
|||
|
||||
rect
|
||||
.attr('class', 'basic label-container')
|
||||
.attr('style', node.style)
|
||||
.attr('rx', node.rx)
|
||||
.attr('ry', node.ry)
|
||||
.attr('x', -bbox.width / 2 - halfPadding)
|
||||
|
@ -288,7 +290,7 @@ const rectWithTitle = (parent, node) => {
|
|||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', classes)
|
||||
.attr('id', node.id);
|
||||
.attr('id', node.domId || node.id);
|
||||
|
||||
// Create the title label and insert it after the rect
|
||||
const rect = shapeSvg.insert('rect', ':first-child');
|
||||
|
@ -457,7 +459,7 @@ const start = (parent, node) => {
|
|||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', 'node default')
|
||||
.attr('id', node.id);
|
||||
.attr('id', node.domId || node.id);
|
||||
const circle = shapeSvg.insert('circle', ':first-child');
|
||||
|
||||
// center the circle around its coordinate
|
||||
|
@ -480,7 +482,7 @@ const forkJoin = (parent, node, dir) => {
|
|||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', 'node default')
|
||||
.attr('id', node.id);
|
||||
.attr('id', node.domId || node.id);
|
||||
|
||||
let width = 70;
|
||||
let height = 10;
|
||||
|
@ -514,7 +516,7 @@ const end = (parent, node) => {
|
|||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', 'node default')
|
||||
.attr('id', node.id);
|
||||
.attr('id', node.domId || node.id);
|
||||
const innerCircle = shapeSvg.insert('circle', ':first-child');
|
||||
const circle = shapeSvg.insert('circle', ':first-child');
|
||||
|
||||
|
@ -554,7 +556,7 @@ const class_box = (parent, node) => {
|
|||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', classes)
|
||||
.attr('id', node.id);
|
||||
.attr('id', node.domId || node.id);
|
||||
|
||||
// Create the title label and insert it after the rect
|
||||
const rect = shapeSvg.insert('rect', ':first-child');
|
||||
|
@ -568,27 +570,60 @@ const class_box = (parent, node) => {
|
|||
const hasInterface = node.classData.annotations && node.classData.annotations[0];
|
||||
|
||||
// 1. Create the labels
|
||||
const interfaceLabelText = node.classData.annotations[0]
|
||||
? '«' + node.classData.annotations[0] + '»'
|
||||
: '';
|
||||
const interfaceLabel = labelContainer
|
||||
.node()
|
||||
.appendChild(createLabel(node.classData.annotations[0], node.labelStyle, true, true));
|
||||
const interfaceBBox = interfaceLabel.getBBox();
|
||||
.appendChild(createLabel(interfaceLabelText, node.labelStyle, true, true));
|
||||
let interfaceBBox = interfaceLabel.getBBox();
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
const div = interfaceLabel.children[0];
|
||||
const dv = select(interfaceLabel);
|
||||
interfaceBBox = div.getBoundingClientRect();
|
||||
dv.attr('width', interfaceBBox.width);
|
||||
dv.attr('height', interfaceBBox.height);
|
||||
}
|
||||
if (node.classData.annotations[0]) {
|
||||
maxHeight += interfaceBBox.height + rowPadding;
|
||||
maxWidth += interfaceBBox.width;
|
||||
}
|
||||
|
||||
let classTitleString = node.classData.id;
|
||||
|
||||
if (node.classData.type !== undefined && node.classData.type !== '') {
|
||||
classTitleString += '<' + node.classData.type + '>';
|
||||
}
|
||||
const classTitleLabel = labelContainer
|
||||
.node()
|
||||
.appendChild(createLabel(node.labelText, node.labelStyle, true, true));
|
||||
const classTitleBBox = classTitleLabel.getBBox();
|
||||
.appendChild(createLabel(classTitleString, node.labelStyle, true, true));
|
||||
select(classTitleLabel).attr('class', 'classTitle');
|
||||
let classTitleBBox = classTitleLabel.getBBox();
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
const div = classTitleLabel.children[0];
|
||||
const dv = select(classTitleLabel);
|
||||
classTitleBBox = div.getBoundingClientRect();
|
||||
dv.attr('width', classTitleBBox.width);
|
||||
dv.attr('height', classTitleBBox.height);
|
||||
}
|
||||
maxHeight += classTitleBBox.height + rowPadding;
|
||||
if (classTitleBBox.width > maxWidth) {
|
||||
maxWidth = classTitleBBox.width;
|
||||
}
|
||||
const classAttributes = [];
|
||||
node.classData.members.forEach(str => {
|
||||
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
|
||||
const bbox = lbl.getBBox();
|
||||
const parsedText = parseMember(str).displayText;
|
||||
const lbl = labelContainer
|
||||
.node()
|
||||
.appendChild(createLabel(parsedText, node.labelStyle, true, true));
|
||||
let bbox = lbl.getBBox();
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
const div = lbl.children[0];
|
||||
const dv = select(lbl);
|
||||
bbox = div.getBoundingClientRect();
|
||||
dv.attr('width', bbox.width);
|
||||
dv.attr('height', bbox.height);
|
||||
}
|
||||
if (bbox.width > maxWidth) {
|
||||
maxWidth = bbox.width;
|
||||
}
|
||||
|
@ -596,10 +631,22 @@ const class_box = (parent, node) => {
|
|||
classAttributes.push(lbl);
|
||||
});
|
||||
|
||||
maxHeight += lineHeight;
|
||||
|
||||
const classMethods = [];
|
||||
node.classData.methods.forEach(str => {
|
||||
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
|
||||
const bbox = lbl.getBBox();
|
||||
const parsedText = parseMember(str).displayText;
|
||||
const lbl = labelContainer
|
||||
.node()
|
||||
.appendChild(createLabel(parsedText, node.labelStyle, true, true));
|
||||
let bbox = lbl.getBBox();
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
const div = lbl.children[0];
|
||||
const dv = select(lbl);
|
||||
bbox = div.getBoundingClientRect();
|
||||
dv.attr('width', bbox.width);
|
||||
dv.attr('height', bbox.height);
|
||||
}
|
||||
if (bbox.width > maxWidth) {
|
||||
maxWidth = bbox.width;
|
||||
}
|
||||
|
@ -614,13 +661,10 @@ const class_box = (parent, node) => {
|
|||
|
||||
// position the interface label
|
||||
if (hasInterface) {
|
||||
let diffX = (maxWidth - interfaceBBox.width) / 2;
|
||||
select(interfaceLabel).attr(
|
||||
'transform',
|
||||
'translate( ' +
|
||||
-(maxWidth + node.padding - interfaceBBox.width / 2) / 2 +
|
||||
', ' +
|
||||
(-1 * maxHeight) / 2 +
|
||||
')'
|
||||
'translate( ' + ((-1 * maxWidth) / 2 + diffX) + ', ' + (-1 * maxHeight) / 2 + ')'
|
||||
);
|
||||
verticalPos = interfaceBBox.height + rowPadding;
|
||||
}
|
||||
|
@ -657,6 +701,7 @@ const class_box = (parent, node) => {
|
|||
verticalPos += classTitleBBox.height + rowPadding;
|
||||
});
|
||||
|
||||
verticalPos += lineHeight;
|
||||
bottomLine
|
||||
.attr('class', 'divider')
|
||||
.attr('x1', -maxWidth / 2 - halfPadding)
|
||||
|
@ -674,14 +719,14 @@ const class_box = (parent, node) => {
|
|||
verticalPos += classTitleBBox.height + rowPadding;
|
||||
});
|
||||
//
|
||||
let bbox;
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
const div = interfaceLabel.children[0];
|
||||
const dv = select(interfaceLabel);
|
||||
bbox = div.getBoundingClientRect();
|
||||
dv.attr('width', bbox.width);
|
||||
dv.attr('height', bbox.height);
|
||||
}
|
||||
// let bbox;
|
||||
// if (getConfig().flowchart.htmlLabels) {
|
||||
// const div = interfaceLabel.children[0];
|
||||
// const dv = select(interfaceLabel);
|
||||
// bbox = div.getBoundingClientRect();
|
||||
// dv.attr('width', bbox.width);
|
||||
// dv.attr('height', bbox.height);
|
||||
// }
|
||||
// bbox = labelContainer.getBBox();
|
||||
|
||||
// logger.info('Text 2', text2);
|
||||
|
@ -778,7 +823,32 @@ const shapes = {
|
|||
let nodeElems = {};
|
||||
|
||||
export const insertNode = (elem, node, dir) => {
|
||||
nodeElems[node.id] = shapes[node.shape](elem, node, dir);
|
||||
let newEl;
|
||||
let el;
|
||||
|
||||
// Add link when appropriate
|
||||
if (node.link) {
|
||||
newEl = elem
|
||||
.insert('svg:a')
|
||||
.attr('xlink:href', node.link)
|
||||
.attr('target', node.linkTarget || '_blank');
|
||||
el = shapes[node.shape](newEl, node, dir);
|
||||
} else {
|
||||
el = shapes[node.shape](elem, node, dir);
|
||||
newEl = el;
|
||||
}
|
||||
if (node.tooltip) {
|
||||
el.attr('title', node.tooltip);
|
||||
}
|
||||
if (node.class) {
|
||||
el.attr('class', 'node default ' + node.class);
|
||||
}
|
||||
|
||||
nodeElems[node.id] = newEl;
|
||||
|
||||
if (node.haveCallback) {
|
||||
nodeElems[node.id].attr('class', nodeElems[node.id].attr('class') + ' clickable');
|
||||
}
|
||||
};
|
||||
export const setNodeElem = (elem, node) => {
|
||||
nodeElems[node.id] = elem;
|
||||
|
|
|
@ -12,10 +12,13 @@ export const labelHelper = (parent, node, _classes, isNode) => {
|
|||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', classes)
|
||||
.attr('id', node.id);
|
||||
.attr('id', node.domId || node.id);
|
||||
|
||||
// Create the label and insert it after the rect
|
||||
const label = shapeSvg.insert('g').attr('class', 'label');
|
||||
const label = shapeSvg
|
||||
.insert('g')
|
||||
.attr('class', 'label')
|
||||
.attr('style', node.labelStyle);
|
||||
|
||||
const text = label
|
||||
.node()
|
||||
|
|
|
@ -173,8 +173,20 @@ const config = {
|
|||
*/
|
||||
curve: 'linear',
|
||||
// Only used in new experimental rendering
|
||||
// repreesents the padding between the labels and the shape
|
||||
padding: 15
|
||||
// represents the padding between the labels and the shape
|
||||
padding: 15,
|
||||
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
*| useMaxWidth | See notes | Boolean | 4 | True, False |
|
||||
*
|
||||
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
|
||||
*available space if not the absolute space required is used.
|
||||
*
|
||||
***Default value true**.
|
||||
*/
|
||||
useMaxWidth: true
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -572,8 +584,23 @@ const config = {
|
|||
* This might need adjustment to match your locale and preferences
|
||||
***Default value '%Y-%m-%d'**.
|
||||
*/
|
||||
axisFormat: '%Y-%m-%d'
|
||||
axisFormat: '%Y-%m-%d',
|
||||
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
*| useMaxWidth | See notes | Boolean | 4 | True, False |
|
||||
*
|
||||
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
|
||||
*available space if not the absolute space required is used.
|
||||
*
|
||||
***Default value true**.
|
||||
*/
|
||||
useMaxWidth: true,
|
||||
|
||||
useWidth: undefined
|
||||
},
|
||||
|
||||
/**
|
||||
* The object containing configurations specific for journey diagrams
|
||||
*/
|
||||
|
@ -711,10 +738,35 @@ const config = {
|
|||
rightAngles: false
|
||||
},
|
||||
class: {
|
||||
arrowMarkerAbsolute: false
|
||||
arrowMarkerAbsolute: false,
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
*| useMaxWidth | See notes | Boolean | 4 | True, False |
|
||||
*
|
||||
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
|
||||
*available space if not the absolute space required is used.
|
||||
*
|
||||
***Default value true**.
|
||||
*/
|
||||
useMaxWidth: true
|
||||
},
|
||||
git: {
|
||||
arrowMarkerAbsolute: false
|
||||
arrowMarkerAbsolute: false,
|
||||
|
||||
useWidth: undefined,
|
||||
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
*| useMaxWidth | See notes | Boolean | 4 | True, False |
|
||||
*
|
||||
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
|
||||
*available space if not the absolute space required is used.
|
||||
*
|
||||
***Default value true**.
|
||||
*/
|
||||
useMaxWidth: true
|
||||
},
|
||||
state: {
|
||||
dividerMargin: 10,
|
||||
|
@ -734,7 +786,18 @@ const config = {
|
|||
labelHeight: 16,
|
||||
edgeLengthFactor: '20',
|
||||
compositTitleSize: 35,
|
||||
radius: 5
|
||||
radius: 5,
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
*| useMaxWidth | See notes | Boolean | 4 | True, False |
|
||||
*
|
||||
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
|
||||
*available space if not the absolute space required is used.
|
||||
*
|
||||
***Default value true**.
|
||||
*/
|
||||
useMaxWidth: true
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -821,6 +884,26 @@ const config = {
|
|||
*/
|
||||
fontSize: 12,
|
||||
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
*| useMaxWidth | See Notes | Boolean | Required | true, false |
|
||||
*
|
||||
***Notes:**
|
||||
*When this flag is set to true, the diagram width is locked to 100% and
|
||||
*scaled based on available space. If set to false, the diagram reserves its
|
||||
*absolute width.
|
||||
***Default value: true**.
|
||||
*/
|
||||
useMaxWidth: true
|
||||
},
|
||||
|
||||
/**
|
||||
* The object containing configurations specific for pie diagrams
|
||||
*/
|
||||
pie: {
|
||||
useWidth: undefined,
|
||||
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
|
|
|
@ -7,8 +7,6 @@ import mermaidAPI from '../../mermaidAPI';
|
|||
|
||||
const MERMAID_DOM_ID_PREFIX = 'classid-';
|
||||
|
||||
const config = configApi.getConfig();
|
||||
|
||||
let relations = [];
|
||||
let classes = {};
|
||||
let classCounter = 0;
|
||||
|
@ -176,6 +174,7 @@ export const setCssClass = function(ids, className) {
|
|||
* @param tooltip Tooltip for the clickable element
|
||||
*/
|
||||
export const setLink = function(ids, linkStr, tooltip) {
|
||||
const config = configApi.getConfig();
|
||||
ids.split(',').forEach(function(_id) {
|
||||
let id = _id;
|
||||
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
|
@ -199,11 +198,13 @@ export const setLink = function(ids, linkStr, tooltip) {
|
|||
export const setClickEvent = function(ids, functionName, tooltip) {
|
||||
ids.split(',').forEach(function(id) {
|
||||
setClickFunc(id, functionName, tooltip);
|
||||
classes[id].haveCallback = true;
|
||||
});
|
||||
setCssClass(ids, 'clickable');
|
||||
};
|
||||
|
||||
const setClickFunc = function(domId, functionName, tooltip) {
|
||||
const config = configApi.getConfig();
|
||||
let id = domId;
|
||||
let elemId = lookUpDomId(id);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { getConfig } from '../../config';
|
|||
import { render } from '../../dagre-wrapper/index.js';
|
||||
// import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { curveLinear } from 'd3';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils';
|
||||
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
|
||||
import common from '../common/common';
|
||||
|
||||
parser.yy = classDb;
|
||||
|
@ -42,7 +42,10 @@ export const addClasses = function(classes, g) {
|
|||
* Variable for storing the classes for the vertex
|
||||
* @type {string}
|
||||
*/
|
||||
let classStr = 'default';
|
||||
let cssClassStr = '';
|
||||
if (vertex.cssClasses.length > 0) {
|
||||
cssClassStr = cssClassStr + ' ' + vertex.cssClasses.join(' ');
|
||||
}
|
||||
// if (vertex.classes.length > 0) {
|
||||
// classStr = vertex.classes.join(' ');
|
||||
// }
|
||||
|
@ -98,9 +101,12 @@ export const addClasses = function(classes, g) {
|
|||
classData: vertex,
|
||||
rx: radious,
|
||||
ry: radious,
|
||||
class: classStr,
|
||||
class: cssClassStr,
|
||||
style: styles.style,
|
||||
id: vertex.id,
|
||||
domId: vertex.domId,
|
||||
haveCallback: vertex.haveCallback,
|
||||
link: vertex.link,
|
||||
width: vertex.type === 'group' ? 500 : undefined,
|
||||
type: vertex.type,
|
||||
padding: getConfig().flowchart.padding
|
||||
|
@ -112,7 +118,7 @@ export const addClasses = function(classes, g) {
|
|||
labelText: vertexText,
|
||||
rx: radious,
|
||||
ry: radious,
|
||||
class: classStr,
|
||||
class: cssClassStr,
|
||||
style: styles.style,
|
||||
id: vertex.id,
|
||||
width: vertex.type === 'group' ? 500 : undefined,
|
||||
|
@ -155,6 +161,12 @@ export const addRelations = function(relations, g) {
|
|||
}
|
||||
|
||||
logger.info(edgeData, edge);
|
||||
//Set edge extra labels
|
||||
//edgeData.startLabelLeft = edge.relationTitle1;
|
||||
edgeData.startLabelRight = edge.relationTitle1 === 'none' ? '' : edge.relationTitle1;
|
||||
edgeData.endLabelLeft = edge.relationTitle2 === 'none' ? '' : edge.relationTitle2;
|
||||
//edgeData.endLabelRight = edge.relationTitle2;
|
||||
|
||||
//Set relation arrow types
|
||||
edgeData.arrowTypeStart = getArrowMarker(edge.relation.type1);
|
||||
edgeData.arrowTypeEnd = getArrowMarker(edge.relation.type2);
|
||||
|
@ -325,13 +337,7 @@ export const drawOld = function(text, id) {
|
|||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
diagram.attr('width', '100%');
|
||||
diagram.attr('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
diagram.attr('height', height);
|
||||
diagram.attr('width', width);
|
||||
}
|
||||
configureSvgSize(diagram, height, width, conf.useMaxWidth);
|
||||
|
||||
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
|
||||
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
|
||||
|
@ -427,13 +433,7 @@ export const draw = function(text, id) {
|
|||
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
|
||||
);
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
svg.attr('width', '100%');
|
||||
svg.attr('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
svg.attr('height', height);
|
||||
svg.attr('width', width);
|
||||
}
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
svg
|
||||
|
|
|
@ -5,6 +5,7 @@ import { logger } from '../../logger';
|
|||
import classDb, { lookUpDomId } from './classDb';
|
||||
import { parser } from './parser/classDiagram';
|
||||
import svgDraw from './svgDraw';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
|
||||
parser.yy = classDb;
|
||||
|
||||
|
@ -232,13 +233,7 @@ export const draw = function(text, id) {
|
|||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
diagram.attr('width', '100%');
|
||||
diagram.attr('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
diagram.attr('height', height);
|
||||
diagram.attr('width', width);
|
||||
}
|
||||
configureSvgSize(diagram, height, width, conf.useMaxWidth);
|
||||
|
||||
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
|
||||
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
|
||||
|
|
|
@ -9,6 +9,11 @@ const getStyles = options =>
|
|||
.title {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.classTitle {
|
||||
font-weight: bolder;
|
||||
}
|
||||
.node rect,
|
||||
.node circle,
|
||||
|
@ -109,6 +114,11 @@ g.classGroup line {
|
|||
stroke: ${options.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.edgeTerminals {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
export default getStyles;
|
||||
|
|
|
@ -6,6 +6,7 @@ import dagre from 'dagre';
|
|||
import { getConfig } from '../../config';
|
||||
import { logger } from '../../logger';
|
||||
import erMarkers from './erMarkers';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
|
||||
const conf = {};
|
||||
|
||||
|
@ -344,13 +345,7 @@ export const draw = function(text, id) {
|
|||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
svg.attr('width', '100%');
|
||||
svg.attr('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
svg.attr('height', height);
|
||||
svg.attr('width', width);
|
||||
}
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
svg.attr('viewBox', `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`);
|
||||
}; // draw
|
||||
|
|
|
@ -3,10 +3,10 @@ import utils from '../../utils';
|
|||
import * as configApi from '../../config';
|
||||
import common from '../common/common';
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
import { logger } from '../../logger';
|
||||
|
||||
// const MERMAID_DOM_ID_PREFIX = 'mermaid-dom-id-';
|
||||
const MERMAID_DOM_ID_PREFIX = '';
|
||||
|
||||
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
|
||||
let vertexCounter = 0;
|
||||
let config = configApi.getConfig();
|
||||
let vertices = {};
|
||||
let edges = [];
|
||||
|
@ -17,6 +17,9 @@ let tooltips = {};
|
|||
let subCount = 0;
|
||||
let firstGraphFlag = true;
|
||||
let direction;
|
||||
|
||||
let version; // As in graph
|
||||
|
||||
// Functions to be run after graph rendering
|
||||
let funs = [];
|
||||
|
||||
|
@ -24,6 +27,21 @@ export const parseDirective = function(statement, context, type) {
|
|||
mermaidAPI.parseDirective(this, statement, context, type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to lookup domId from id in the graph definition.
|
||||
* @param id
|
||||
* @public
|
||||
*/
|
||||
export const lookUpDomId = function(id) {
|
||||
const veritceKeys = Object.keys(vertices);
|
||||
for (let i = 0; i < veritceKeys.length; i++) {
|
||||
if (vertices[veritceKeys[i]].id === id) {
|
||||
return vertices[veritceKeys[i]].domId;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function called by parser when a node definition has been found
|
||||
* @param id
|
||||
|
@ -42,11 +60,17 @@ export const addVertex = function(_id, text, type, style, classes) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
// if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
|
||||
if (typeof vertices[id] === 'undefined') {
|
||||
vertices[id] = { id: id, styles: [], classes: [] };
|
||||
vertices[id] = {
|
||||
id: id,
|
||||
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
|
||||
styles: [],
|
||||
classes: []
|
||||
};
|
||||
}
|
||||
vertexCounter++;
|
||||
if (typeof text !== 'undefined') {
|
||||
config = configApi.getConfig();
|
||||
txt = common.sanitizeText(text.trim(), config);
|
||||
|
@ -91,8 +115,8 @@ export const addVertex = function(_id, text, type, style, classes) {
|
|||
export const addSingleLink = function(_start, _end, type, linktext) {
|
||||
let start = _start;
|
||||
let end = _end;
|
||||
if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
|
||||
if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end;
|
||||
// if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
|
||||
// if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end;
|
||||
// logger.info('Got edge...', start, end);
|
||||
|
||||
const edge = { start: start, end: end, type: undefined, text: '' };
|
||||
|
@ -202,8 +226,9 @@ export const setDirection = function(dir) {
|
|||
*/
|
||||
export const setClass = function(ids, className) {
|
||||
ids.split(',').forEach(function(_id) {
|
||||
// let id = version === 'gen-2' ? lookUpDomId(_id) : _id;
|
||||
let id = _id;
|
||||
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
// if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
vertices[id].classes.push(className);
|
||||
}
|
||||
|
@ -217,14 +242,14 @@ export const setClass = function(ids, className) {
|
|||
const setTooltip = function(ids, tooltip) {
|
||||
ids.split(',').forEach(function(id) {
|
||||
if (typeof tooltip !== 'undefined') {
|
||||
tooltips[id] = common.sanitizeText(tooltip, config);
|
||||
tooltips[version === 'gen-1' ? lookUpDomId(id) : id] = common.sanitizeText(tooltip, config);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setClickFun = function(_id, functionName) {
|
||||
let id = _id;
|
||||
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
const setClickFun = function(id, functionName) {
|
||||
let domId = lookUpDomId(id);
|
||||
// if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
if (configApi.getConfig().securityLevel !== 'loose') {
|
||||
return;
|
||||
}
|
||||
|
@ -232,8 +257,9 @@ const setClickFun = function(_id, functionName) {
|
|||
return;
|
||||
}
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
vertices[id].haveCallback = true;
|
||||
funs.push(function() {
|
||||
const elem = document.querySelector(`[id="${id}"]`);
|
||||
const elem = document.querySelector(`[id="${domId}"]`);
|
||||
if (elem !== null) {
|
||||
elem.addEventListener(
|
||||
'click',
|
||||
|
@ -254,9 +280,7 @@ const setClickFun = function(_id, functionName) {
|
|||
* @param tooltip Tooltip for the clickable element
|
||||
*/
|
||||
export const setLink = function(ids, linkStr, tooltip, target) {
|
||||
ids.split(',').forEach(function(_id) {
|
||||
let id = _id;
|
||||
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
ids.split(',').forEach(function(id) {
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
vertices[id].link = utils.formatUrl(linkStr, config);
|
||||
vertices[id].linkTarget = target;
|
||||
|
@ -362,7 +386,7 @@ funs.push(setupToolTips);
|
|||
/**
|
||||
* Clears the internal graph db so that a new graph can be parsed.
|
||||
*/
|
||||
export const clear = function() {
|
||||
export const clear = function(ver) {
|
||||
vertices = {};
|
||||
classes = {};
|
||||
edges = [];
|
||||
|
@ -373,6 +397,10 @@ export const clear = function() {
|
|||
subCount = 0;
|
||||
tooltips = [];
|
||||
firstGraphFlag = true;
|
||||
version = ver || 'gen-1';
|
||||
};
|
||||
export const setGen = ver => {
|
||||
version = ver || 'gen-1';
|
||||
};
|
||||
/**
|
||||
*
|
||||
|
@ -411,16 +439,39 @@ export const addSubGraph = function(_id, list, _title) {
|
|||
let nodeList = [];
|
||||
|
||||
nodeList = uniq(nodeList.concat.apply(nodeList, list));
|
||||
for (let i = 0; i < nodeList.length; i++) {
|
||||
if (nodeList[i][0].match(/\d/)) nodeList[i] = MERMAID_DOM_ID_PREFIX + nodeList[i];
|
||||
if (version === 'gen-1') {
|
||||
logger.warn('LOOKING UP');
|
||||
for (let i = 0; i < nodeList.length; i++) {
|
||||
nodeList[i] = lookUpDomId(nodeList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
id = id || 'subGraph' + subCount;
|
||||
if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
// if (id[0].match(/\d/)) id = lookUpDomId(id);
|
||||
title = title || '';
|
||||
title = common.sanitizeText(title, config);
|
||||
subCount = subCount + 1;
|
||||
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] };
|
||||
|
||||
console.log('Adding', subGraph.id, subGraph.nodes);
|
||||
|
||||
/**
|
||||
* Deletes an id from all subgraphs
|
||||
*/
|
||||
// const del = _id => {
|
||||
// subGraphs.forEach(sg => {
|
||||
// const pos = sg.nodes.indexOf(_id);
|
||||
// if (pos >= 0) {
|
||||
// sg.nodes.splice(pos, 1);
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
|
||||
// // Removes the members of this subgraph from any other subgraphs, a node only belong to one subgraph
|
||||
// subGraph.nodes.forEach(_id => del(_id));
|
||||
|
||||
// Remove the members in the new subgraph if they already belong to another subgraph
|
||||
subGraph.nodes = makeUniq(subGraph, subGraphs).nodes;
|
||||
subGraphs.push(subGraph);
|
||||
subGraphLookup[id] = subGraph;
|
||||
return id;
|
||||
|
@ -618,10 +669,35 @@ const destructLink = (_str, _startStr) => {
|
|||
return info;
|
||||
};
|
||||
|
||||
// Todo optimizer this by caching existing nodes
|
||||
const exists = (allSgs, _id) => {
|
||||
let res = false;
|
||||
allSgs.forEach(sg => {
|
||||
const pos = sg.nodes.indexOf(_id);
|
||||
if (pos >= 0) {
|
||||
res = true;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
/**
|
||||
* Deletes an id from all subgraphs
|
||||
*/
|
||||
const makeUniq = (sg, allSubgraphs) => {
|
||||
const res = [];
|
||||
sg.nodes.forEach((_id, pos) => {
|
||||
if (!exists(allSubgraphs, _id)) {
|
||||
res.push(sg.nodes[pos]);
|
||||
}
|
||||
});
|
||||
return { nodes: res };
|
||||
};
|
||||
|
||||
export default {
|
||||
parseDirective,
|
||||
defaultConfig: () => configApi.defaultConfig.flowchart,
|
||||
addVertex,
|
||||
lookUpDomId,
|
||||
addLink,
|
||||
updateLinkInterpolate,
|
||||
updateLink,
|
||||
|
@ -637,6 +713,7 @@ export default {
|
|||
getEdges,
|
||||
getClasses,
|
||||
clear,
|
||||
setGen,
|
||||
defaultStyle,
|
||||
addSubGraph,
|
||||
getDepthFirstPos,
|
||||
|
@ -645,5 +722,7 @@ export default {
|
|||
destructLink,
|
||||
lex: {
|
||||
firstGraph
|
||||
}
|
||||
},
|
||||
exists,
|
||||
makeUniq
|
||||
};
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import flowDb from './flowDb';
|
||||
|
||||
describe('flow db subgraphs', () => {
|
||||
let subgraphs;
|
||||
beforeEach( ()=>{
|
||||
subgraphs = [
|
||||
{nodes:['a', 'b', 'c', 'e']},
|
||||
{nodes:['f', 'g', 'h']},
|
||||
{nodes:['i', 'j']},
|
||||
{nodes:['k']},
|
||||
];
|
||||
});
|
||||
describe('exist', () => {
|
||||
it('should return true when the is exists in a subgraph', () => {
|
||||
expect(flowDb.exists(subgraphs, 'a')).toBe(true);
|
||||
expect(flowDb.exists(subgraphs, 'h')).toBe(true);
|
||||
expect(flowDb.exists(subgraphs, 'j')).toBe(true);
|
||||
expect(flowDb.exists(subgraphs, 'k')).toBe(true);
|
||||
});
|
||||
it('should return false when the is exists in a subgraph', () => {
|
||||
expect(flowDb.exists(subgraphs, 'a2')).toBe(false);
|
||||
expect(flowDb.exists(subgraphs, 'l')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeUniq', () => {
|
||||
it('should remove ids from sungraph that already exists in another subgraph even if it gets empty', () => {
|
||||
const subgraph = flowDb.makeUniq({nodes:['i', 'j']}, subgraphs);
|
||||
|
||||
expect(subgraph.nodes).toEqual([]);
|
||||
});
|
||||
it('should remove ids from sungraph that already exists in another subgraph', () => {
|
||||
const subgraph = flowDb.makeUniq({nodes:['i', 'j', 'o']}, subgraphs);
|
||||
|
||||
expect(subgraph.nodes).toEqual(['o']);
|
||||
});
|
||||
it('should not remove ids from subgraph if they are unique', () => {
|
||||
const subgraph = flowDb.makeUniq({nodes:['q', 'r', 's']}, subgraphs);
|
||||
|
||||
expect(subgraph.nodes).toEqual(['q', 'r', 's']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -9,7 +9,7 @@ import { render } from '../../dagre-wrapper/index.js';
|
|||
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { logger } from '../../logger';
|
||||
import common from '../common/common';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils';
|
||||
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
|
||||
|
||||
const conf = {};
|
||||
export const setConf = function(cnf) {
|
||||
|
@ -141,6 +141,11 @@ export const addVertices = function(vert, g, svgId) {
|
|||
class: classStr,
|
||||
style: styles.style,
|
||||
id: vertex.id,
|
||||
link: vertex.link,
|
||||
linkTarget: vertex.linkTarget,
|
||||
tooltip: flowDb.getTooltip(vertex.id) || '',
|
||||
domId: flowDb.lookUpDomId(vertex.id),
|
||||
haveCallback: vertex.haveCallback,
|
||||
width: vertex.type === 'group' ? 500 : undefined,
|
||||
type: vertex.type,
|
||||
padding: getConfig().flowchart.padding
|
||||
|
@ -155,6 +160,7 @@ export const addVertices = function(vert, g, svgId) {
|
|||
class: classStr,
|
||||
style: styles.style,
|
||||
id: vertex.id,
|
||||
domId: flowDb.lookUpDomId(vertex.id),
|
||||
width: vertex.type === 'group' ? 500 : undefined,
|
||||
type: vertex.type,
|
||||
padding: getConfig().flowchart.padding
|
||||
|
@ -329,6 +335,7 @@ export const getClasses = function(text) {
|
|||
export const draw = function(text, id) {
|
||||
logger.info('Drawing flowchart');
|
||||
flowDb.clear();
|
||||
flowDb.setGen('gen-2');
|
||||
const parser = flow.parser;
|
||||
parser.yy = flowDb;
|
||||
|
||||
|
@ -382,11 +389,13 @@ export const draw = function(text, id) {
|
|||
logger.info(edges);
|
||||
let i = 0;
|
||||
for (i = subGraphs.length - 1; i >= 0; i--) {
|
||||
// for (let i = 0; i < subGraphs.length; i++) {
|
||||
subG = subGraphs[i];
|
||||
|
||||
selectAll('cluster').append('text');
|
||||
|
||||
for (let j = 0; j < subG.nodes.length; j++) {
|
||||
logger.info('Setting up subgraphs', subG.nodes[j], subG.id);
|
||||
g.setParent(subG.nodes[j], subG.id);
|
||||
}
|
||||
}
|
||||
|
@ -403,11 +412,6 @@ export const draw = function(text, id) {
|
|||
// Run the renderer. This is what draws the final graph.
|
||||
const element = select('#' + id + ' g');
|
||||
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
|
||||
// dagre.layout(g);
|
||||
|
||||
element.selectAll('g.node').attr('title', function() {
|
||||
return flowDb.getTooltip(this.id);
|
||||
});
|
||||
|
||||
const padding = conf.diagramPadding;
|
||||
const svgBounds = svg.node().getBBox();
|
||||
|
@ -418,13 +422,7 @@ export const draw = function(text, id) {
|
|||
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
|
||||
);
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
svg.attr('width', '100%');
|
||||
svg.attr('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
svg.attr('height', height);
|
||||
svg.attr('width', width);
|
||||
}
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
svg
|
||||
|
@ -434,28 +432,6 @@ export const draw = function(text, id) {
|
|||
// Index nodes
|
||||
flowDb.indexNodes('subGraph' + i);
|
||||
|
||||
// // reposition labels
|
||||
// for (i = 0; i < subGraphs.length; i++) {
|
||||
// subG = subGraphs[i];
|
||||
|
||||
// if (subG.title !== 'undefined') {
|
||||
// const clusterRects = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"] rect');
|
||||
// const clusterEl = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"]');
|
||||
|
||||
// const xPos = clusterRects[0].x.baseVal.value;
|
||||
// const yPos = clusterRects[0].y.baseVal.value;
|
||||
// const width = clusterRects[0].width.baseVal.value;
|
||||
// const cluster = d3.select(clusterEl[0]);
|
||||
// const te = cluster.select('.label');
|
||||
// te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`);
|
||||
// te.attr('id', id + 'Text');
|
||||
|
||||
// for (let j = 0; j < subG.classes.length; j++) {
|
||||
// clusterEl[0].classList.add(subG.classes[j]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Add label rects for non html labels
|
||||
if (!conf.htmlLabels) {
|
||||
const labels = document.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
|
||||
|
|
|
@ -9,7 +9,7 @@ import dagreD3 from 'dagre-d3';
|
|||
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { logger } from '../../logger';
|
||||
import common from '../common/common';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils';
|
||||
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
|
||||
import flowChartShapes from './flowChartShapes';
|
||||
|
||||
const conf = {};
|
||||
|
@ -133,7 +133,8 @@ export const addVertices = function(vert, g, svgId) {
|
|||
_shape = 'rect';
|
||||
}
|
||||
// Add the node
|
||||
g.setNode(vertex.id, {
|
||||
logger.warn('Adding node', vertex.id, vertex.domId);
|
||||
g.setNode(flowDb.lookUpDomId(vertex.id), {
|
||||
labelType: 'svg',
|
||||
labelStyle: styles.labelStyle,
|
||||
shape: _shape,
|
||||
|
@ -142,7 +143,7 @@ export const addVertices = function(vert, g, svgId) {
|
|||
ry: radious,
|
||||
class: classStr,
|
||||
style: styles.style,
|
||||
id: vertex.id
|
||||
id: flowDb.lookUpDomId(vertex.id)
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -247,7 +248,7 @@ export const addEdges = function(edges, g) {
|
|||
edgeData.minlen = edge.length || 1;
|
||||
|
||||
// Add the edge to the graph
|
||||
g.setEdge(edge.start, edge.end, edgeData, cnt);
|
||||
g.setEdge(flowDb.lookUpDomId(edge.start), flowDb.lookUpDomId(edge.end), edgeData, cnt);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -278,6 +279,7 @@ export const getClasses = function(text) {
|
|||
export const draw = function(text, id) {
|
||||
logger.info('Drawing flowchart');
|
||||
flowDb.clear();
|
||||
flowDb.setGen('gen-1');
|
||||
const parser = flow.parser;
|
||||
parser.yy = flowDb;
|
||||
|
||||
|
@ -323,6 +325,7 @@ export const draw = function(text, id) {
|
|||
|
||||
// Fetch the verices/nodes and edges/links from the parsed graph definition
|
||||
const vert = flowDb.getVertices();
|
||||
logger.warn('Get vertices', vert);
|
||||
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
|
@ -333,7 +336,13 @@ export const draw = function(text, id) {
|
|||
selectAll('cluster').append('text');
|
||||
|
||||
for (let j = 0; j < subG.nodes.length; j++) {
|
||||
g.setParent(subG.nodes[j], subG.id);
|
||||
logger.warn(
|
||||
'Setting subgraph',
|
||||
subG.nodes[j],
|
||||
flowDb.lookUpDomId(subG.nodes[j]),
|
||||
flowDb.lookUpDomId(subG.id)
|
||||
);
|
||||
g.setParent(flowDb.lookUpDomId(subG.nodes[j]), flowDb.lookUpDomId(subG.id));
|
||||
}
|
||||
}
|
||||
addVertices(vert, g, id);
|
||||
|
@ -388,6 +397,8 @@ export const draw = function(text, id) {
|
|||
const svg = select(`[id="${id}"]`);
|
||||
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
||||
|
||||
logger.warn(g);
|
||||
|
||||
// Run the renderer. This is what draws the final graph.
|
||||
const element = select('#' + id + ' g');
|
||||
render(element, g);
|
||||
|
@ -401,13 +412,7 @@ export const draw = function(text, id) {
|
|||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
svg.attr('width', '100%');
|
||||
svg.attr('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
svg.attr('height', height);
|
||||
svg.attr('width', width);
|
||||
}
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
|
||||
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
|
||||
|
@ -420,10 +425,13 @@ export const draw = function(text, id) {
|
|||
// reposition labels
|
||||
for (i = 0; i < subGraphs.length; i++) {
|
||||
subG = subGraphs[i];
|
||||
|
||||
if (subG.title !== 'undefined') {
|
||||
const clusterRects = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"] rect');
|
||||
const clusterEl = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"]');
|
||||
const clusterRects = document.querySelectorAll(
|
||||
'#' + id + ' [id="' + flowDb.lookUpDomId(subG.id) + '"] rect'
|
||||
);
|
||||
const clusterEl = document.querySelectorAll(
|
||||
'#' + id + ' [id="' + flowDb.lookUpDomId(subG.id) + '"]'
|
||||
);
|
||||
|
||||
const xPos = clusterRects[0].x.baseVal.value;
|
||||
const yPos = clusterRects[0].y.baseVal.value;
|
||||
|
@ -465,7 +473,7 @@ export const draw = function(text, id) {
|
|||
const vertex = vert[key];
|
||||
|
||||
if (vertex.link) {
|
||||
const node = select('#' + id + ' [id="' + key + '"]');
|
||||
const node = select('#' + id + ' [id="' + flowDb.lookUpDomId(key) + '"]');
|
||||
if (node) {
|
||||
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
|
||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
|
||||
|
|
|
@ -21,7 +21,6 @@ describe('[Text] when parsing', () => {
|
|||
|
||||
expect(edges[0].type).toBe('arrow_point');
|
||||
expect(edges.length).toBe(47917);
|
||||
console.log(vert);
|
||||
expect(Object.keys(vert).length).toBe(2);
|
||||
});
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ describe('[Style] when parsing', () => {
|
|||
beforeEach(function() {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
|
||||
// log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));
|
||||
|
|
|
@ -10,6 +10,7 @@ describe('when parsing flowcharts', function() {
|
|||
beforeEach(function() {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
|
||||
it('should handle chaining of vertices', function() {
|
||||
|
|
|
@ -121,7 +121,6 @@ describe('when parsing ', function() {
|
|||
|
||||
const res = flow.parser.parse(statement);
|
||||
const vertices = flow.parser.yy.getVertices();
|
||||
console.log(vertices);
|
||||
const classes = flow.parser.yy.getClasses();
|
||||
expect(vertices['node1TB'].id).toBe('node1TB');
|
||||
});
|
||||
|
@ -132,8 +131,16 @@ describe('when parsing ', function() {
|
|||
statement = statement + 'graph TD;A--x|text including URL space|B;';
|
||||
const res = flow.parser.parse(statement);
|
||||
const vertices = flow.parser.yy.getVertices();
|
||||
console.log(vertices);
|
||||
const classes = flow.parser.yy.getClasses();
|
||||
expect(vertices['A'].id).toBe('A');
|
||||
});
|
||||
it('should be possible to use numbers as labels', function() {
|
||||
let statement = '';
|
||||
|
||||
statement = statement + 'graph TB;subgraph "number as labels";1;end;';
|
||||
const res = flow.parser.parse(statement);
|
||||
const vertices = flow.parser.yy.getVertices();
|
||||
const classes = flow.parser.yy.getClasses();
|
||||
expect(vertices['1'].id).toBe('1');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import flowDb from '../flowDb';
|
||||
import flow from './flow';
|
||||
import filter from 'lodash/filter';
|
||||
import { setConfig } from '../../../config';
|
||||
|
||||
setConfig({
|
||||
|
@ -10,6 +11,7 @@ describe('when parsing subgraphs', function() {
|
|||
beforeEach(function() {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
it('should handle subgraph with tab indentation', function() {
|
||||
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2\nend');
|
||||
|
@ -238,4 +240,74 @@ describe('when parsing subgraphs', function() {
|
|||
|
||||
expect(edges[0].type).toBe('arrow_point');
|
||||
});
|
||||
it('should handle nested subgraphs 1', function() {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
subgraph A
|
||||
b-->B
|
||||
a
|
||||
end
|
||||
a-->c
|
||||
subgraph B
|
||||
c
|
||||
end`);
|
||||
|
||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||
expect(subgraphs.length).toBe(2);
|
||||
|
||||
const subgraphA = filter(subgraphs,o => o.id === 'A')[0];
|
||||
const subgraphB = filter(subgraphs,o => o.id === 'B')[0];
|
||||
|
||||
expect(subgraphB.nodes[0]).toBe('c');
|
||||
expect(subgraphA.nodes).toContain('B');
|
||||
expect(subgraphA.nodes).toContain('b');
|
||||
expect(subgraphA.nodes).toContain('a');
|
||||
expect(subgraphA.nodes).not.toContain('c');
|
||||
});
|
||||
it('should handle nested subgraphs 2', function() {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
b-->B
|
||||
a-->c
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
subgraph A
|
||||
a
|
||||
b
|
||||
B
|
||||
end`);
|
||||
|
||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||
expect(subgraphs.length).toBe(2);
|
||||
|
||||
const subgraphA = filter(subgraphs,o => o.id === 'A')[0];
|
||||
const subgraphB = filter(subgraphs,o => o.id === 'B')[0];
|
||||
|
||||
expect(subgraphB.nodes[0]).toBe('c');
|
||||
expect(subgraphA.nodes).toContain('B');
|
||||
expect(subgraphA.nodes).toContain('b');
|
||||
expect(subgraphA.nodes).toContain('a');
|
||||
expect(subgraphA.nodes).not.toContain('c');
|
||||
});
|
||||
it('should handle nested subgraphs 3', function() {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
a-->c
|
||||
subgraph A
|
||||
b-->B
|
||||
a
|
||||
end`);
|
||||
|
||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||
expect(subgraphs.length).toBe(2);
|
||||
|
||||
const subgraphA = filter(subgraphs,o => o.id === 'A')[0];
|
||||
const subgraphB = filter(subgraphs,o => o.id === 'B')[0];
|
||||
expect(subgraphB.nodes[0]).toBe('c');
|
||||
expect(subgraphA.nodes).toContain('B');
|
||||
expect(subgraphA.nodes).toContain('b');
|
||||
expect(subgraphA.nodes).toContain('a');
|
||||
expect(subgraphA.nodes).not.toContain('c');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { parser } from './parser/gantt';
|
||||
import common from '../common/common';
|
||||
import ganttDb from './ganttDb';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
|
||||
parser.yy = ganttDb;
|
||||
|
||||
|
@ -53,7 +54,6 @@ export const draw = function(text, id) {
|
|||
// Set height based on number of tasks
|
||||
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding;
|
||||
|
||||
elem.setAttribute('height', '100%');
|
||||
// Set viewBox
|
||||
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
|
||||
const svg = select(`[id="${id}"]`);
|
||||
|
@ -97,9 +97,8 @@ export const draw = function(text, id) {
|
|||
taskArray.sort(taskCompare);
|
||||
|
||||
makeGant(taskArray, w, h);
|
||||
if (typeof conf.useWidth !== 'undefined') {
|
||||
elem.setAttribute('width', w);
|
||||
}
|
||||
|
||||
configureSvgSize(svg, h, w, conf.useMaxWidth);
|
||||
|
||||
svg
|
||||
.append('text')
|
||||
|
@ -302,7 +301,6 @@ export const draw = function(text, id) {
|
|||
}
|
||||
|
||||
let secNum = 0;
|
||||
console.log(conf);
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
secNum = i % conf.numberSectionStyles;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
\%\%(?!\{)[^\n]* /* skip comments */
|
||||
[^\}]\%\%[^\n]* /* skip comments */{ console.log('Crap after close'); }
|
||||
[^\}]\%\%[^\n]* /* skip comments */{ /*console.log('');*/ }
|
||||
[\n\r]+ return 'NEWLINE';
|
||||
\%\%[^\n]* /* do nothing */
|
||||
[\s]+ /* ignore */
|
||||
|
|
|
@ -17,7 +17,6 @@ describe('when parsing pie', function() {
|
|||
"ash" : 100
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
console.log('sections: ', sections);
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(100);
|
||||
});
|
||||
|
@ -27,7 +26,6 @@ describe('when parsing pie', function() {
|
|||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
console.log('sections: ', sections);
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
|
@ -38,7 +36,6 @@ describe('when parsing pie', function() {
|
|||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
console.log('sections: ', sections);
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
|
@ -50,7 +47,6 @@ pie
|
|||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
console.log('sections: ', sections);
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
|
@ -62,7 +58,6 @@ pie
|
|||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getTitle();
|
||||
console.log('sections: ', sections);
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
expect(title).toBe('a 60/40 pie');
|
||||
|
@ -74,7 +69,6 @@ pie
|
|||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
console.log('sections: ', sections);
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60.67);
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@ import { select, scaleOrdinal, schemeSet2, pie as d3pie, entries, arc } from 'd3
|
|||
import pieData from './pieDb';
|
||||
import pieParser from './parser/pie';
|
||||
import { logger } from '../../logger';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
|
||||
const conf = {};
|
||||
export const setConf = function(cnf) {
|
||||
|
@ -20,7 +21,8 @@ export const setConf = function(cnf) {
|
|||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
let w;
|
||||
let width;
|
||||
const height = 450;
|
||||
export const draw = (txt, id) => {
|
||||
try {
|
||||
const parser = pieParser.parser;
|
||||
|
@ -31,34 +33,30 @@ export const draw = (txt, id) => {
|
|||
parser.parse(txt);
|
||||
logger.debug('Parsed info diagram');
|
||||
const elem = document.getElementById(id);
|
||||
w = elem.parentElement.offsetWidth;
|
||||
width = elem.parentElement.offsetWidth;
|
||||
|
||||
if (typeof w === 'undefined') {
|
||||
w = 1200;
|
||||
if (typeof width === 'undefined') {
|
||||
width = 1200;
|
||||
}
|
||||
|
||||
if (typeof conf.useWidth !== 'undefined') {
|
||||
w = conf.useWidth;
|
||||
width = conf.useWidth;
|
||||
}
|
||||
const h = 450;
|
||||
elem.setAttribute('height', '100%');
|
||||
|
||||
const diagram = select('#' + id);
|
||||
configureSvgSize(diagram, height, width, conf.useMaxWidth);
|
||||
|
||||
// Set viewBox
|
||||
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
|
||||
elem.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
|
||||
var width = w; // 450
|
||||
var height = 450;
|
||||
var margin = 40;
|
||||
var legendRectSize = 18;
|
||||
var legendSpacing = 4;
|
||||
|
||||
var radius = Math.min(width, height) / 2 - margin;
|
||||
|
||||
var svg = select('#' + id)
|
||||
.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
var svg = diagram
|
||||
.append('g')
|
||||
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
|
||||
|
||||
|
@ -67,9 +65,8 @@ export const draw = (txt, id) => {
|
|||
Object.keys(data).forEach(function(key) {
|
||||
sum += data[key];
|
||||
});
|
||||
logger.info(data);
|
||||
|
||||
// set the color scale
|
||||
// Set the color scale
|
||||
var color = scaleOrdinal()
|
||||
.domain(data)
|
||||
.range(schemeSet2);
|
||||
|
@ -80,12 +77,12 @@ export const draw = (txt, id) => {
|
|||
});
|
||||
var dataReady = pie(entries(data));
|
||||
|
||||
// shape helper to build arcs:
|
||||
// Shape helper to build arcs:
|
||||
var arcGenerator = arc()
|
||||
.innerRadius(0)
|
||||
.outerRadius(radius);
|
||||
|
||||
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
|
||||
// Build the pie chart: each part of the pie is a path that we build using the arc function.
|
||||
svg
|
||||
.selectAll('mySlices')
|
||||
.data(dataReady)
|
||||
|
@ -99,7 +96,8 @@ export const draw = (txt, id) => {
|
|||
.style('stroke-width', '2px')
|
||||
.style('opacity', 0.7);
|
||||
|
||||
// Now add the Percentage. Use the centroid method to get the best coordinates
|
||||
// Now add the percentage.
|
||||
// Use the centroid method to get the best coordinates.
|
||||
svg
|
||||
.selectAll('mySlices')
|
||||
.data(dataReady)
|
||||
|
@ -119,10 +117,10 @@ export const draw = (txt, id) => {
|
|||
.append('text')
|
||||
.text(parser.yy.getTitle())
|
||||
.attr('x', 0)
|
||||
.attr('y', -(h - 50) / 2)
|
||||
.attr('y', -(height - 50) / 2)
|
||||
.attr('class', 'pieTitleText');
|
||||
|
||||
//Add the slegend/annotations for each section
|
||||
// Add the legends/annotations for each section
|
||||
var legend = svg
|
||||
.selectAll('.legend')
|
||||
.data(color.domain())
|
||||
|
|
|
@ -43,7 +43,6 @@ const activationCount = part => {
|
|||
let i;
|
||||
let count = 0;
|
||||
for (i = 0; i < messages.length; i++) {
|
||||
// console.warn(i, messages[i]);
|
||||
if (messages[i].type === LINETYPE.ACTIVE_START) {
|
||||
if (messages[i].from.actor === part) {
|
||||
count++;
|
||||
|
|
|
@ -942,7 +942,6 @@ describe('when rendering a sequenceDiagram APA', function() {
|
|||
wrap: false,
|
||||
mirrorActors: false
|
||||
};
|
||||
console.warn('Set site config');
|
||||
configApi.setSiteConfig({ logLevel: 5, sequence: conf });
|
||||
});
|
||||
let conf;
|
||||
|
@ -1249,7 +1248,6 @@ Bob->>Alice: Fine!`;
|
|||
const { bounds, models } = renderer.bounds.getBounds();
|
||||
const msgs = parser.yy.getMessages();
|
||||
const mermaid = mermaidAPI.getConfig();
|
||||
console.log(mermaid)
|
||||
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
|
||||
expect(bounds.starty).toBe(0);
|
||||
expect(mermaid.theme).toBe('dark');
|
||||
|
|
|
@ -5,7 +5,7 @@ import { parser } from './parser/sequenceDiagram';
|
|||
import common from '../common/common';
|
||||
import sequenceDb from './sequenceDb';
|
||||
import * as configApi from '../../config';
|
||||
import utils, { assignWithDepth } from '../../utils';
|
||||
import utils, { assignWithDepth, configureSvgSize } from '../../utils';
|
||||
|
||||
parser.yy = sequenceDb;
|
||||
|
||||
|
@ -192,7 +192,6 @@ export const bounds = {
|
|||
return this.verticalPos;
|
||||
},
|
||||
getBounds: function() {
|
||||
console.log('here', this.data);
|
||||
return { bounds: this.data, models: this.models };
|
||||
}
|
||||
};
|
||||
|
@ -503,7 +502,6 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop
|
|||
*/
|
||||
export const draw = function(text, id) {
|
||||
conf = configApi.getConfig().sequence;
|
||||
console.log('there ', conf);
|
||||
parser.yy.clear();
|
||||
parser.yy.setWrap(conf.wrap);
|
||||
parser.parse(text + '\n');
|
||||
|
@ -706,15 +704,8 @@ export const draw = function(text, id) {
|
|||
.attr('y', -25);
|
||||
}
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
diagram.attr('height', '100%');
|
||||
diagram.attr('width', '100%');
|
||||
diagram.attr('style', 'max-width:' + width + 'px;');
|
||||
// diagram.attr('style', 'max-width:100%;');
|
||||
} else {
|
||||
diagram.attr('height', height);
|
||||
diagram.attr('width', width);
|
||||
}
|
||||
configureSvgSize(diagram, height, width, conf.useMaxWidth);
|
||||
|
||||
const extraVertForTitle = title ? 40 : 0;
|
||||
diagram.attr(
|
||||
'viewBox',
|
||||
|
|
|
@ -169,7 +169,6 @@ export const addTitleAndBox = (g, stateDef, altBkg) => {
|
|||
// .attr('class', 'descr-divider');
|
||||
|
||||
const graphBox = g.node().getBBox();
|
||||
// console.warn(width / 2, titleWidth / 2, getConfig().state.padding, orgBox);
|
||||
// descrLine.attr('x2', graphBox.width + getConfig().state.padding);
|
||||
|
||||
if (stateDef.doc) {
|
||||
|
@ -328,7 +327,6 @@ const _drawLongText = (_text, x, y, g) => {
|
|||
const textBounds = span.node().getBBox();
|
||||
tHeight += textBounds.height;
|
||||
}
|
||||
// console.warn('textBounds', textBounds);
|
||||
textHeight += tHeight;
|
||||
span.attr('x', x + getConfig().state.noteMargin);
|
||||
span.attr('y', y + textHeight + 1.25 * getConfig().state.noteMargin);
|
||||
|
@ -456,8 +454,6 @@ export const drawEdge = function(elem, path, relation) {
|
|||
|
||||
const rows = common.getRows(relation.title);
|
||||
|
||||
// console.warn(rows);
|
||||
|
||||
let titleHeight = 0;
|
||||
const titleRows = [];
|
||||
let maxWidth = 0;
|
||||
|
|
|
@ -6,6 +6,7 @@ import { getConfig } from '../../config';
|
|||
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
import { logger } from '../../logger';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
|
||||
const conf = {};
|
||||
export const setConf = function(cnf) {
|
||||
|
@ -99,6 +100,7 @@ const setupNode = (g, parent, node, altFlag) => {
|
|||
classes: nodeDb[node.id].classes, //classStr,
|
||||
style: '', //styles.style,
|
||||
id: node.id,
|
||||
domId: 'state-' + node.id + '-' + cnt,
|
||||
type: nodeDb[node.id].type,
|
||||
padding: 15 //getConfig().flowchart.padding
|
||||
};
|
||||
|
@ -112,6 +114,7 @@ const setupNode = (g, parent, node, altFlag) => {
|
|||
classes: 'statediagram-note', //classStr,
|
||||
style: '', //styles.style,
|
||||
id: node.id + '----note',
|
||||
domId: 'state-' + node.id + '----note-' + cnt,
|
||||
type: nodeDb[node.id].type,
|
||||
padding: 15 //getConfig().flowchart.padding
|
||||
};
|
||||
|
@ -122,9 +125,12 @@ const setupNode = (g, parent, node, altFlag) => {
|
|||
classes: nodeDb[node.id].classes, //classStr,
|
||||
style: '', //styles.style,
|
||||
id: node.id + '----parent',
|
||||
domId: 'state-' + node.id + '----parent-' + cnt,
|
||||
type: 'group',
|
||||
padding: 0 //getConfig().flowchart.padding
|
||||
};
|
||||
cnt++;
|
||||
|
||||
g.setNode(node.id + '----parent', groupData);
|
||||
|
||||
g.setNode(noteData.id, noteData);
|
||||
|
@ -169,6 +175,7 @@ const setupNode = (g, parent, node, altFlag) => {
|
|||
};
|
||||
let cnt = 0;
|
||||
const setupDoc = (g, parent, doc, altFlag) => {
|
||||
cnt = 0;
|
||||
logger.trace('items', doc);
|
||||
doc.forEach(item => {
|
||||
if (item.stmt === 'state' || item.stmt === 'default') {
|
||||
|
@ -211,11 +218,7 @@ export const draw = function(text, id) {
|
|||
parser.yy = stateDb;
|
||||
|
||||
// Parse the graph definition
|
||||
// try {
|
||||
parser.parse(text);
|
||||
// } catch (err) {
|
||||
// logger.error('Parsing failed', err);
|
||||
// }
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
let dir = stateDb.getDirection();
|
||||
|
@ -256,54 +259,18 @@ export const draw = function(text, id) {
|
|||
render(element, g, ['barb'], 'statediagram', id);
|
||||
|
||||
const padding = 8;
|
||||
// const svgBounds = svg.node().getBBox();
|
||||
// const width = svgBounds.width + padding * 2;
|
||||
// const height = svgBounds.height + padding * 2;
|
||||
// logger.debug(
|
||||
// `new ViewBox 0 0 ${width} ${height}`,
|
||||
// `translate(${padding + g._label.marginx}, ${padding + g._label.marginy})`
|
||||
// );
|
||||
|
||||
// if (conf.useMaxWidth) {
|
||||
// svg.attr('width', '100%');
|
||||
// svg.attr('style', `max-width: ${width}px;`);
|
||||
// } else {
|
||||
// svg.attr('height', height);
|
||||
// svg.attr('width', width);
|
||||
// }
|
||||
|
||||
// svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
// svg
|
||||
// .select('g')
|
||||
// .attr('transform', `translate(${padding - g._label.marginx}, ${padding - svgBounds.y})`);
|
||||
|
||||
const bounds = svg.node().getBBox();
|
||||
|
||||
const width = bounds.width + padding * 2;
|
||||
const height = bounds.height + padding * 2;
|
||||
|
||||
// diagram.attr('height', '100%');
|
||||
// diagram.attr('style', `width: ${bounds.width * 3 + conf.padding * 2};`);
|
||||
// diagram.attr('height', height);
|
||||
|
||||
// Zoom in a bit
|
||||
svg.attr('width', width * 1.75);
|
||||
svg.attr('class', 'statediagram');
|
||||
// diagram.attr('height', bounds.height * 3 + conf.padding * 2);
|
||||
// svg.attr(
|
||||
// 'viewBox',
|
||||
// `${bounds.x - conf.padding} ${bounds.y - conf.padding} ` + width + ' ' + height
|
||||
// );
|
||||
|
||||
const svgBounds = svg.node().getBBox();
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
svg.attr('width', '100%');
|
||||
svg.attr('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
svg.attr('height', height);
|
||||
svg.attr('width', width);
|
||||
}
|
||||
configureSvgSize(svg, height, width * 1.75, conf.useMaxWidth);
|
||||
|
||||
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
|
||||
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
|
||||
|
|
|
@ -8,6 +8,7 @@ import { parser } from './parser/stateDiagram';
|
|||
// import idCache from './id-cache';
|
||||
import { drawState, addTitleAndBox, drawEdge } from './shapes';
|
||||
import { getConfig } from '../../config';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
|
||||
parser.yy = stateDb;
|
||||
|
||||
|
@ -75,14 +76,10 @@ export const draw = function(text, id) {
|
|||
const width = bounds.width + padding * 2;
|
||||
const height = bounds.height + padding * 2;
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
diagram.attr('width', '100%');
|
||||
diagram.attr('style', `max-width: ${width * 1.75}px;`);
|
||||
} else {
|
||||
// Zoom in a bit
|
||||
diagram.attr('width', width * 1.75);
|
||||
}
|
||||
// diagram.attr('height', bounds.height * 3 + conf.padding * 2);
|
||||
// zoom in a bit
|
||||
const svgWidth = width * 1.75;
|
||||
configureSvgSize(diagram, height, svgWidth, conf.useMaxWidth);
|
||||
|
||||
diagram.attr(
|
||||
'viewBox',
|
||||
`${bounds.x - conf.padding} ${bounds.y - conf.padding} ` + width + ' ' + height
|
||||
|
|
|
@ -2,6 +2,7 @@ import { select } from 'd3';
|
|||
import { parser } from './parser/journey';
|
||||
import journeyDb from './journeyDb';
|
||||
import svgDraw from './svgDraw';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
|
||||
parser.yy = journeyDb;
|
||||
|
||||
|
@ -118,14 +119,8 @@ export const draw = function(text, id) {
|
|||
}
|
||||
const height = box.stopy - box.starty + 2 * conf.diagramMarginY;
|
||||
const width = LEFT_MARGIN + box.stopx + 2 * conf.diagramMarginX;
|
||||
if (conf.useMaxWidth) {
|
||||
diagram.attr('height', '100%');
|
||||
diagram.attr('width', '100%');
|
||||
diagram.attr('style', 'max-width:' + width + 'px;');
|
||||
} else {
|
||||
diagram.attr('height', height);
|
||||
diagram.attr('width', width);
|
||||
}
|
||||
|
||||
configureSvgSize(diagram, height, width, conf.useMaxWidth);
|
||||
|
||||
// Draw activity line
|
||||
diagram
|
||||
|
|
|
@ -42,6 +42,7 @@ describe('when using mermaid and ', function() {
|
|||
beforeEach(function() {
|
||||
flowParser.parser.yy = flowDb;
|
||||
flowDb.clear();
|
||||
flowDb.setGen('gen-2');
|
||||
});
|
||||
it('it should handle edges with text', function() {
|
||||
flowParser.parser.parse('graph TD;A-->|text ex|B;');
|
||||
|
@ -50,8 +51,8 @@ describe('when using mermaid and ', function() {
|
|||
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B-');
|
||||
expect(options.arrowhead).toBe('normal');
|
||||
expect(options.label.match('text ex')).toBeTruthy();
|
||||
}
|
||||
|
@ -67,8 +68,8 @@ describe('when using mermaid and ', function() {
|
|||
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B-');
|
||||
expect(options.arrowhead).toBe('normal');
|
||||
}
|
||||
};
|
||||
|
@ -83,8 +84,8 @@ describe('when using mermaid and ', function() {
|
|||
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B-');
|
||||
expect(options.arrowhead).toBe('none');
|
||||
}
|
||||
};
|
||||
|
@ -99,8 +100,8 @@ describe('when using mermaid and ', function() {
|
|||
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B-');
|
||||
expect(options.arrowhead).toBe('none');
|
||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
|
||||
}
|
||||
|
@ -115,8 +116,8 @@ describe('when using mermaid and ', function() {
|
|||
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B-');
|
||||
expect(options.arrowhead).toBe('none');
|
||||
expect(options.curve).toBe('basis'); // mocked as string
|
||||
}
|
||||
|
@ -133,8 +134,8 @@ describe('when using mermaid and ', function() {
|
|||
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B-');
|
||||
expect(options.arrowhead).toBe('none');
|
||||
expect(options.label.match('the text')).toBeTruthy();
|
||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
|
||||
|
@ -151,8 +152,8 @@ describe('when using mermaid and ', function() {
|
|||
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B');
|
||||
expect(options.arrowhead).toBe('none');
|
||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
|
||||
}
|
||||
|
@ -169,8 +170,8 @@ describe('when using mermaid and ', function() {
|
|||
const edges = flowParser.parser.yy.getEdges();
|
||||
const mockG = {
|
||||
setEdge: function(start, end, options) {
|
||||
expect(start).toBe('A');
|
||||
expect(end).toBe('B');
|
||||
expect(start).toContain('flowchart-A-');
|
||||
expect(end).toContain('flowchart-B-');
|
||||
expect(options.arrowhead).toBe('none');
|
||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:blue;');
|
||||
}
|
||||
|
@ -184,6 +185,7 @@ describe('when using mermaid and ', function() {
|
|||
beforeEach(function() {
|
||||
flowParser.parser.yy = flowDb;
|
||||
flowDb.clear();
|
||||
flowDb.setGen('gen-2');
|
||||
});
|
||||
it('it should throw for an invalid definiton', function() {
|
||||
expect(() => mermaid.parse('this is not a mermaid diagram definition')).toThrow();
|
||||
|
|
|
@ -220,7 +220,7 @@ const render = function(id, _txt, cb, container) {
|
|||
// console.warn('Render fetching config');
|
||||
|
||||
const cnf = configApi.getConfig();
|
||||
console.warn('Render with config after adding new directives', cnf.sequence);
|
||||
// console.warn('Render with config after adding new directives', cnf.sequence);
|
||||
// console.warn(
|
||||
// 'Render with config after adding new directives',
|
||||
// cnf.fontFamily,
|
||||
|
@ -385,7 +385,7 @@ const render = function(id, _txt, cb, container) {
|
|||
break;
|
||||
case 'pie':
|
||||
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
|
||||
pieRenderer.setConf(cnf.class);
|
||||
pieRenderer.setConf(cnf.pie);
|
||||
pieRenderer.draw(txt, id, pkg.version);
|
||||
break;
|
||||
case 'er':
|
||||
|
@ -437,6 +437,7 @@ const render = function(id, _txt, cb, container) {
|
|||
cb(svgCode, ganttDb.bindFunctions);
|
||||
break;
|
||||
case 'class':
|
||||
case 'classDiagram':
|
||||
cb(svgCode, classDb.bindFunctions);
|
||||
break;
|
||||
default:
|
||||
|
@ -616,9 +617,9 @@ configApi.reset(configApi.getConfig());
|
|||
export default mermaidAPI;
|
||||
/**
|
||||
* ## mermaidAPI configuration defaults
|
||||
* <pre>
|
||||
*
|
||||
* <script>
|
||||
* ```html
|
||||
* <script>
|
||||
* var config = {
|
||||
* theme:'default',
|
||||
* logLevel:'fatal',
|
||||
|
@ -673,6 +674,6 @@ export default mermaidAPI;
|
|||
* }
|
||||
* };
|
||||
* mermaid.initialize(config);
|
||||
* </script>
|
||||
*</pre>
|
||||
* </script>
|
||||
* ```
|
||||
*/
|
||||
|
|
|
@ -166,6 +166,5 @@ class Theme {
|
|||
export const getThemeVariables = userOverrides => {
|
||||
const theme = new Theme();
|
||||
theme.calculate(userOverrides);
|
||||
console.info('Theme(dark)', { userOverrides, theme });
|
||||
return theme;
|
||||
};
|
||||
|
|
|
@ -177,6 +177,5 @@ class Theme {
|
|||
export const getThemeVariables = userOverrides => {
|
||||
const theme = new Theme();
|
||||
theme.calculate(userOverrides);
|
||||
// console.info('Theme(default)', { userOverrides, theme });
|
||||
return theme;
|
||||
};
|
||||
|
|
|
@ -147,6 +147,5 @@ class Theme {
|
|||
export const getThemeVariables = userOverrides => {
|
||||
const theme = new Theme();
|
||||
theme.calculate(userOverrides);
|
||||
console.info('Theme(forest)', { userOverrides, theme });
|
||||
return theme;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,6 @@ class Theme {
|
|||
|
||||
// this.secondaryColor = adjust(this.primaryColor, { h: 120 });
|
||||
this.tertiaryColor = adjust(this.primaryColor, { h: -160 });
|
||||
// console.log('primary color', this.primaryColor, 'tertiary - color', this.tertiaryColor);
|
||||
this.primaryBorderColor = mkBorder(this.primaryColor, this.darkMode);
|
||||
this.secondaryBorderColor = mkBorder(this.secondaryColor, this.darkMode);
|
||||
this.tertiaryBorderColor = mkBorder(this.tertiaryColor, this.darkMode);
|
||||
|
@ -185,6 +184,5 @@ class Theme {
|
|||
export const getThemeVariables = userOverrides => {
|
||||
const theme = new Theme();
|
||||
theme.calculate(userOverrides);
|
||||
console.info('Theme(neutral)', { userOverrides, theme });
|
||||
return theme;
|
||||
};
|
||||
|
|
98
src/utils.js
98
src/utils.js
|
@ -334,6 +334,7 @@ const calcLabelPosition = points => {
|
|||
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
|
||||
let prevPoint;
|
||||
let totalDistance = 0; // eslint-disable-line
|
||||
logger.info('our points', points);
|
||||
if (points[0] !== initialPosition) {
|
||||
points = points.reverse();
|
||||
}
|
||||
|
@ -380,6 +381,77 @@ const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition)
|
|||
return cardinalityPosition;
|
||||
};
|
||||
|
||||
/**
|
||||
* position ['start_left', 'start_right', 'end_left', 'end_right']
|
||||
*/
|
||||
const calcTerminalLabelPosition = (terminalMarkerSize, position, _points) => {
|
||||
// Todo looking to faster cloning method
|
||||
let points = JSON.parse(JSON.stringify(_points));
|
||||
let prevPoint;
|
||||
let totalDistance = 0; // eslint-disable-line
|
||||
logger.info('our points', points);
|
||||
if (position !== 'start_left' && position !== 'start_right') {
|
||||
points = points.reverse();
|
||||
}
|
||||
|
||||
points.forEach(point => {
|
||||
totalDistance += distance(point, prevPoint);
|
||||
prevPoint = point;
|
||||
});
|
||||
|
||||
// Traverse only 25 total distance along points to find cardinality point
|
||||
const distanceToCardinalityPoint = 25;
|
||||
|
||||
let remainingDistance = distanceToCardinalityPoint;
|
||||
let center;
|
||||
prevPoint = undefined;
|
||||
points.forEach(point => {
|
||||
if (prevPoint && !center) {
|
||||
const vectorDistance = distance(point, prevPoint);
|
||||
if (vectorDistance < remainingDistance) {
|
||||
remainingDistance -= vectorDistance;
|
||||
} else {
|
||||
// The point is remainingDistance from prevPoint in the vector between prevPoint and point
|
||||
// Calculate the coordinates
|
||||
const distanceRatio = remainingDistance / vectorDistance;
|
||||
if (distanceRatio <= 0) center = prevPoint;
|
||||
if (distanceRatio >= 1) center = { x: point.x, y: point.y };
|
||||
if (distanceRatio > 0 && distanceRatio < 1) {
|
||||
center = {
|
||||
x: (1 - distanceRatio) * prevPoint.x + distanceRatio * point.x,
|
||||
y: (1 - distanceRatio) * prevPoint.y + distanceRatio * point.y
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
prevPoint = point;
|
||||
});
|
||||
// if relation is present (Arrows will be added), change cardinality point off-set distance (d)
|
||||
let d = 10;
|
||||
//Calculate Angle for x and y axis
|
||||
let angle = Math.atan2(points[0].y - center.y, points[0].x - center.x);
|
||||
|
||||
let cardinalityPosition = { x: 0, y: 0 };
|
||||
|
||||
//Calculation cardinality position using angle, center point on the line/curve but pendicular and with offset-distance
|
||||
|
||||
cardinalityPosition.x = Math.sin(angle) * d + (points[0].x + center.x) / 2;
|
||||
cardinalityPosition.y = -Math.cos(angle) * d + (points[0].y + center.y) / 2;
|
||||
if (position === 'start_left') {
|
||||
cardinalityPosition.x = Math.sin(angle + Math.PI) * d + (points[0].x + center.x) / 2;
|
||||
cardinalityPosition.y = -Math.cos(angle + Math.PI) * d + (points[0].y + center.y) / 2;
|
||||
}
|
||||
if (position === 'end_right') {
|
||||
cardinalityPosition.x = Math.sin(angle - Math.PI) * d + (points[0].x + center.x) / 2 - 5;
|
||||
cardinalityPosition.y = -Math.cos(angle - Math.PI) * d + (points[0].y + center.y) / 2 - 5;
|
||||
}
|
||||
if (position === 'end_left') {
|
||||
cardinalityPosition.x = Math.sin(angle) * d + (points[0].x + center.x) / 2 - 5;
|
||||
cardinalityPosition.y = -Math.cos(angle) * d + (points[0].y + center.y) / 2 - 5;
|
||||
}
|
||||
return cardinalityPosition;
|
||||
};
|
||||
|
||||
export const getStylesFromArray = arr => {
|
||||
let style = '';
|
||||
let labelStyle = '';
|
||||
|
@ -695,12 +767,37 @@ export const calculateTextDimensions = memoize(
|
|||
(text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
|
||||
);
|
||||
|
||||
const d3Attrs = function(d3Elem, attrs) {
|
||||
for (let attr of attrs) {
|
||||
d3Elem.attr(attr[0], attr[1]);
|
||||
}
|
||||
};
|
||||
|
||||
export const calculateSvgSizeAttrs = function(height, width, useMaxWidth) {
|
||||
let attrs = new Map();
|
||||
attrs.set('height', height);
|
||||
if (useMaxWidth) {
|
||||
attrs.set('width', '100%');
|
||||
attrs.set('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
attrs.set('width', width);
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
export const configureSvgSize = function(svgElem, height, width, useMaxWidth) {
|
||||
const attrs = calculateSvgSizeAttrs(height, width, useMaxWidth);
|
||||
d3Attrs(svgElem, attrs);
|
||||
};
|
||||
|
||||
export default {
|
||||
assignWithDepth,
|
||||
wrapLabel,
|
||||
calculateTextHeight,
|
||||
calculateTextWidth,
|
||||
calculateTextDimensions,
|
||||
calculateSvgSizeAttrs,
|
||||
configureSvgSize,
|
||||
detectInit,
|
||||
detectDirective,
|
||||
detectType,
|
||||
|
@ -708,6 +805,7 @@ export default {
|
|||
interpolateToCurve,
|
||||
calcLabelPosition,
|
||||
calcCardinalityPosition,
|
||||
calcTerminalLabelPosition,
|
||||
formatUrl,
|
||||
getStylesFromArray,
|
||||
generateId,
|
||||
|
|
|
@ -171,7 +171,6 @@ Alice->Bob: hi`;
|
|||
expect(type).toBe('git');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when finding substring in array ', function() {
|
||||
it('should return the array index that contains the substring', function() {
|
||||
const arr = ['stroke:val1', 'fill:val2'];
|
||||
|
@ -184,7 +183,6 @@ describe('when finding substring in array ', function() {
|
|||
expect(result).toEqual(-1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when formatting urls', function() {
|
||||
it('should handle links', function() {
|
||||
const url = 'https://mermaid-js.github.io/mermaid/#/';
|
||||
|
@ -242,3 +240,16 @@ describe('when formatting urls', function() {
|
|||
expect(result).toEqual('about:blank');
|
||||
});
|
||||
});
|
||||
describe('when calculating SVG size', function() {
|
||||
it('should return width 100% when useMaxWidth is true', function () {
|
||||
const attrs = utils.calculateSvgSizeAttrs(100, 200, true);
|
||||
expect(attrs.get('height')).toEqual(100);
|
||||
expect(attrs.get('style')).toEqual('max-width: 200px;');
|
||||
expect(attrs.get('width')).toEqual('100%');
|
||||
});
|
||||
it('should return absolute width when useMaxWidth is false', function () {
|
||||
const attrs = utils.calculateSvgSizeAttrs(100, 200, false);
|
||||
expect(attrs.get('height')).toEqual(100);
|
||||
expect(attrs.get('width')).toEqual(200);
|
||||
});
|
||||
});
|
||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -4411,9 +4411,9 @@ eventemitter2@4.1.2:
|
|||
integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=
|
||||
|
||||
eventemitter3@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
|
||||
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||
|
||||
events@^3.0.0:
|
||||
version "3.1.0"
|
||||
|
@ -4882,11 +4882,9 @@ follow-redirects@1.9.0:
|
|||
debug "^3.0.0"
|
||||
|
||||
follow-redirects@^1.0.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb"
|
||||
integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==
|
||||
dependencies:
|
||||
debug "^3.0.0"
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
|
||||
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
|
||||
|
||||
for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
|
@ -5579,9 +5577,9 @@ http-proxy-middleware@0.19.1:
|
|||
micromatch "^3.1.10"
|
||||
|
||||
http-proxy@^1.17.0:
|
||||
version "1.18.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
|
||||
integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
|
||||
version "1.18.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
|
||||
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
|
||||
dependencies:
|
||||
eventemitter3 "^4.0.0"
|
||||
follow-redirects "^1.0.0"
|
||||
|
|
Loading…
Reference in New Issue