mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-10 10:59:51 +02:00
373
cypress/integration/rendering/classDiagram-v2.spec.js
Normal file
373
cypress/integration/rendering/classDiagram-v2.spec.js
Normal file
@@ -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" exClass
|
||||||
|
`,
|
||||||
|
{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:::exClass {
|
||||||
|
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" exClass
|
||||||
|
`,
|
||||||
|
{logLevel : 1, flowchart: { "htmlLabels": false },}
|
||||||
|
);
|
||||||
|
cy.get('svg');
|
||||||
|
});
|
||||||
|
});
|
@@ -21,7 +21,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<h1>info below</h1>
|
<h1>info below</h1>
|
||||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||||
%%{init: {'theme': 'base', 'fontFamily': 'arial', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
|
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
|
||||||
classDiagram-v2
|
classDiagram-v2
|
||||||
class BankAccount{
|
class BankAccount{
|
||||||
+String owner
|
+String owner
|
||||||
@@ -29,6 +29,24 @@
|
|||||||
+deposit(amount) bool
|
+deposit(amount) bool
|
||||||
+withdrawl(amount) int
|
+withdrawl(amount) int
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
<div class="mermaid" 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
|
classA --|> classB : Inheritance
|
||||||
classC --* classD : Composition
|
classC --* classD : Composition
|
||||||
classE --o classF : Aggregation
|
classE --o classF : Aggregation
|
||||||
@@ -42,15 +60,15 @@
|
|||||||
classA : method1()
|
classA : method1()
|
||||||
<<interface>> classB
|
<<interface>> classB
|
||||||
classB : method2() int
|
classB : method2() int
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
Customer "1" --> "*" Ticket
|
||||||
classDiagram-v2
|
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>
|
</div>
|
||||||
<script src="./mermaid.js"></script>
|
<script src="./mermaid.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -62,7 +80,7 @@
|
|||||||
// arrowMarkerAbsolute: true,
|
// arrowMarkerAbsolute: true,
|
||||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||||
logLevel: 0,
|
logLevel: 0,
|
||||||
flowchart: { curve: 'linear', "htmlLabels": false },
|
flowchart: { curve: 'linear', "htmlLabels": true },
|
||||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
sequence: { actorMargin: 50, showSequenceNumbers: true },
|
sequence: { actorMargin: 50, showSequenceNumbers: true },
|
||||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||||
|
@@ -6,9 +6,11 @@ import utils from '../utils';
|
|||||||
// import { calcLabelPosition } from '../utils';
|
// import { calcLabelPosition } from '../utils';
|
||||||
|
|
||||||
let edgeLabels = {};
|
let edgeLabels = {};
|
||||||
|
let terminalLabels = {};
|
||||||
|
|
||||||
export const clear = () => {
|
export const clear = () => {
|
||||||
edgeLabels = {};
|
edgeLabels = {};
|
||||||
|
terminalLabels = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const insertEdgeLabel = (elem, edge) => {
|
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
|
// Update the abstract data of the edge with the new information about its width and height
|
||||||
edge.width = bbox.width;
|
edge.width = bbox.width;
|
||||||
edge.height = bbox.height;
|
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]);
|
logger.info('Moving label', edge.id, edge.label, edgeLabels[edge.id]);
|
||||||
|
let path = paths.updatedPath ? paths.updatedPath : paths.originalPath;
|
||||||
if (edge.label) {
|
if (edge.label) {
|
||||||
const el = edgeLabels[edge.id];
|
const el = edgeLabels[edge.id];
|
||||||
let x = edge.x;
|
let x = edge.x;
|
||||||
let y = edge.y;
|
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;
|
// 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;
|
x = pos.x;
|
||||||
y = pos.y;
|
y = pos.y;
|
||||||
}
|
}
|
||||||
@@ -354,8 +468,10 @@ export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
let paths = {};
|
||||||
if (pointsHasChanged) {
|
if (pointsHasChanged) {
|
||||||
return points;
|
paths.updatedPath = points;
|
||||||
}
|
}
|
||||||
|
paths.originalPath = edge.points;
|
||||||
|
return paths;
|
||||||
};
|
};
|
||||||
|
@@ -128,8 +128,8 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||||||
const edge = graph.edge(e);
|
const edge = graph.edge(e);
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
||||||
|
|
||||||
const updatedPath = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
|
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
|
||||||
positionEdgeLabel(edge, updatedPath);
|
positionEdgeLabel(edge, paths);
|
||||||
});
|
});
|
||||||
|
|
||||||
return elem;
|
return elem;
|
||||||
|
@@ -5,6 +5,7 @@ import { getConfig } from '../config';
|
|||||||
import intersect from './intersect/index.js';
|
import intersect from './intersect/index.js';
|
||||||
import createLabel from './createLabel';
|
import createLabel from './createLabel';
|
||||||
import note from './shapes/note';
|
import note from './shapes/note';
|
||||||
|
import { parseMember } from '../diagrams/class/svgDraw';
|
||||||
|
|
||||||
const question = (parent, node) => {
|
const question = (parent, node) => {
|
||||||
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
|
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
|
||||||
@@ -568,27 +569,60 @@ const class_box = (parent, node) => {
|
|||||||
const hasInterface = node.classData.annotations && node.classData.annotations[0];
|
const hasInterface = node.classData.annotations && node.classData.annotations[0];
|
||||||
|
|
||||||
// 1. Create the labels
|
// 1. Create the labels
|
||||||
|
const interfaceLabelText = node.classData.annotations[0]
|
||||||
|
? '«' + node.classData.annotations[0] + '»'
|
||||||
|
: '';
|
||||||
const interfaceLabel = labelContainer
|
const interfaceLabel = labelContainer
|
||||||
.node()
|
.node()
|
||||||
.appendChild(createLabel(node.classData.annotations[0], node.labelStyle, true, true));
|
.appendChild(createLabel(interfaceLabelText, node.labelStyle, true, true));
|
||||||
const interfaceBBox = interfaceLabel.getBBox();
|
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]) {
|
if (node.classData.annotations[0]) {
|
||||||
maxHeight += interfaceBBox.height + rowPadding;
|
maxHeight += interfaceBBox.height + rowPadding;
|
||||||
maxWidth += interfaceBBox.width;
|
maxWidth += interfaceBBox.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let classTitleString = node.classData.id;
|
||||||
|
|
||||||
|
if (node.classData.type !== undefined && node.classData.type !== '') {
|
||||||
|
classTitleString += '<' + node.classData.type + '>';
|
||||||
|
}
|
||||||
const classTitleLabel = labelContainer
|
const classTitleLabel = labelContainer
|
||||||
.node()
|
.node()
|
||||||
.appendChild(createLabel(node.labelText, node.labelStyle, true, true));
|
.appendChild(createLabel(classTitleString, node.labelStyle, true, true));
|
||||||
const classTitleBBox = classTitleLabel.getBBox();
|
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;
|
maxHeight += classTitleBBox.height + rowPadding;
|
||||||
if (classTitleBBox.width > maxWidth) {
|
if (classTitleBBox.width > maxWidth) {
|
||||||
maxWidth = classTitleBBox.width;
|
maxWidth = classTitleBBox.width;
|
||||||
}
|
}
|
||||||
const classAttributes = [];
|
const classAttributes = [];
|
||||||
node.classData.members.forEach(str => {
|
node.classData.members.forEach(str => {
|
||||||
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
|
const parsedText = parseMember(str).displayText;
|
||||||
const bbox = lbl.getBBox();
|
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) {
|
if (bbox.width > maxWidth) {
|
||||||
maxWidth = bbox.width;
|
maxWidth = bbox.width;
|
||||||
}
|
}
|
||||||
@@ -596,10 +630,22 @@ const class_box = (parent, node) => {
|
|||||||
classAttributes.push(lbl);
|
classAttributes.push(lbl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
maxHeight += lineHeight;
|
||||||
|
|
||||||
const classMethods = [];
|
const classMethods = [];
|
||||||
node.classData.methods.forEach(str => {
|
node.classData.methods.forEach(str => {
|
||||||
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
|
const parsedText = parseMember(str).displayText;
|
||||||
const bbox = lbl.getBBox();
|
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) {
|
if (bbox.width > maxWidth) {
|
||||||
maxWidth = bbox.width;
|
maxWidth = bbox.width;
|
||||||
}
|
}
|
||||||
@@ -614,13 +660,10 @@ const class_box = (parent, node) => {
|
|||||||
|
|
||||||
// position the interface label
|
// position the interface label
|
||||||
if (hasInterface) {
|
if (hasInterface) {
|
||||||
|
let diffX = (maxWidth - interfaceBBox.width) / 2;
|
||||||
select(interfaceLabel).attr(
|
select(interfaceLabel).attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate( ' +
|
'translate( ' + ((-1 * maxWidth) / 2 + diffX) + ', ' + (-1 * maxHeight) / 2 + ')'
|
||||||
-(maxWidth + node.padding - interfaceBBox.width / 2) / 2 +
|
|
||||||
', ' +
|
|
||||||
(-1 * maxHeight) / 2 +
|
|
||||||
')'
|
|
||||||
);
|
);
|
||||||
verticalPos = interfaceBBox.height + rowPadding;
|
verticalPos = interfaceBBox.height + rowPadding;
|
||||||
}
|
}
|
||||||
@@ -657,6 +700,7 @@ const class_box = (parent, node) => {
|
|||||||
verticalPos += classTitleBBox.height + rowPadding;
|
verticalPos += classTitleBBox.height + rowPadding;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
verticalPos += lineHeight;
|
||||||
bottomLine
|
bottomLine
|
||||||
.attr('class', 'divider')
|
.attr('class', 'divider')
|
||||||
.attr('x1', -maxWidth / 2 - halfPadding)
|
.attr('x1', -maxWidth / 2 - halfPadding)
|
||||||
@@ -674,14 +718,14 @@ const class_box = (parent, node) => {
|
|||||||
verticalPos += classTitleBBox.height + rowPadding;
|
verticalPos += classTitleBBox.height + rowPadding;
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
let bbox;
|
// let bbox;
|
||||||
if (getConfig().flowchart.htmlLabels) {
|
// if (getConfig().flowchart.htmlLabels) {
|
||||||
const div = interfaceLabel.children[0];
|
// const div = interfaceLabel.children[0];
|
||||||
const dv = select(interfaceLabel);
|
// const dv = select(interfaceLabel);
|
||||||
bbox = div.getBoundingClientRect();
|
// bbox = div.getBoundingClientRect();
|
||||||
dv.attr('width', bbox.width);
|
// dv.attr('width', bbox.width);
|
||||||
dv.attr('height', bbox.height);
|
// dv.attr('height', bbox.height);
|
||||||
}
|
// }
|
||||||
// bbox = labelContainer.getBBox();
|
// bbox = labelContainer.getBBox();
|
||||||
|
|
||||||
// logger.info('Text 2', text2);
|
// logger.info('Text 2', text2);
|
||||||
|
@@ -155,6 +155,12 @@ export const addRelations = function(relations, g) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info(edgeData, edge);
|
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
|
//Set relation arrow types
|
||||||
edgeData.arrowTypeStart = getArrowMarker(edge.relation.type1);
|
edgeData.arrowTypeStart = getArrowMarker(edge.relation.type1);
|
||||||
edgeData.arrowTypeEnd = getArrowMarker(edge.relation.type2);
|
edgeData.arrowTypeEnd = getArrowMarker(edge.relation.type2);
|
||||||
|
@@ -9,6 +9,11 @@ const getStyles = options =>
|
|||||||
.title {
|
.title {
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.classTitle {
|
||||||
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
.node rect,
|
.node rect,
|
||||||
.node circle,
|
.node circle,
|
||||||
@@ -109,6 +114,11 @@ g.classGroup line {
|
|||||||
stroke: ${options.lineColor} !important;
|
stroke: ${options.lineColor} !important;
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edgeTerminals {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default getStyles;
|
export default getStyles;
|
||||||
|
73
src/utils.js
73
src/utils.js
@@ -334,6 +334,7 @@ const calcLabelPosition = points => {
|
|||||||
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
|
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
|
||||||
let prevPoint;
|
let prevPoint;
|
||||||
let totalDistance = 0; // eslint-disable-line
|
let totalDistance = 0; // eslint-disable-line
|
||||||
|
logger.info('our points', points);
|
||||||
if (points[0] !== initialPosition) {
|
if (points[0] !== initialPosition) {
|
||||||
points = points.reverse();
|
points = points.reverse();
|
||||||
}
|
}
|
||||||
@@ -380,6 +381,77 @@ const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition)
|
|||||||
return cardinalityPosition;
|
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 => {
|
export const getStylesFromArray = arr => {
|
||||||
let style = '';
|
let style = '';
|
||||||
let labelStyle = '';
|
let labelStyle = '';
|
||||||
@@ -734,6 +806,7 @@ export default {
|
|||||||
interpolateToCurve,
|
interpolateToCurve,
|
||||||
calcLabelPosition,
|
calcLabelPosition,
|
||||||
calcCardinalityPosition,
|
calcCardinalityPosition,
|
||||||
|
calcTerminalLabelPosition,
|
||||||
formatUrl,
|
formatUrl,
|
||||||
getStylesFromArray,
|
getStylesFromArray,
|
||||||
generateId,
|
generateId,
|
||||||
|
Reference in New Issue
Block a user