mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 22:39:26 +02:00
Compare commits
21 Commits
6623-add-i
...
develop
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e6ee145edf | ||
![]() |
5cc264feb7 | ||
![]() |
690cc73259 | ||
![]() |
b9ef683fb6 | ||
![]() |
6a6289f2aa | ||
![]() |
accb4c6369 | ||
![]() |
52be254ad3 | ||
![]() |
9c071a9064 | ||
![]() |
91d7229f1b | ||
![]() |
60feec465b | ||
![]() |
d1ae687d1e | ||
![]() |
7af6723ac0 | ||
![]() |
da90f6760b | ||
![]() |
d74013c642 | ||
![]() |
ce996346f8 | ||
![]() |
29edfa7f56 | ||
![]() |
14a4ab81c9 | ||
![]() |
2260948b7b | ||
![]() |
657a9ef785 | ||
![]() |
4f24489d81 | ||
![]() |
17142ef8d7 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: Add custom images and icons support for sequence diagram actors
|
6
.changeset/busy-clocks-melt.md
Normal file
6
.changeset/busy-clocks-melt.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@mermaid-js/layout-elk': patch
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
feat: Exposing elk configuration forceNodeModelOrder and considerModelOrder to the mermaid configuration
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.
|
5
.changeset/spicy-ties-see.md
Normal file
5
.changeset/spicy-ties-see.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@mermaid-js/layout-elk': patch
|
||||
---
|
||||
|
||||
Make elk not force node model order, but strongly consider it instead
|
9
.changeset/strong-laws-confess.md
Normal file
9
.changeset/strong-laws-confess.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: fallback to raw text instead of rendering _Unsupported markdown_ or empty blocks
|
||||
|
||||
Instead of printing **Unsupported markdown: XXX**, or empty blocks when using a markdown feature
|
||||
that Mermaid does not yet support when `htmlLabels: true`(default) or `htmlLabels: false`,
|
||||
fallback to the raw markdown text.
|
@@ -7,5 +7,6 @@ export default {
|
||||
'prettier --write',
|
||||
],
|
||||
'.cspell/*.txt': ['tsx scripts/fixCSpell.ts'],
|
||||
'**/*.md': ['pnpm cspell'],
|
||||
'**/*.jison': ['pnpm -w run lint:jison'],
|
||||
};
|
||||
|
@@ -1053,6 +1053,21 @@ flowchart LR
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('6647-elk: should keep node order when using elk layout unless it would add crossings', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
a --> a1 & a2 & a3 & a4
|
||||
b --> b1 & b2
|
||||
b2 --> b3
|
||||
b1 --> b4
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Title and arrow styling #4813', () => {
|
||||
|
@@ -1164,4 +1164,26 @@ end
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
describe('when rendering unsuported markdown', () => {
|
||||
const graph = `flowchart TB
|
||||
mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"]
|
||||
mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"]
|
||||
subgraph subgraph1["\`How to fix **fix**\`"]
|
||||
broken --> B["B"]
|
||||
end
|
||||
githost["Github, Gitlab, BitBucket, etc."]
|
||||
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
|
||||
a["1."]
|
||||
b["- x"]
|
||||
`;
|
||||
|
||||
it('should render raw strings', () => {
|
||||
imgSnapshotTest(graph);
|
||||
});
|
||||
|
||||
it('should render raw strings with htmlLabels: false', () => {
|
||||
imgSnapshotTest(graph, { htmlLabels: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,112 +0,0 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
|
||||
const looks = ['classic'] as const;
|
||||
|
||||
looks.forEach((look) => {
|
||||
describe(`SequenceDiagram icon participants in ${look} look`, () => {
|
||||
it(`single participant with icon`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "fa:bell" }
|
||||
Note over Bob: Icon participant`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`two participants, one icon and one normal`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "fa:bell" }
|
||||
participant Alice
|
||||
Bob->>Alice: Hello`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`two icon participants`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "fa:bell" }
|
||||
participant Alice@{ type: "icon", icon: "fa:user" }
|
||||
Bob->>Alice: Hello
|
||||
Alice-->>Bob: Hi`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`with markdown htmlLabels:true content`, () => {
|
||||
// html/markdown in messages/notes (participants themselves don't support label/form/w/h)
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "fa:bell" }
|
||||
participant Alice
|
||||
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>
|
||||
Note over Bob,Alice: Mixed <em>HTML</em> and **markdown**`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`with markdown htmlLabels:false content`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "fa:bell" }
|
||||
participant Alice
|
||||
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>`;
|
||||
imgSnapshotTest(diagram, {
|
||||
look,
|
||||
htmlLabels: false,
|
||||
flowchart: { htmlLabels: false },
|
||||
});
|
||||
});
|
||||
|
||||
it(`with styles applied to participant`, () => {
|
||||
// style by participant id
|
||||
const diagram = `
|
||||
sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "fa:bell" }
|
||||
participant Alice
|
||||
Bob->>Alice: Styled participant
|
||||
`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`with classDef and class application`, () => {
|
||||
const diagram = `
|
||||
sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "fa:bell" }
|
||||
participant Alice
|
||||
Bob->>Alice: Classed participant
|
||||
`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Colored emoji icon tests (analogous to the flowchart colored icon tests), no direction line.
|
||||
describe('SequenceDiagram colored icon participant', () => {
|
||||
it('colored emoji icon without styles', () => {
|
||||
const icon = 'fluent-emoji:tropical-fish';
|
||||
const diagram = `
|
||||
sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "${icon}" }
|
||||
Note over Bob: colored emoji icon
|
||||
`;
|
||||
imgSnapshotTest(diagram);
|
||||
});
|
||||
|
||||
it('colored emoji icon with styles', () => {
|
||||
const icon = 'fluent-emoji:tropical-fish';
|
||||
const diagram = `
|
||||
sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "${icon}" }
|
||||
`;
|
||||
imgSnapshotTest(diagram);
|
||||
});
|
||||
});
|
||||
|
||||
// Mixed scenario: multiple interactions, still no direction line.
|
||||
describe('SequenceDiagram icon participant with multiple interactions', () => {
|
||||
const icon = 'fa:bell-slash';
|
||||
it('icon participant interacts with two normal participants', () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "icon", icon: "${icon}" }
|
||||
participant Alice
|
||||
participant Carol
|
||||
Bob->>Alice: Ping
|
||||
Alice-->>Bob: Pong
|
||||
Bob->>Carol: Notify
|
||||
Note right of Bob: Icon side note`;
|
||||
imgSnapshotTest(diagram);
|
||||
});
|
||||
});
|
@@ -1,88 +0,0 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
|
||||
const looks = ['classic'] as const;
|
||||
|
||||
looks.forEach((look) => {
|
||||
describe(`SequenceDiagram image participants in ${look} look`, () => {
|
||||
it(`single participant with image`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
Note over Bob: Image participant`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`two participants, one image and one normal`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice
|
||||
Bob->>Alice: Hello`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`two image participants`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
Bob->>Alice: Hello
|
||||
Alice-->>Bob: Hi`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`with markdown htmlLabels:true content`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice
|
||||
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>
|
||||
Note over Bob,Alice: Mixed <em>HTML</em> and **markdown**`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`with markdown htmlLabels:false content`, () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice
|
||||
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>`;
|
||||
imgSnapshotTest(diagram, {
|
||||
look,
|
||||
htmlLabels: false,
|
||||
flowchart: { htmlLabels: false },
|
||||
});
|
||||
});
|
||||
|
||||
it(`with styles applied to participant`, () => {
|
||||
const diagram = `
|
||||
sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice
|
||||
Bob->>Alice: Styled participant
|
||||
`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
|
||||
it(`with classDef and class application`, () => {
|
||||
const diagram = `
|
||||
sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice
|
||||
Bob->>Alice: Classed participant
|
||||
`;
|
||||
imgSnapshotTest(diagram, { look });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Mixed scenario: multiple interactions, still no direction line.
|
||||
describe('SequenceDiagram image participant with multiple interactions', () => {
|
||||
const imageUrl = 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg';
|
||||
it('image participant interacts with two normal participants', () => {
|
||||
const diagram = `sequenceDiagram
|
||||
participant Bob@{ type: "image", "image": "${imageUrl}" }
|
||||
participant Alice
|
||||
participant Carol
|
||||
Bob->>Alice: Ping
|
||||
Alice-->>Bob: Pong
|
||||
Bob->>Carol: Notify
|
||||
Note right of Bob: Image side note`;
|
||||
imgSnapshotTest(diagram);
|
||||
});
|
||||
});
|
@@ -1,659 +0,0 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||
|
||||
const looks = ['classic'];
|
||||
const participantTypes = [
|
||||
{ type: 'participant', display: 'participant' },
|
||||
{ type: 'actor', display: 'actor' },
|
||||
{ type: 'boundary', display: 'boundary' },
|
||||
{ type: 'control', display: 'control' },
|
||||
{ type: 'entity', display: 'entity' },
|
||||
{ type: 'database', display: 'database' },
|
||||
{ type: 'collections', display: 'collections' },
|
||||
{ type: 'queue', display: 'queue' },
|
||||
];
|
||||
|
||||
const restrictedTypes = ['boundary', 'control', 'entity', 'database', 'collections', 'queue'];
|
||||
|
||||
const interactionTypes = ['->>', '-->>', '->', '-->', '-x', '--x', '->>+', '-->>+'];
|
||||
|
||||
const notePositions = ['left of', 'right of', 'over'];
|
||||
|
||||
function getParticipantLine(name, type, alias) {
|
||||
if (restrictedTypes.includes(type)) {
|
||||
return ` participant ${name}@{ "type" : "${type}" }\n`;
|
||||
} else if (alias) {
|
||||
return ` participant ${name}@{ "type" : "${type}" } \n`;
|
||||
} else {
|
||||
return ` participant ${name}@{ "type" : "${type}" }\n`;
|
||||
}
|
||||
}
|
||||
|
||||
looks.forEach((look) => {
|
||||
describe(`Sequence Diagram Tests - ${look} look`, () => {
|
||||
it('should render all participant types', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.forEach((pt, index) => {
|
||||
const name = `${pt.display}${index}`;
|
||||
diagramCode += getParticipantLine(name, pt.type);
|
||||
});
|
||||
for (let i = 0; i < participantTypes.length - 1; i++) {
|
||||
diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
|
||||
}
|
||||
imgSnapshotTest(diagramCode, { look, sequence: { diagramMarginX: 50, diagramMarginY: 10 } });
|
||||
});
|
||||
|
||||
it('should render all interaction types', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += getParticipantLine('A', 'actor');
|
||||
diagramCode += getParticipantLine('B', 'boundary');
|
||||
interactionTypes.forEach((interaction, index) => {
|
||||
diagramCode += ` A ${interaction} B: ${interaction} message ${index}\n`;
|
||||
});
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render participant creation and destruction', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.forEach((pt, index) => {
|
||||
const name = `${pt.display}${index}`;
|
||||
diagramCode += getParticipantLine('A', pt.type);
|
||||
diagramCode += getParticipantLine('B', pt.type);
|
||||
diagramCode += ` create participant ${name}@{ "type" : "${pt.type}" }\n`;
|
||||
diagramCode += ` A ->> ${name}: Hello ${pt.display}\n`;
|
||||
if (index % 2 === 0) {
|
||||
diagramCode += ` destroy ${name}\n`;
|
||||
}
|
||||
});
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render notes in all positions', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += getParticipantLine('A', 'actor');
|
||||
diagramCode += getParticipantLine('B', 'boundary');
|
||||
notePositions.forEach((position, index) => {
|
||||
diagramCode += ` Note ${position} A: Note ${position} ${index}\n`;
|
||||
});
|
||||
diagramCode += ` A ->> B: Message with notes\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render parallel interactions', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.slice(0, 4).forEach((pt, index) => {
|
||||
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
|
||||
});
|
||||
diagramCode += ` par Parallel actions\n`;
|
||||
for (let i = 0; i < 3; i += 2) {
|
||||
diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
|
||||
if (i < participantTypes.length - 2) {
|
||||
diagramCode += ` and\n`;
|
||||
}
|
||||
}
|
||||
diagramCode += ` end\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render alternative flows', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += getParticipantLine('A', 'actor');
|
||||
diagramCode += getParticipantLine('B', 'boundary');
|
||||
diagramCode += ` alt Successful case\n`;
|
||||
diagramCode += ` A ->> B: Request\n`;
|
||||
diagramCode += ` B -->> A: Success\n`;
|
||||
diagramCode += ` else Failure case\n`;
|
||||
diagramCode += ` A ->> B: Request\n`;
|
||||
diagramCode += ` B --x A: Failure\n`;
|
||||
diagramCode += ` end\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render loops', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.slice(0, 3).forEach((pt, index) => {
|
||||
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
|
||||
});
|
||||
diagramCode += ` loop For each participant\n`;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Message ${i}\n`;
|
||||
}
|
||||
diagramCode += ` end\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render boxes around groups', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += ` box Group 1\n`;
|
||||
participantTypes.slice(0, 3).forEach((pt, index) => {
|
||||
diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
|
||||
});
|
||||
diagramCode += ` end\n`;
|
||||
diagramCode += ` box rgb(200,220,255) Group 2\n`;
|
||||
participantTypes.slice(3, 6).forEach((pt, index) => {
|
||||
diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
|
||||
});
|
||||
diagramCode += ` end\n`;
|
||||
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[3].display}0: Cross-group message\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render with different font settings', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.slice(0, 3).forEach((pt, index) => {
|
||||
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
|
||||
});
|
||||
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Regular message\n`;
|
||||
diagramCode += ` Note right of ${participantTypes[1].display}1: Regular note\n`;
|
||||
imgSnapshotTest(diagramCode, {
|
||||
look,
|
||||
sequence: {
|
||||
actorFontFamily: 'courier',
|
||||
actorFontSize: 14,
|
||||
messageFontFamily: 'Arial',
|
||||
messageFontSize: 12,
|
||||
noteFontFamily: 'times',
|
||||
noteFontSize: 16,
|
||||
noteAlign: 'left',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Additional tests for specific combinations
|
||||
describe('Sequence Diagram Special Cases', () => {
|
||||
it('should render complex sequence with all features', () => {
|
||||
const diagramCode = `
|
||||
sequenceDiagram
|
||||
box rgb(200,220,255) Authentication
|
||||
actor User
|
||||
participant LoginUI@{ "type": "boundary" }
|
||||
participant AuthService@{ "type": "control" }
|
||||
participant UserDB@{ "type": "database" }
|
||||
end
|
||||
|
||||
box rgb(200,255,220) Order Processing
|
||||
participant Order@{ "type": "entity" }
|
||||
participant OrderQueue@{ "type": "queue" }
|
||||
participant AuditLogs@{ "type": "collections" }
|
||||
end
|
||||
|
||||
User ->> LoginUI: Enter credentials
|
||||
LoginUI ->> AuthService: Validate
|
||||
AuthService ->> UserDB: Query user
|
||||
UserDB -->> AuthService: User data
|
||||
alt Valid credentials
|
||||
AuthService -->> LoginUI: Success
|
||||
LoginUI -->> User: Welcome
|
||||
|
||||
par Place order
|
||||
User ->> Order: New order
|
||||
Order ->> OrderQueue: Process
|
||||
and
|
||||
Order ->> AuditLogs: Record
|
||||
end
|
||||
|
||||
loop Until confirmed
|
||||
OrderQueue ->> Order: Update status
|
||||
Order -->> User: Notification
|
||||
end
|
||||
else Invalid credentials
|
||||
AuthService --x LoginUI: Failure
|
||||
LoginUI --x User: Retry
|
||||
end
|
||||
`;
|
||||
imgSnapshotTest(diagramCode, {});
|
||||
});
|
||||
|
||||
it('should render with wrapped messages and notes', () => {
|
||||
const diagramCode = `
|
||||
sequenceDiagram
|
||||
participant A
|
||||
participant B
|
||||
|
||||
A ->> B: This is a very long message that should wrap properly in the diagram rendering
|
||||
Note over A,B: This is a very long note that should also wrap properly when rendered in the diagram
|
||||
|
||||
par Wrapped parallel
|
||||
A ->> B: Parallel message 1<br>with explicit line break
|
||||
and
|
||||
B ->> A: Parallel message 2<br>with explicit line break
|
||||
end
|
||||
|
||||
loop Wrapped loop
|
||||
Note right of B: This is a long note<br>in a loop
|
||||
A ->> B: Message in loop
|
||||
end
|
||||
`;
|
||||
imgSnapshotTest(diagramCode, { sequence: { wrap: true } });
|
||||
});
|
||||
describe('Sequence Diagram Rendering with Different Participant Types', () => {
|
||||
it('should render a sequence diagram with various participant types', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant User@{ "type": "actor" }
|
||||
participant AuthService@{ "type": "control" }
|
||||
participant UI@{ "type": "boundary" }
|
||||
participant OrderController@{ "type": "control" }
|
||||
participant Product@{ "type": "entity" }
|
||||
participant MongoDB@{ "type": "database" }
|
||||
participant Products@{ "type": "collections" }
|
||||
participant OrderQueue@{ "type": "queue" }
|
||||
User ->> UI: Login request
|
||||
UI ->> AuthService: Validate credentials
|
||||
AuthService -->> UI: Authentication token
|
||||
UI ->> OrderController: Place order
|
||||
OrderController ->> Product: Check availability
|
||||
Product -->> OrderController: Available
|
||||
OrderController ->> MongoDB: Save order
|
||||
MongoDB -->> OrderController: Order saved
|
||||
OrderController ->> OrderQueue: Process payment
|
||||
OrderQueue -->> User: Order confirmation
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render participant creation and destruction with different types', () => {
|
||||
imgSnapshotTest(`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "boundary" }
|
||||
Alice->>Bob: Hello Bob, how are you ?
|
||||
Bob->>Alice: Fine, thank you. And you?
|
||||
create participant Carl@{ "type" : "control" }
|
||||
Alice->>Carl: Hi Carl!
|
||||
create actor D as Donald
|
||||
Carl->>D: Hi!
|
||||
destroy Carl
|
||||
Alice-xCarl: We are too many
|
||||
destroy Bob
|
||||
Bob->>Alice: I agree
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle complex interactions between different participant types', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
box rgb(200,220,255) Authentication
|
||||
participant User@{ "type": "actor" }
|
||||
participant LoginUI@{ "type": "boundary" }
|
||||
participant AuthService@{ "type": "control" }
|
||||
participant UserDB@{ "type": "database" }
|
||||
end
|
||||
|
||||
box rgb(200,255,220) Order Processing
|
||||
participant Order@{ "type": "entity" }
|
||||
participant OrderQueue@{ "type": "queue" }
|
||||
participant AuditLogs@{ "type": "collections" }
|
||||
end
|
||||
|
||||
User ->> LoginUI: Enter credentials
|
||||
LoginUI ->> AuthService: Validate
|
||||
AuthService ->> UserDB: Query user
|
||||
UserDB -->> AuthService: User data
|
||||
|
||||
alt Valid credentials
|
||||
AuthService -->> LoginUI: Success
|
||||
LoginUI -->> User: Welcome
|
||||
|
||||
par Place order
|
||||
User ->> Order: New order
|
||||
Order ->> OrderQueue: Process
|
||||
and
|
||||
Order ->> AuditLogs: Record
|
||||
end
|
||||
|
||||
loop Until confirmed
|
||||
OrderQueue ->> Order: Update status
|
||||
Order -->> User: Notification
|
||||
end
|
||||
else Invalid credentials
|
||||
AuthService --x LoginUI: Failure
|
||||
LoginUI --x User: Retry
|
||||
end
|
||||
`,
|
||||
{ sequence: { useMaxWidth: false } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render parallel processes with different participant types', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Customer@{ "type": "actor" }
|
||||
participant Frontend@{ "type": "participant" }
|
||||
participant PaymentService@{ "type": "boundary" }
|
||||
participant InventoryManager@{ "type": "control" }
|
||||
participant Order@{ "type": "entity" }
|
||||
participant OrdersDB@{ "type": "database" }
|
||||
participant NotificationQueue@{ "type": "queue" }
|
||||
|
||||
Customer ->> Frontend: Place order
|
||||
Frontend ->> Order: Create order
|
||||
par Parallel Processing
|
||||
Order ->> PaymentService: Process payment
|
||||
and
|
||||
Order ->> InventoryManager: Reserve items
|
||||
end
|
||||
PaymentService -->> Order: Payment confirmed
|
||||
InventoryManager -->> Order: Items reserved
|
||||
Order ->> OrdersDB: Save finalized order
|
||||
OrdersDB -->> Order: Order saved
|
||||
Order ->> NotificationQueue: Send confirmation
|
||||
NotificationQueue -->> Customer: Order confirmation
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
it('should render different participant types with notes and loops', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Admin
|
||||
participant Dashboard
|
||||
participant AuthService@{ "type" : "boundary" }
|
||||
participant UserManager@{ "type" : "control" }
|
||||
participant UserProfile@{ "type" : "entity" }
|
||||
participant UserDB@{ "type" : "database" }
|
||||
participant Logs@{ "type" : "database" }
|
||||
|
||||
Admin ->> Dashboard: Open user management
|
||||
loop Authentication check
|
||||
Dashboard ->> AuthService: Verify admin rights
|
||||
AuthService ->> Dashboard: Access granted
|
||||
end
|
||||
Dashboard ->> UserManager: List users
|
||||
UserManager ->> UserDB: Query users
|
||||
UserDB ->> UserManager: Return user data
|
||||
Note right of UserDB: Encrypted data<br/>requires decryption
|
||||
UserManager ->> UserProfile: Format profiles
|
||||
UserProfile ->> UserManager: Formatted data
|
||||
UserManager ->> Dashboard: Display users
|
||||
Dashboard ->> Logs: Record access
|
||||
Logs ->> Admin: Audit trail
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render different participant types with alternative flows', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Client
|
||||
participant MobileApp
|
||||
participant CloudService@{ "type" : "boundary" }
|
||||
participant DataProcessor@{ "type" : "control" }
|
||||
participant Transaction@{ "type" : "entity" }
|
||||
participant TransactionsDB@{ "type" : "database" }
|
||||
participant EventBus@{ "type" : "queue" }
|
||||
|
||||
Client ->> MobileApp: Initiate transaction
|
||||
MobileApp ->> CloudService: Authenticate
|
||||
alt Authentication successful
|
||||
CloudService -->> MobileApp: Auth token
|
||||
MobileApp ->> DataProcessor: Process data
|
||||
DataProcessor ->> Transaction: Create transaction
|
||||
Transaction ->> TransactionsDB: Save record
|
||||
TransactionsDB -->> Transaction: Confirmation
|
||||
Transaction ->> EventBus: Publish event
|
||||
EventBus -->> Client: Notification
|
||||
else Authentication failed
|
||||
CloudService -->> MobileApp: Error
|
||||
MobileApp -->> Client: Show error
|
||||
end
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render different participant types with wrapping text', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant B@{ "type" : "boundary" }
|
||||
participant C@{ "type" : "control" }
|
||||
participant E@{ "type" : "entity" }
|
||||
participant DB@{ "type" : "database" }
|
||||
participant COL@{ "type" : "collections" }
|
||||
participant Q@{ "type" : "queue" }
|
||||
|
||||
FE ->> B: Another long message<br/>with explicit<br/>line breaks
|
||||
B -->> FE: Response message that is also quite long and needs to wrap
|
||||
FE ->> C: Process data
|
||||
C ->> E: Validate
|
||||
E -->> C: Validation result
|
||||
C ->> DB: Save
|
||||
DB -->> C: Save result
|
||||
C ->> COL: Log
|
||||
COL -->> Q: Forward
|
||||
Q -->> LongNameUser: Final response with confirmation of all actions taken
|
||||
`,
|
||||
{ sequence: { wrap: true } }
|
||||
);
|
||||
});
|
||||
|
||||
describe('Sequence Diagram - New Participant Types with Long Notes and Messages', () => {
|
||||
it('should render long notes left of boundary', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "boundary" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note left of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long notes left of control', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "control" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note left of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long notes right of entity', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "entity" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note right of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long notes right of database', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note right of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long notes over collections', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "collections" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note over Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long notes over queue', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "queue" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note over Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render notes over actor and boundary', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Charlie@{ "type" : "boundary" }
|
||||
note over Alice: Some note
|
||||
note over Charlie: Other note
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long messages from database to collections', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
participant Bob@{ "type" : "collections" }
|
||||
Alice->>Bob: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long messages from control to entity', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "control" }
|
||||
participant Bob@{ "type" : "entity" }
|
||||
Alice->>Bob:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long messages from queue to boundary', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "queue" }
|
||||
participant Bob@{ "type" : "boundary" }
|
||||
Alice->>Bob: I'm short
|
||||
Bob->>Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long messages from actor to database', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Bob@{ "type" : "database" }
|
||||
Alice->>Bob: I'm short
|
||||
Bob->>Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('svg size', () => {
|
||||
it('should render a sequence diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Bob@{ "type" : "boundary" }
|
||||
participant John@{ "type" : "control" }
|
||||
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%');
|
||||
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(820 * 0.95, 820 * 1.05);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a sequence diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Bob@{ "type" : "boundary" }
|
||||
participant John@{ "type" : "control" }
|
||||
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 width = parseFloat(svg.attr('width'));
|
||||
expect(width).to.be.within(820 * 0.95, 820 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -131,6 +131,22 @@
|
||||
|
||||
<body>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
elk:
|
||||
mergeEdges: false
|
||||
forceNodeModelOrder: false
|
||||
considerModelOrder: NONE
|
||||
|
||||
---
|
||||
flowchart TB
|
||||
a --> a1 & a2 & a3 & a4
|
||||
b --> b1 & b2
|
||||
b2 --> b3
|
||||
b1 --> b4</pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
treemap
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
|
@@ -2,219 +2,219 @@
|
||||
"durations": [
|
||||
{
|
||||
"spec": "cypress/integration/other/configuration.spec.js",
|
||||
"duration": 5672
|
||||
"duration": 5815
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/external-diagrams.spec.js",
|
||||
"duration": 1990
|
||||
"duration": 2035
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/ghsa.spec.js",
|
||||
"duration": 3186
|
||||
"duration": 3386
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/iife.spec.js",
|
||||
"duration": 1948
|
||||
"duration": 2089
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/interaction.spec.js",
|
||||
"duration": 11938
|
||||
"duration": 11578
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/rerender.spec.js",
|
||||
"duration": 1932
|
||||
"duration": 2119
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/xss.spec.js",
|
||||
"duration": 27237
|
||||
"duration": 27282
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/appli.spec.js",
|
||||
"duration": 3170
|
||||
"duration": 3377
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/architecture.spec.ts",
|
||||
"duration": 104
|
||||
"duration": 97
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/block.spec.js",
|
||||
"duration": 17390
|
||||
"duration": 18137
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/c4.spec.js",
|
||||
"duration": 5296
|
||||
"duration": 5455
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
|
||||
"duration": 39004
|
||||
"duration": 40850
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
|
||||
"duration": 37653
|
||||
"duration": 37964
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
|
||||
"duration": 23278
|
||||
"duration": 23446
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
|
||||
"duration": 36645
|
||||
"duration": 37207
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram.spec.js",
|
||||
"duration": 15418
|
||||
"duration": 16531
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
|
||||
"duration": 9684
|
||||
"duration": 9385
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/current.spec.js",
|
||||
"duration": 2570
|
||||
"duration": 2697
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
|
||||
"duration": 84687
|
||||
"duration": 88648
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/erDiagram.spec.js",
|
||||
"duration": 14819
|
||||
"duration": 15094
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
|
||||
"duration": 3371
|
||||
"duration": 3548
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
|
||||
"duration": 39925
|
||||
"duration": 44889
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
|
||||
"duration": 34694
|
||||
"duration": 30487
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
|
||||
"duration": 7137
|
||||
"duration": 7375
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
|
||||
"duration": 24740
|
||||
"duration": 24913
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
|
||||
"duration": 42077
|
||||
"duration": 51927
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart.spec.js",
|
||||
"duration": 30642
|
||||
"duration": 31676
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/gantt.spec.js",
|
||||
"duration": 18085
|
||||
"duration": 19897
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/gitGraph.spec.js",
|
||||
"duration": 50107
|
||||
"duration": 53450
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/iconShape.spec.ts",
|
||||
"duration": 276279
|
||||
"duration": 287035
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/imageShape.spec.ts",
|
||||
"duration": 56505
|
||||
"duration": 58555
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/info.spec.ts",
|
||||
"duration": 3036
|
||||
"duration": 3179
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/journey.spec.js",
|
||||
"duration": 6889
|
||||
"duration": 7099
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/kanban.spec.ts",
|
||||
"duration": 7353
|
||||
"duration": 7628
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/katex.spec.js",
|
||||
"duration": 3580
|
||||
"duration": 3764
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
|
||||
"duration": 2508
|
||||
"duration": 2573
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/mindmap.spec.ts",
|
||||
"duration": 10939
|
||||
"duration": 11269
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/newShapes.spec.ts",
|
||||
"duration": 149102
|
||||
"duration": 148389
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
|
||||
"duration": 113987
|
||||
"duration": 113395
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/packet.spec.ts",
|
||||
"duration": 4060
|
||||
"duration": 4714
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/pie.spec.ts",
|
||||
"duration": 5715
|
||||
"duration": 6446
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
|
||||
"duration": 8945
|
||||
"duration": 9133
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/radar.spec.js",
|
||||
"duration": 5337
|
||||
"duration": 5544
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/requirement.spec.js",
|
||||
"duration": 2643
|
||||
"duration": 2709
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
|
||||
"duration": 52072
|
||||
"duration": 55647
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/sankey.spec.ts",
|
||||
"duration": 6692
|
||||
"duration": 6751
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
|
||||
"duration": 35721
|
||||
"duration": 36618
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
|
||||
"duration": 26030
|
||||
"duration": 29642
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
|
||||
"duration": 16333
|
||||
"duration": 16037
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/theme.spec.js",
|
||||
"duration": 29287
|
||||
"duration": 30006
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/timeline.spec.ts",
|
||||
"duration": 8491
|
||||
"duration": 8451
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/treemap.spec.ts",
|
||||
"duration": 12291
|
||||
"duration": 11996
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/xyChart.spec.js",
|
||||
"duration": 20651
|
||||
"duration": 20627
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/zenuml.spec.js",
|
||||
"duration": 3218
|
||||
"duration": 3472
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -12,4 +12,4 @@
|
||||
|
||||
> `const` **configKeys**: `Set`<`string`>
|
||||
|
||||
Defined in: [packages/mermaid/src/defaultConfig.ts:290](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L290)
|
||||
Defined in: [packages/mermaid/src/defaultConfig.ts:292](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L292)
|
||||
|
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/config.type.ts:58](https://github.com/mermaid-
|
||||
|
||||
> `optional` **altFontFamily**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L122)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L132)
|
||||
|
||||
---
|
||||
|
||||
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/config.type.ts:122](https://github.com/mermaid
|
||||
|
||||
> `optional` **architecture**: `ArchitectureDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
|
||||
|
||||
---
|
||||
|
||||
@@ -34,7 +34,7 @@ Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid
|
||||
|
||||
> `optional` **arrowMarkerAbsolute**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L151)
|
||||
|
||||
Controls whether or arrow markers in html code are absolute paths or anchors.
|
||||
This matters if you are using base tag settings.
|
||||
@@ -45,7 +45,7 @@ This matters if you are using base tag settings.
|
||||
|
||||
> `optional` **block**: `BlockDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:211](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L211)
|
||||
|
||||
---
|
||||
|
||||
@@ -53,7 +53,7 @@ Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid
|
||||
|
||||
> `optional` **c4**: `C4DiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:208](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L208)
|
||||
|
||||
---
|
||||
|
||||
@@ -61,7 +61,7 @@ Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid
|
||||
|
||||
> `optional` **class**: `ClassDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
|
||||
|
||||
---
|
||||
|
||||
@@ -69,7 +69,7 @@ Defined in: [packages/mermaid/src/config.type.ts:187](https://github.com/mermaid
|
||||
|
||||
> `optional` **darkMode**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L113)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L123)
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +77,7 @@ Defined in: [packages/mermaid/src/config.type.ts:113](https://github.com/mermaid
|
||||
|
||||
> `optional` **deterministicIds**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
|
||||
|
||||
This option controls if the generated ids of nodes in the SVG are
|
||||
generated randomly or based on a seed.
|
||||
@@ -93,7 +93,7 @@ should not change unless content is changed.
|
||||
|
||||
> `optional` **deterministicIDSeed**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
|
||||
|
||||
This option is the optional seed for deterministic ids.
|
||||
If set to `undefined` but deterministicIds is `true`, a simple number iterator is used.
|
||||
@@ -105,7 +105,7 @@ You can set this attribute to base the seed on a static string.
|
||||
|
||||
> `optional` **dompurifyConfig**: `Config`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:213](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L213)
|
||||
|
||||
---
|
||||
|
||||
@@ -115,12 +115,24 @@ Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L91)
|
||||
|
||||
#### considerModelOrder?
|
||||
|
||||
> `optional` **considerModelOrder**: `"NONE"` | `"NODES_AND_EDGES"` | `"PREFER_EDGES"` | `"PREFER_NODES"`
|
||||
|
||||
Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting.
|
||||
|
||||
#### cycleBreakingStrategy?
|
||||
|
||||
> `optional` **cycleBreakingStrategy**: `"GREEDY"` | `"DEPTH_FIRST"` | `"INTERACTIVE"` | `"MODEL_ORDER"` | `"GREEDY_MODEL_ORDER"`
|
||||
|
||||
This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops.
|
||||
|
||||
#### forceNodeModelOrder?
|
||||
|
||||
> `optional` **forceNodeModelOrder**: `boolean`
|
||||
|
||||
The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES.
|
||||
|
||||
#### mergeEdges?
|
||||
|
||||
> `optional` **mergeEdges**: `boolean`
|
||||
@@ -139,7 +151,7 @@ Elk specific option affecting how nodes are placed.
|
||||
|
||||
> `optional` **er**: `ErDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
|
||||
|
||||
---
|
||||
|
||||
@@ -147,7 +159,7 @@ Defined in: [packages/mermaid/src/config.type.ts:189](https://github.com/mermaid
|
||||
|
||||
> `optional` **flowchart**: `FlowchartDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
|
||||
|
||||
---
|
||||
|
||||
@@ -155,7 +167,7 @@ Defined in: [packages/mermaid/src/config.type.ts:182](https://github.com/mermaid
|
||||
|
||||
> `optional` **fontFamily**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
|
||||
|
||||
Specifies the font to be used in the rendered diagrams.
|
||||
Can be any possible CSS `font-family`.
|
||||
@@ -167,7 +179,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
|
||||
|
||||
> `optional` **fontSize**: `number`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:215](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L215)
|
||||
|
||||
---
|
||||
|
||||
@@ -175,7 +187,7 @@ Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid
|
||||
|
||||
> `optional` **forceLegacyMathML**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173)
|
||||
|
||||
This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
|
||||
fonts and browser's MathML implementation, this option is recommended if consistent rendering is important.
|
||||
@@ -187,7 +199,7 @@ If set to true, ignores legacyMathML.
|
||||
|
||||
> `optional` **gantt**: `GanttDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
|
||||
|
||||
---
|
||||
|
||||
@@ -195,7 +207,7 @@ Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid
|
||||
|
||||
> `optional` **gitGraph**: `GitGraphDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:207](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L207)
|
||||
|
||||
---
|
||||
|
||||
@@ -213,7 +225,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
|
||||
|
||||
> `optional` **htmlLabels**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L114)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L124)
|
||||
|
||||
---
|
||||
|
||||
@@ -221,7 +233,7 @@ Defined in: [packages/mermaid/src/config.type.ts:114](https://github.com/mermaid
|
||||
|
||||
> `optional` **journey**: `JourneyDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
|
||||
|
||||
---
|
||||
|
||||
@@ -229,7 +241,7 @@ Defined in: [packages/mermaid/src/config.type.ts:185](https://github.com/mermaid
|
||||
|
||||
> `optional` **kanban**: `KanbanDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
|
||||
|
||||
---
|
||||
|
||||
@@ -247,7 +259,7 @@ Defines which layout algorithm to use for rendering the diagram.
|
||||
|
||||
> `optional` **legacyMathML**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L156)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:166](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L166)
|
||||
|
||||
This option specifies if Mermaid can expect the dependent to include KaTeX stylesheets for browsers
|
||||
without their own MathML implementation. If this option is disabled and MathML is not supported, the math
|
||||
@@ -260,7 +272,7 @@ fall back to legacy rendering for KaTeX.
|
||||
|
||||
> `optional` **logLevel**: `0` | `2` | `1` | `"trace"` | `"debug"` | `"info"` | `"warn"` | `"error"` | `"fatal"` | `3` | `4` | `5`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L127)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L137)
|
||||
|
||||
This option decides the amount of logging to be used by mermaid.
|
||||
|
||||
@@ -280,7 +292,7 @@ Defines which main look to use for the diagram.
|
||||
|
||||
> `optional` **markdownAutoWrap**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:216](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L216)
|
||||
|
||||
---
|
||||
|
||||
@@ -308,7 +320,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
> `optional` **mindmap**: `MindmapDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
|
||||
|
||||
---
|
||||
|
||||
@@ -316,7 +328,7 @@ Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid
|
||||
|
||||
> `optional` **packet**: `PacketDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210)
|
||||
|
||||
---
|
||||
|
||||
@@ -324,7 +336,7 @@ Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid
|
||||
|
||||
> `optional` **pie**: `PieDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
|
||||
|
||||
---
|
||||
|
||||
@@ -332,7 +344,7 @@ Defined in: [packages/mermaid/src/config.type.ts:190](https://github.com/mermaid
|
||||
|
||||
> `optional` **quadrantChart**: `QuadrantChartConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
|
||||
|
||||
---
|
||||
|
||||
@@ -340,7 +352,7 @@ Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid
|
||||
|
||||
> `optional` **radar**: `RadarDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
|
||||
|
||||
---
|
||||
|
||||
@@ -348,7 +360,7 @@ Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid
|
||||
|
||||
> `optional` **requirement**: `RequirementDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
|
||||
|
||||
---
|
||||
|
||||
@@ -356,7 +368,7 @@ Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid
|
||||
|
||||
> `optional` **sankey**: `SankeyDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L209)
|
||||
|
||||
---
|
||||
|
||||
@@ -364,7 +376,7 @@ Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid
|
||||
|
||||
> `optional` **secure**: `string`\[]
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L148)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:158](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L158)
|
||||
|
||||
This option controls which `currentConfig` keys are considered secure and
|
||||
can only be changed via call to `mermaid.initialize`.
|
||||
@@ -376,7 +388,7 @@ This prevents malicious graph directives from overriding a site's default securi
|
||||
|
||||
> `optional` **securityLevel**: `"strict"` | `"loose"` | `"antiscript"` | `"sandbox"`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
|
||||
|
||||
Level of trust for parsed diagram
|
||||
|
||||
@@ -386,7 +398,7 @@ Level of trust for parsed diagram
|
||||
|
||||
> `optional` **sequence**: `SequenceDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
|
||||
|
||||
---
|
||||
|
||||
@@ -394,7 +406,7 @@ Defined in: [packages/mermaid/src/config.type.ts:183](https://github.com/mermaid
|
||||
|
||||
> `optional` **startOnLoad**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:135](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L135)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:145](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L145)
|
||||
|
||||
Dictates whether mermaid starts on Page load
|
||||
|
||||
@@ -404,7 +416,7 @@ Dictates whether mermaid starts on Page load
|
||||
|
||||
> `optional` **state**: `StateDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
|
||||
|
||||
---
|
||||
|
||||
@@ -412,7 +424,7 @@ Defined in: [packages/mermaid/src/config.type.ts:188](https://github.com/mermaid
|
||||
|
||||
> `optional` **suppressErrorRendering**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:222](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L222)
|
||||
|
||||
Suppresses inserting 'Syntax error' diagram in the DOM.
|
||||
This is useful when you want to control how to handle syntax errors in your application.
|
||||
@@ -450,7 +462,7 @@ Defined in: [packages/mermaid/src/config.type.ts:65](https://github.com/mermaid-
|
||||
|
||||
> `optional` **timeline**: `TimelineDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
|
||||
|
||||
---
|
||||
|
||||
@@ -458,7 +470,7 @@ Defined in: [packages/mermaid/src/config.type.ts:186](https://github.com/mermaid
|
||||
|
||||
> `optional` **wrap**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:214](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L214)
|
||||
|
||||
---
|
||||
|
||||
@@ -466,4 +478,4 @@ Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid
|
||||
|
||||
> `optional` **xyChart**: `XYChartConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
|
||||
|
@@ -74,166 +74,6 @@ sequenceDiagram
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
### Boundary
|
||||
|
||||
If you want to use the boundary symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "boundary" }
|
||||
participant Bob
|
||||
Alice->>Bob: Request from boundary
|
||||
Bob->>Alice: Response to boundary
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "boundary" }
|
||||
participant Bob
|
||||
Alice->>Bob: Request from boundary
|
||||
Bob->>Alice: Response to boundary
|
||||
```
|
||||
|
||||
### Control
|
||||
|
||||
If you want to use the control symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "control" }
|
||||
participant Bob
|
||||
Alice->>Bob: Control request
|
||||
Bob->>Alice: Control response
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "control" }
|
||||
participant Bob
|
||||
Alice->>Bob: Control request
|
||||
Bob->>Alice: Control response
|
||||
```
|
||||
|
||||
### Entity
|
||||
|
||||
If you want to use the entity symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "entity" }
|
||||
participant Bob
|
||||
Alice->>Bob: Entity request
|
||||
Bob->>Alice: Entity response
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "entity" }
|
||||
participant Bob
|
||||
Alice->>Bob: Entity request
|
||||
Bob->>Alice: Entity response
|
||||
```
|
||||
|
||||
### Database
|
||||
|
||||
If you want to use the database symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
participant Bob
|
||||
Alice->>Bob: DB query
|
||||
Bob->>Alice: DB result
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
participant Bob
|
||||
Alice->>Bob: DB query
|
||||
Bob->>Alice: DB result
|
||||
```
|
||||
|
||||
### Collections
|
||||
|
||||
If you want to use the collections symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "collections" }
|
||||
participant Bob
|
||||
Alice->>Bob: Collections request
|
||||
Bob->>Alice: Collections response
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "collections" }
|
||||
participant Bob
|
||||
Alice->>Bob: Collections request
|
||||
Bob->>Alice: Collections response
|
||||
```
|
||||
|
||||
### Queue
|
||||
|
||||
If you want to use the queue symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "queue" }
|
||||
participant Bob
|
||||
Alice->>Bob: Queue message
|
||||
Bob->>Alice: Queue response
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "queue" }
|
||||
participant Bob
|
||||
Alice->>Bob: Queue message
|
||||
Bob->>Alice: Queue response
|
||||
```
|
||||
|
||||
### Icon
|
||||
|
||||
If you want to use a custom icon for a participant, use the JSON configuration syntax as shown below. The `icon` value can be a FontAwesome icon name, emoji, or other supported icon identifier.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "icon", "icon": "fa:bell" }
|
||||
participant Bob
|
||||
Alice->>Bob: Icon participant
|
||||
Bob->>Alice: Response to icon
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "icon", "icon": "fa:bell" }
|
||||
participant Bob
|
||||
Alice->>Bob: Icon participant
|
||||
Bob->>Alice: Response to icon
|
||||
```
|
||||
|
||||
### Image
|
||||
|
||||
If you want to use a custom image for a participant, use the JSON configuration syntax as shown below. The `image` value should be a valid image URL.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Bob
|
||||
Alice->>Bob: Image participant
|
||||
Bob->>Alice: Response to image
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Bob
|
||||
Alice->>Bob: Image participant
|
||||
Bob->>Alice: Response to image
|
||||
```
|
||||
|
||||
### Aliases
|
||||
|
||||
The actor can have a convenient identifier and a descriptive label.
|
||||
|
@@ -766,7 +766,10 @@ export const render = async (
|
||||
id: 'root',
|
||||
layoutOptions: {
|
||||
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
|
||||
'elk.layered.crossingMinimization.forceNodeModelOrder': true,
|
||||
'elk.layered.crossingMinimization.forceNodeModelOrder':
|
||||
data4Layout.config.elk?.forceNodeModelOrder,
|
||||
'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,
|
||||
|
||||
'elk.algorithm': algorithm,
|
||||
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
|
||||
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
|
||||
|
@@ -109,6 +109,16 @@ export interface MermaidConfig {
|
||||
| 'INTERACTIVE'
|
||||
| 'MODEL_ORDER'
|
||||
| 'GREEDY_MODEL_ORDER';
|
||||
/**
|
||||
* The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES.
|
||||
*
|
||||
*/
|
||||
forceNodeModelOrder?: boolean;
|
||||
/**
|
||||
* Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting.
|
||||
*
|
||||
*/
|
||||
considerModelOrder?: 'NONE' | 'NODES_AND_EDGES' | 'PREFER_EDGES' | 'PREFER_NODES';
|
||||
};
|
||||
darkMode?: boolean;
|
||||
htmlLabels?: boolean;
|
||||
|
@@ -24,6 +24,8 @@ const config: RequiredDeep<MermaidConfig> = {
|
||||
// mergeEdges is needed here to be considered
|
||||
mergeEdges: false,
|
||||
nodePlacementStrategy: 'BRANDES_KOEPF',
|
||||
forceNodeModelOrder: false,
|
||||
considerModelOrder: 'NODES_AND_EDGES',
|
||||
},
|
||||
themeCSS: undefined,
|
||||
|
||||
|
@@ -14,7 +14,7 @@
|
||||
|
||||
// Special states for recognizing aliases
|
||||
// A special state for grabbing text up to the first comment/newline
|
||||
%x ID ALIAS LINE CONFIG CONFIG_DATA
|
||||
%x ID ALIAS LINE
|
||||
|
||||
%x acc_title
|
||||
%x acc_descr
|
||||
@@ -28,11 +28,6 @@
|
||||
\%%(?!\{)[^\n]* /* skip comments */
|
||||
[^\}]\%\%[^\n]* /* skip comments */
|
||||
[0-9]+(?=[ \n]+) return 'NUM';
|
||||
<ID>\@\{ { this.begin('CONFIG'); return 'CONFIG_START'; }
|
||||
<CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; }
|
||||
<CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; }
|
||||
<ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; }
|
||||
<ID>[^\<->\->:\n,;@]+?([\-]*[^\<->\->:\n,;@]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
||||
"box" { this.begin('LINE'); return 'box'; }
|
||||
"participant" { this.begin('ID'); return 'participant'; }
|
||||
"actor" { this.begin('ID'); return 'participant_actor'; }
|
||||
@@ -236,8 +231,6 @@ participant_statement
|
||||
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
||||
| 'participant_actor' actor 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant'; $$=$2;}
|
||||
| 'destroy' actor 'NEWLINE' {$2.type='destroyParticipant'; $$=$2;}
|
||||
| 'participant' actor_with_config 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant'; $$=$2;}
|
||||
|
||||
;
|
||||
|
||||
note_statement
|
||||
@@ -308,23 +301,6 @@ signal
|
||||
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
||||
;
|
||||
|
||||
actor_with_config
|
||||
: ACTOR config_object
|
||||
{
|
||||
$$ = {
|
||||
type: 'addParticipant',
|
||||
actor: $1,
|
||||
config: $2
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
config_object
|
||||
: CONFIG_START CONFIG_CONTENT CONFIG_END
|
||||
{
|
||||
$$ = $2.trim();
|
||||
}
|
||||
;
|
||||
// actor
|
||||
// : actor_participant
|
||||
// | actor_actor
|
||||
@@ -337,7 +313,7 @@ signaltype
|
||||
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
||||
| DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
|
||||
| SOLID_ARROW { $$ = yy.LINETYPE.SOLID; }
|
||||
| BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; }
|
||||
| BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; }
|
||||
| DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; }
|
||||
| BIDIRECTIONAL_DOTTED_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_DOTTED; }
|
||||
| SOLID_CROSS { $$ = yy.LINETYPE.SOLID_CROSS; }
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import * as yaml from 'js-yaml';
|
||||
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { ImperativeState } from '../../utils/imperativeState.js';
|
||||
@@ -13,7 +12,7 @@ import {
|
||||
setAccTitle,
|
||||
setDiagramTitle,
|
||||
} from '../common/commonDb.js';
|
||||
import type { Actor, AddMessageParams, Box, Message, Note, ParticipantMetaData } from './types.js';
|
||||
import type { Actor, AddMessageParams, Box, Message, Note } from './types.js';
|
||||
|
||||
interface SequenceState {
|
||||
prevActor?: string;
|
||||
@@ -76,19 +75,6 @@ const PLACEMENT = {
|
||||
OVER: 2,
|
||||
} as const;
|
||||
|
||||
export const PARTICIPANT_TYPE = {
|
||||
ACTOR: 'actor',
|
||||
BOUNDARY: 'boundary',
|
||||
COLLECTIONS: 'collections',
|
||||
CONTROL: 'control',
|
||||
DATABASE: 'database',
|
||||
ENTITY: 'entity',
|
||||
PARTICIPANT: 'participant',
|
||||
QUEUE: 'queue',
|
||||
ICON: 'icon',
|
||||
IMAGE: 'image',
|
||||
} as const;
|
||||
|
||||
export class SequenceDB implements DiagramDB {
|
||||
private readonly state = new ImperativeState<SequenceState>(() => ({
|
||||
prevActor: undefined,
|
||||
@@ -133,22 +119,9 @@ export class SequenceDB implements DiagramDB {
|
||||
id: string,
|
||||
name: string,
|
||||
description: { text: string; wrap?: boolean | null; type: string },
|
||||
type: string,
|
||||
metadata?: any
|
||||
type: string
|
||||
) {
|
||||
let assignedBox = this.state.records.currentBox;
|
||||
let doc;
|
||||
if (metadata !== undefined) {
|
||||
let yamlData;
|
||||
// detect if shapeData contains a newline character
|
||||
if (!metadata.includes('\n')) {
|
||||
yamlData = '{\n' + metadata + '\n}';
|
||||
} else {
|
||||
yamlData = metadata + '\n';
|
||||
}
|
||||
doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as ParticipantMetaData;
|
||||
}
|
||||
type = doc?.type ?? type;
|
||||
const old = this.state.records.actors.get(id);
|
||||
if (old) {
|
||||
// If already set and trying to set to a new one throw error
|
||||
@@ -187,7 +160,6 @@ export class SequenceDB implements DiagramDB {
|
||||
actorCnt: null,
|
||||
rectData: null,
|
||||
type: type ?? 'participant',
|
||||
doc: doc,
|
||||
});
|
||||
if (this.state.records.prevActor) {
|
||||
const prevActorInRecords = this.state.records.actors.get(this.state.records.prevActor);
|
||||
@@ -546,7 +518,7 @@ export class SequenceDB implements DiagramDB {
|
||||
});
|
||||
break;
|
||||
case 'addParticipant':
|
||||
this.addActor(param.actor, param.actor, param.description, param.draw, param.config);
|
||||
this.addActor(param.actor, param.actor, param.description, param.draw);
|
||||
break;
|
||||
case 'createParticipant':
|
||||
if (this.state.records.actors.has(param.actor)) {
|
||||
@@ -555,7 +527,7 @@ export class SequenceDB implements DiagramDB {
|
||||
);
|
||||
}
|
||||
this.state.records.lastCreated = param.actor;
|
||||
this.addActor(param.actor, param.actor, param.description, param.draw, param.config);
|
||||
this.addActor(param.actor, param.actor, param.description, param.draw);
|
||||
this.state.records.createdActors.set(param.actor, this.state.records.messages.length);
|
||||
break;
|
||||
case 'destroyParticipant':
|
||||
|
@@ -2058,231 +2058,4 @@ Bob->>Alice:Got it!
|
||||
expect(messages[0].from).toBe('Alice');
|
||||
expect(messages[0].to).toBe('Bob');
|
||||
});
|
||||
describe('when parsing extended participant syntax ', () => {
|
||||
it('should parse a message', async () => {
|
||||
const actor1 = 'database';
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
participant Bob@{ "type" : "database" }
|
||||
participant Carl@{ type: "database" }
|
||||
participant David@{ "type" : 'database' }
|
||||
participant Eve@{ type: 'database' }
|
||||
participant Favela@{ "type" : "database" }
|
||||
Bob->>+Alice: Hi Alice
|
||||
Alice->>+Bob: Hi Bob
|
||||
`);
|
||||
|
||||
const messages = diagram.db.getMessages();
|
||||
});
|
||||
|
||||
it('should parse a message', async () => {
|
||||
const actor1 = 'database';
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant lead
|
||||
participant dsa@{ "type" : "queue" }
|
||||
API->>+Database: getUserb
|
||||
Database-->>-API: userb
|
||||
dsa --> Database: hello
|
||||
`);
|
||||
|
||||
const messages = diagram.db.getMessages();
|
||||
});
|
||||
});
|
||||
describe('participant type parsing', () => {
|
||||
it('should parse boundary participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant boundary@{ "type" : "boundary" }
|
||||
boundary->boundary: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('boundary').type).toBe('boundary');
|
||||
expect(actors.get('boundary').description).toBe('boundary');
|
||||
});
|
||||
|
||||
it('should parse control participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant C@{ "type" : "control" }
|
||||
C->C: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('C').type).toBe('control');
|
||||
expect(actors.get('C').description).toBe('C');
|
||||
});
|
||||
|
||||
it('should parse entity participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant E@{ "type" : "entity" }
|
||||
E->E: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('E').type).toBe('entity');
|
||||
expect(actors.get('E').description).toBe('E');
|
||||
});
|
||||
|
||||
it('should parse database participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant D@{ "type" : "database" }
|
||||
D->D: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('D').type).toBe('database');
|
||||
expect(actors.get('D').description).toBe('D');
|
||||
});
|
||||
|
||||
it('should parse collections participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant L@{ "type" : "collections" }
|
||||
L->L: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('L').type).toBe('collections');
|
||||
expect(actors.get('L').description).toBe('L');
|
||||
});
|
||||
|
||||
it('should parse queue participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Q@{ "type" : "queue" }
|
||||
Q->Q: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('Q').type).toBe('queue');
|
||||
expect(actors.get('Q').description).toBe('Q');
|
||||
});
|
||||
});
|
||||
|
||||
describe('participant type parsing', () => {
|
||||
it('should parse actor participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant A@{ "type" : "queue" }
|
||||
A->A: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('A').type).toBe('queue');
|
||||
expect(actors.get('A').description).toBe('A');
|
||||
});
|
||||
|
||||
it('should parse participant participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant P@{ "type" : "database" }
|
||||
P->P: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('P').type).toBe('database');
|
||||
expect(actors.get('P').description).toBe('P');
|
||||
});
|
||||
|
||||
it('should parse boundary using actor keyword', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "collections" }
|
||||
participant Bob@{ "type" : "control" }
|
||||
Alice->>Bob: Hello Bob, how are you?
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('Alice').type).toBe('collections');
|
||||
expect(actors.get('Bob').type).toBe('control');
|
||||
expect(actors.get('Bob').description).toBe('Bob');
|
||||
});
|
||||
|
||||
it('should parse control using participant keyword', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant C@{ "type" : "control" }
|
||||
C->C: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('C').type).toBe('control');
|
||||
expect(actors.get('C').description).toBe('C');
|
||||
});
|
||||
|
||||
it('should parse entity using actor keyword', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant E@{ "type" : "entity" }
|
||||
E->E: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('E').type).toBe('entity');
|
||||
expect(actors.get('E').description).toBe('E');
|
||||
});
|
||||
});
|
||||
|
||||
describe('image and icon participant parsing', () => {
|
||||
it('should parse image participant with image URL', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Bob@{ "type": "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
Bob->>Bob: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('Bob').type).toBe('image');
|
||||
expect(actors.get('Bob').doc.image).toBe(
|
||||
'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg'
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse icon participant with icon name', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type": "icon", "icon": "fa:bell" }
|
||||
Alice->>Alice: test
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('Alice').type).toBe('icon');
|
||||
expect(actors.get('Alice').doc.icon).toBe('fa:bell');
|
||||
});
|
||||
|
||||
it('should parse two image participants', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Bob@{ "type": "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice@{ "type": "image", "image": "https://cdn.pixabay.com/photo/2016/11/29/09/32/animal-1867121_1280.jpg" }
|
||||
Bob->>Alice: Hello
|
||||
Alice-->>Bob: Hi
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('Bob').type).toBe('image');
|
||||
expect(actors.get('Bob').doc.image).toBe(
|
||||
'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg'
|
||||
);
|
||||
expect(actors.get('Alice').type).toBe('image');
|
||||
expect(actors.get('Alice').doc.image).toBe(
|
||||
'https://cdn.pixabay.com/photo/2016/11/29/09/32/animal-1867121_1280.jpg'
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse image participant with normal participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Bob@{ "type": "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Alice
|
||||
Bob->>Alice: Hello
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('Bob').type).toBe('image');
|
||||
expect(actors.get('Alice').type).toBe('participant');
|
||||
});
|
||||
|
||||
it('should parse icon participant with normal participant', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
participant Bob@{ "type": "icon", "icon": "fa:bell" }
|
||||
participant Alice
|
||||
Bob->>Alice: Hello
|
||||
`);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.get('Bob').type).toBe('icon');
|
||||
expect(actors.get('Alice').type).toBe('participant');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -10,7 +10,6 @@ import assignWithDepth from '../../assignWithDepth.js';
|
||||
import utils from '../../utils.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import type { Diagram } from '../../Diagram.js';
|
||||
import { PARTICIPANT_TYPE } from './sequenceDb.js';
|
||||
|
||||
let conf = {};
|
||||
|
||||
@@ -725,21 +724,11 @@ function adjustCreatedDestroyedData(
|
||||
msgModel.startx = msgModel.startx - adjustment;
|
||||
}
|
||||
}
|
||||
const actorArray = [
|
||||
PARTICIPANT_TYPE.ACTOR,
|
||||
PARTICIPANT_TYPE.CONTROL,
|
||||
PARTICIPANT_TYPE.ENTITY,
|
||||
PARTICIPANT_TYPE.DATABASE,
|
||||
PARTICIPANT_TYPE.ICON,
|
||||
PARTICIPANT_TYPE.IMAGE,
|
||||
];
|
||||
|
||||
// if it is a create message
|
||||
if (createdActors.get(msg.to) == index) {
|
||||
const actor = actors.get(msg.to);
|
||||
const adjustment = actorArray.includes(actor.type)
|
||||
? ACTOR_TYPE_WIDTH / 2 + 3
|
||||
: actor.width / 2 + 3;
|
||||
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
|
||||
receiverAdjustment(actor, adjustment);
|
||||
actor.starty = lineStartY - actor.height / 2;
|
||||
bounds.bumpVerticalPos(actor.height / 2);
|
||||
@@ -748,7 +737,7 @@ function adjustCreatedDestroyedData(
|
||||
else if (destroyedActors.get(msg.from) == index) {
|
||||
const actor = actors.get(msg.from);
|
||||
if (conf.mirrorActors) {
|
||||
const adjustment = actorArray.includes(actor.type) ? ACTOR_TYPE_WIDTH / 2 : actor.width / 2;
|
||||
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 : actor.width / 2;
|
||||
senderAdjustment(actor, adjustment);
|
||||
}
|
||||
actor.stopy = lineStartY - actor.height / 2;
|
||||
@@ -758,9 +747,7 @@ function adjustCreatedDestroyedData(
|
||||
else if (destroyedActors.get(msg.to) == index) {
|
||||
const actor = actors.get(msg.to);
|
||||
if (conf.mirrorActors) {
|
||||
const adjustment = actorArray.includes(actor.type)
|
||||
? ACTOR_TYPE_WIDTH / 2 + 3
|
||||
: actor.width / 2 + 3;
|
||||
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
|
||||
receiverAdjustment(actor, adjustment);
|
||||
}
|
||||
actor.stopy = lineStartY - actor.height / 2;
|
||||
@@ -1078,11 +1065,10 @@ export const draw = async function (_text: string, id: string, _version: string,
|
||||
for (const box of bounds.models.boxes) {
|
||||
box.height = bounds.getVerticalPos() - box.y;
|
||||
bounds.insert(box.x, box.y, box.x + box.width, box.height);
|
||||
const boxPadding = conf.boxMargin * 2;
|
||||
box.startx = box.x - boxPadding;
|
||||
box.starty = box.y - boxPadding * 0.25;
|
||||
box.stopx = box.startx + box.width + 2 * boxPadding;
|
||||
box.stopy = box.starty + box.height + boxPadding * 0.75;
|
||||
box.startx = box.x;
|
||||
box.starty = box.y;
|
||||
box.stopx = box.startx + box.width;
|
||||
box.stopy = box.starty + box.height;
|
||||
box.stroke = 'rgb(0,0,0, 0.5)';
|
||||
svgDraw.drawBox(diagram, box, conf);
|
||||
}
|
||||
@@ -1347,9 +1333,6 @@ async function calculateActorMargins(
|
||||
return (total += actors.get(aKey).width + (actors.get(aKey).margin || 0));
|
||||
}, 0);
|
||||
|
||||
const standardBoxPadding = conf.boxMargin * 8;
|
||||
totalWidth += standardBoxPadding;
|
||||
|
||||
totalWidth -= 2 * conf.boxTextMargin;
|
||||
if (box.wrap) {
|
||||
box.name = utils.wrapLabel(box.name, totalWidth - 2 * conf.wrapPadding, textFont);
|
||||
|
@@ -12,11 +12,6 @@ const getStyles = (options) =>
|
||||
.actor-line {
|
||||
stroke: ${options.actorLineColor};
|
||||
}
|
||||
|
||||
.innerArc {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: none;
|
||||
}
|
||||
|
||||
.messageLine0 {
|
||||
stroke-width: 1.5;
|
||||
@@ -120,7 +115,6 @@ const getStyles = (options) =>
|
||||
fill: ${options.actorBkg};
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
export default getStyles;
|
||||
|
@@ -3,7 +3,6 @@ import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import * as configApi from '../../config.js';
|
||||
import { getIconSVG } from '../../rendering-util/icons.js';
|
||||
|
||||
export const ACTOR_TYPE_WIDTH = 18 * 2;
|
||||
const TOP_ACTOR_CLASS = 'actor-top';
|
||||
@@ -319,89 +318,6 @@ export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => {
|
||||
});
|
||||
};
|
||||
|
||||
const drawActorTypeIcon = async function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + actor.height / 2;
|
||||
|
||||
const line = elem.append('g').lower();
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
line
|
||||
.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', centerY + 25)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
actor.actorCnt = actorCnt;
|
||||
}
|
||||
|
||||
const actElem = elem.append('g');
|
||||
let cssClass = 'actor-icon';
|
||||
if (isFooter) {
|
||||
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
|
||||
actElem.attr('class', cssClass);
|
||||
actElem.attr('name', actor.name);
|
||||
|
||||
// Define the size of the square and icon
|
||||
const iconSize = actor.width / 5;
|
||||
const squareX = center - iconSize / 2;
|
||||
const squareY = !isFooter ? actorY + 10 : actorY;
|
||||
|
||||
// Draw a square rectangle for the actor icon background
|
||||
|
||||
actElem
|
||||
.append('rect')
|
||||
.attr('x', squareX)
|
||||
.attr('y', squareY)
|
||||
.attr('width', iconSize)
|
||||
.attr('height', iconSize)
|
||||
.attr('rx', 3) // rounded corners, optional
|
||||
.attr('ry', 3)
|
||||
.attr('fill', 'none'); // light gray background or customize as needed
|
||||
|
||||
// Render icon SVG inside the rectangle
|
||||
const iconGroup = actElem.append('g').attr('transform', `translate(${squareX}, ${squareY})`);
|
||||
|
||||
iconGroup
|
||||
.append('svg')
|
||||
.attr('width', iconSize)
|
||||
.attr('height', iconSize)
|
||||
.html(
|
||||
`<g>${await getIconSVG(actor.doc.icon, {
|
||||
height: iconSize,
|
||||
width: iconSize,
|
||||
fallbackPrefix: '',
|
||||
})}</g>`
|
||||
);
|
||||
|
||||
// Add text label below icon
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
actElem,
|
||||
actor.x,
|
||||
actorY + (!isFooter ? 40 : 30), // positioning below the square icon
|
||||
actor.width,
|
||||
20,
|
||||
{ class: 'actor-icon-text' },
|
||||
conf
|
||||
);
|
||||
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height + (conf.sequence?.labelBoxHeight ?? 0);
|
||||
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws an actor in the diagram with the attached line
|
||||
*
|
||||
@@ -494,775 +410,6 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
||||
|
||||
return height;
|
||||
};
|
||||
const drawActorTypeImage = async function (elem, actor, conf, isFooter) {
|
||||
const img = new Image();
|
||||
img.src = actor.doc.image ?? '';
|
||||
await img.decode();
|
||||
|
||||
const imageNaturalWidth = Number(img.naturalWidth.toString().replace('px', ''));
|
||||
const imageNaturalHeight = Number(img.naturalHeight.toString().replace('px', ''));
|
||||
|
||||
actor.doc.imageAspectRatio = imageNaturalWidth / imageNaturalHeight;
|
||||
|
||||
// Calculate image dimensions with proper sizing logic
|
||||
let imageWidth, imageHeight;
|
||||
|
||||
// Check if custom dimensions are provided and valid
|
||||
const hasValidCustomDimensions =
|
||||
actor.doc.height && actor.doc.height > 10 && actor.doc.width && actor.doc.width > 10;
|
||||
|
||||
if (hasValidCustomDimensions) {
|
||||
if (actor.doc.constraint === 'on') {
|
||||
// Maintain aspect ratio with constraint
|
||||
const customAspectRatio = imageNaturalWidth / imageNaturalHeight;
|
||||
|
||||
if (customAspectRatio > actor.doc.imageAspectRatio) {
|
||||
// Width is the limiting factor
|
||||
imageHeight = actor.doc.height;
|
||||
imageWidth = actor.doc.height * actor.doc.imageAspectRatio;
|
||||
} else {
|
||||
// Height is the limiting factor
|
||||
imageWidth = actor.doc.width;
|
||||
imageHeight = actor.doc.width / actor.doc.imageAspectRatio;
|
||||
}
|
||||
} else {
|
||||
// Use custom dimensions without maintaining aspect ratio
|
||||
imageWidth = actor.doc.width;
|
||||
imageHeight = actor.doc.height;
|
||||
}
|
||||
} else {
|
||||
// Use default sizing based on actor width
|
||||
const defaultImageSize = actor.width / 3.5;
|
||||
|
||||
// Ensure minimum and maximum sizes
|
||||
const minSize = 30;
|
||||
const maxSize = actor.width * 0.8;
|
||||
|
||||
if (actor.doc.imageAspectRatio >= 1) {
|
||||
// Landscape or square image
|
||||
imageWidth = Math.min(Math.max(defaultImageSize, minSize), maxSize);
|
||||
imageHeight = imageWidth / actor.doc.imageAspectRatio;
|
||||
} else {
|
||||
// Portrait image
|
||||
imageHeight = Math.min(Math.max(defaultImageSize, minSize), maxSize);
|
||||
imageWidth = imageHeight * actor.doc.imageAspectRatio;
|
||||
}
|
||||
|
||||
// Ensure the image doesn't exceed actor bounds
|
||||
if (imageWidth > actor.width * 0.9) {
|
||||
imageWidth = actor.width * 0.9;
|
||||
imageHeight = imageWidth / actor.doc.imageAspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + imageHeight;
|
||||
|
||||
// Calculate positioning
|
||||
const squareX = center - imageWidth / 2;
|
||||
let squareY;
|
||||
|
||||
if (isFooter) {
|
||||
squareY = actorY + (imageHeight - imageHeight * 1);
|
||||
} else {
|
||||
squareY = actorY + 5;
|
||||
}
|
||||
|
||||
// Calculate text position based on image position and size
|
||||
const textY = !isFooter ? squareY + imageHeight : actorY + imageHeight; // Place text below image for header
|
||||
|
||||
// Draw actor line for non-footer elements
|
||||
const x = center;
|
||||
const y = centerY + (isFooter ? 0 : imageHeight / 2); // Adjust line start based on image height
|
||||
const line = elem.append('g').lower();
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
line
|
||||
.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', x)
|
||||
.attr('y1', y) // Adjust line start based on image height
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
actor.actorCnt = actorCnt;
|
||||
}
|
||||
|
||||
const actElem = elem.append('g');
|
||||
let cssClass = 'actor-image';
|
||||
if (isFooter) {
|
||||
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
|
||||
actElem.attr('class', cssClass);
|
||||
actElem.attr('name', actor.name);
|
||||
|
||||
// Draw background rectangle for the actor image
|
||||
actElem
|
||||
.append('rect')
|
||||
.attr('x', squareX)
|
||||
.attr('y', squareY)
|
||||
.attr('width', imageWidth)
|
||||
.attr('height', imageHeight)
|
||||
.attr('rx', 3)
|
||||
.attr('ry', 3)
|
||||
.attr('fill', 'white')
|
||||
.attr('stroke', '#ddd')
|
||||
.attr('stroke-width', '1px');
|
||||
|
||||
// Create clipping path for the image
|
||||
const clipId = `clip-actor-${actorCnt || 'footer'}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
actElem
|
||||
.append('defs')
|
||||
.append('clipPath')
|
||||
.attr('id', clipId)
|
||||
.append('rect')
|
||||
.attr('x', squareX)
|
||||
.attr('y', squareY)
|
||||
.attr('width', imageWidth)
|
||||
.attr('height', imageHeight)
|
||||
.attr('rx', 3)
|
||||
.attr('ry', 3);
|
||||
|
||||
// Render image inside the rectangle
|
||||
const imageGroup = actElem.append('g');
|
||||
|
||||
if (actor.doc.image) {
|
||||
imageGroup
|
||||
.append('image')
|
||||
.attr('x', squareX)
|
||||
.attr('y', squareY)
|
||||
.attr('width', imageWidth)
|
||||
.attr('height', imageHeight)
|
||||
.attr('href', img.src)
|
||||
.attr('preserveAspectRatio', actor.doc.constraint === 'on' ? 'xMidYMid meet' : 'none');
|
||||
}
|
||||
|
||||
// Add text label
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
actElem,
|
||||
actor.x,
|
||||
textY,
|
||||
actor.width,
|
||||
20,
|
||||
{ class: 'actor-image-text' },
|
||||
conf
|
||||
);
|
||||
|
||||
// Calculate final bounds and update actor height
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height + (conf.sequence?.labelBoxHeight ?? 0);
|
||||
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws an actor in the diagram with the attached line
|
||||
*
|
||||
* @param {any} elem - The diagram we'll draw to.
|
||||
* @param {any} actor - The actor to draw.
|
||||
* @param {any} conf - DrawText implementation discriminator object
|
||||
* @param {boolean} isFooter - If the actor is the footer one
|
||||
*/
|
||||
const drawActorTypeCollections = function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + actor.height;
|
||||
|
||||
const boxplusLineGroup = elem.append('g').lower();
|
||||
var g = boxplusLineGroup;
|
||||
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
if (Object.keys(actor.links || {}).length && !conf.forceMenus) {
|
||||
g.attr('onclick', popupMenuToggle(`actor${actorCnt}_popup`)).attr('cursor', 'pointer');
|
||||
}
|
||||
g.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', centerY)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line 200')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
|
||||
g = boxplusLineGroup.append('g');
|
||||
actor.actorCnt = actorCnt;
|
||||
|
||||
if (actor.links != null) {
|
||||
g.attr('id', 'root-' + actorCnt);
|
||||
}
|
||||
}
|
||||
|
||||
const rect = svgDrawCommon.getNoteRect();
|
||||
var cssclass = 'actor';
|
||||
if (actor.properties?.class) {
|
||||
cssclass = actor.properties.class;
|
||||
} else {
|
||||
rect.fill = '#eaeaea';
|
||||
}
|
||||
if (isFooter) {
|
||||
cssclass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssclass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
rect.x = actor.x;
|
||||
rect.y = actorY;
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = cssclass;
|
||||
// rect.rx = 3;
|
||||
// rect.ry = 3;
|
||||
rect.name = actor.name;
|
||||
|
||||
// 🔹 DRAW STACKED RECTANGLES
|
||||
const offset = 6;
|
||||
const shadowRect = {
|
||||
...rect,
|
||||
x: rect.x + offset,
|
||||
y: rect.y + (isFooter ? +offset : -offset),
|
||||
class: 'actor',
|
||||
};
|
||||
drawRect(g, shadowRect);
|
||||
const rectElem = drawRect(g, rect); // draw main rectangle on top
|
||||
actor.rectData = rect;
|
||||
|
||||
if (actor.properties?.icon) {
|
||||
const iconSrc = actor.properties.icon.trim();
|
||||
if (iconSrc.charAt(0) === '@') {
|
||||
svgDrawCommon.drawEmbeddedImage(g, rect.x + rect.width - 20, rect.y + 10, iconSrc.substr(1));
|
||||
} else {
|
||||
svgDrawCommon.drawImage(g, rect.x + rect.width - 20, rect.y + 10, iconSrc);
|
||||
}
|
||||
}
|
||||
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
g,
|
||||
rect.x,
|
||||
rect.y,
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: `actor ${ACTOR_BOX_CLASS}` },
|
||||
conf
|
||||
);
|
||||
|
||||
let height = actor.height;
|
||||
if (rectElem.node) {
|
||||
const bounds = rectElem.node().getBBox();
|
||||
actor.height = bounds.height;
|
||||
height = bounds.height;
|
||||
}
|
||||
|
||||
return height;
|
||||
};
|
||||
|
||||
const drawActorTypeQueue = function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + actor.height;
|
||||
|
||||
const boxplusLineGroup = elem.append('g').lower();
|
||||
let g = boxplusLineGroup;
|
||||
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
if (Object.keys(actor.links || {}).length && !conf.forceMenus) {
|
||||
g.attr('onclick', popupMenuToggle(`actor${actorCnt}_popup`)).attr('cursor', 'pointer');
|
||||
}
|
||||
g.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', centerY)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line 200')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
|
||||
g = boxplusLineGroup.append('g');
|
||||
actor.actorCnt = actorCnt;
|
||||
|
||||
if (actor.links != null) {
|
||||
g.attr('id', 'root-' + actorCnt);
|
||||
}
|
||||
}
|
||||
|
||||
const rect = svgDrawCommon.getNoteRect();
|
||||
let cssclass = 'actor';
|
||||
if (actor.properties?.class) {
|
||||
cssclass = actor.properties.class;
|
||||
} else {
|
||||
rect.fill = '#eaeaea';
|
||||
}
|
||||
|
||||
if (isFooter) {
|
||||
cssclass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssclass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
|
||||
rect.x = actor.x;
|
||||
rect.y = actorY;
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = cssclass;
|
||||
rect.name = actor.name;
|
||||
|
||||
// Cylinder dimensions
|
||||
const ry = rect.height / 2;
|
||||
const rx = ry / (2.5 + rect.height / 50);
|
||||
|
||||
// Cylinder base group
|
||||
const cylinderGroup = g.append('g');
|
||||
const cylinderArc = g.append('g');
|
||||
|
||||
// Main cylinder body
|
||||
cylinderGroup
|
||||
.append('path')
|
||||
.attr(
|
||||
'd',
|
||||
`M ${rect.x},${rect.y + ry}
|
||||
a ${rx},${ry} 0 0 0 0,${rect.height}
|
||||
h ${rect.width - 2 * rx}
|
||||
a ${rx},${ry} 0 0 0 0,-${rect.height}
|
||||
Z
|
||||
`
|
||||
)
|
||||
.attr('class', cssclass);
|
||||
cylinderArc
|
||||
.append('path')
|
||||
.attr(
|
||||
'd',
|
||||
`M ${rect.x},${rect.y + ry}
|
||||
a ${rx},${ry} 0 0 0 0,${rect.height}`
|
||||
)
|
||||
.attr('stroke', '#666')
|
||||
.attr('stroke-width', '1px')
|
||||
.attr('class', cssclass);
|
||||
|
||||
cylinderGroup.attr('transform', `translate(${rx}, ${-(rect.height / 2)})`);
|
||||
cylinderArc.attr('transform', `translate(${rect.width - rx}, ${-rect.height / 2})`);
|
||||
|
||||
actor.rectData = rect;
|
||||
|
||||
if (actor.properties?.icon) {
|
||||
const iconSrc = actor.properties.icon.trim();
|
||||
const iconX = rect.x + rect.width - 20;
|
||||
const iconY = rect.y + 10;
|
||||
if (iconSrc.charAt(0) === '@') {
|
||||
svgDrawCommon.drawEmbeddedImage(g, iconX, iconY, iconSrc.substr(1));
|
||||
} else {
|
||||
svgDrawCommon.drawImage(g, iconX, iconY, iconSrc);
|
||||
}
|
||||
}
|
||||
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
g,
|
||||
rect.x,
|
||||
rect.y,
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: `actor ${ACTOR_BOX_CLASS}` },
|
||||
conf
|
||||
);
|
||||
|
||||
let height = actor.height;
|
||||
const lastPath = cylinderGroup.select('path:last-child');
|
||||
if (lastPath.node()) {
|
||||
const bounds = lastPath.node().getBBox();
|
||||
actor.height = bounds.height;
|
||||
height = bounds.height;
|
||||
}
|
||||
|
||||
return height;
|
||||
};
|
||||
|
||||
const drawActorTypeControl = function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + 75;
|
||||
|
||||
const line = elem.append('g').lower();
|
||||
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
line
|
||||
.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', centerY)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line 200')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
|
||||
actor.actorCnt = actorCnt;
|
||||
}
|
||||
const actElem = elem.append('g');
|
||||
let cssClass = ACTOR_MAN_FIGURE_CLASS;
|
||||
if (isFooter) {
|
||||
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
actElem.attr('class', cssClass);
|
||||
actElem.attr('name', actor.name);
|
||||
|
||||
const rect = svgDrawCommon.getNoteRect();
|
||||
rect.x = actor.x;
|
||||
rect.y = actorY;
|
||||
rect.fill = '#eaeaea';
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = 'actor';
|
||||
|
||||
const cx = actor.x + actor.width / 2;
|
||||
const cy = actorY + 30;
|
||||
const r = 18;
|
||||
|
||||
actElem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'filled-head-control')
|
||||
.attr('refX', 15.5)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 20)
|
||||
.attr('markerHeight', 28)
|
||||
.attr('orient', '180')
|
||||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z');
|
||||
|
||||
// Draw the base circle
|
||||
actElem
|
||||
.append('circle')
|
||||
.attr('cx', cx)
|
||||
.attr('cy', cy)
|
||||
.attr('r', r)
|
||||
.attr('fill', '#eaeaf7')
|
||||
.attr('stroke', '#666')
|
||||
.attr('stroke-width', 1.2);
|
||||
|
||||
// Draw looping arrow as arc path
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('marker-end', 'url(#filled-head-control)')
|
||||
.attr('transform', `translate(${cx}, ${cy - r})`);
|
||||
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height + 2 * (conf?.sequence?.labelBoxHeight ?? 0);
|
||||
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
actElem,
|
||||
rect.x,
|
||||
rect.y + 30,
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
|
||||
conf
|
||||
);
|
||||
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
const drawActorTypeEntity = function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + 75;
|
||||
|
||||
const line = elem.append('g').lower();
|
||||
|
||||
const actElem = elem.append('g');
|
||||
let cssClass = ACTOR_MAN_FIGURE_CLASS;
|
||||
if (isFooter) {
|
||||
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
actElem.attr('class', cssClass);
|
||||
actElem.attr('name', actor.name);
|
||||
|
||||
const rect = svgDrawCommon.getNoteRect();
|
||||
rect.x = actor.x;
|
||||
rect.y = actorY;
|
||||
rect.fill = '#eaeaea';
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = 'actor';
|
||||
|
||||
const cx = actor.x + actor.width / 2;
|
||||
const cy = actorY + 25;
|
||||
const r = 18;
|
||||
|
||||
actElem
|
||||
.append('circle')
|
||||
.attr('cx', cx)
|
||||
.attr('cy', cy)
|
||||
.attr('r', r)
|
||||
.attr('width', actor.width)
|
||||
.attr('height', actor.height);
|
||||
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('x1', cx - r)
|
||||
.attr('x2', cx + r)
|
||||
.attr('y1', cy + r)
|
||||
.attr('y2', cy + r)
|
||||
.attr('stroke', '#333')
|
||||
.attr('stroke-width', 2);
|
||||
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height + (conf?.sequence?.labelBoxHeight ?? 0);
|
||||
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
line
|
||||
.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', centerY)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line 200')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
|
||||
actor.actorCnt = actorCnt;
|
||||
}
|
||||
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
actElem,
|
||||
rect.x,
|
||||
rect.y + (!isFooter ? (cy + r - actorY) / 2 : (cy - actorY) / 2 + r + 2),
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
|
||||
conf
|
||||
);
|
||||
|
||||
if (!isFooter) {
|
||||
actElem.attr('transform', `translate(${0}, ${r / 2})`);
|
||||
} else {
|
||||
actElem.attr('transform', `translate(${0}, ${r / 2})`);
|
||||
}
|
||||
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
const drawActorTypeDatabase = function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + actor.height + 2 * conf.boxTextMargin;
|
||||
|
||||
const boxplusLineGroup = elem.append('g').lower();
|
||||
let g = boxplusLineGroup;
|
||||
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
if (Object.keys(actor.links || {}).length && !conf.forceMenus) {
|
||||
g.attr('onclick', popupMenuToggle(`actor${actorCnt}_popup`)).attr('cursor', 'pointer');
|
||||
}
|
||||
g.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', centerY)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line 200')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
|
||||
g = boxplusLineGroup.append('g');
|
||||
actor.actorCnt = actorCnt;
|
||||
|
||||
if (actor.links != null) {
|
||||
g.attr('id', 'root-' + actorCnt);
|
||||
}
|
||||
}
|
||||
|
||||
const rect = svgDrawCommon.getNoteRect();
|
||||
|
||||
let cssclass = 'actor';
|
||||
if (actor.properties?.class) {
|
||||
cssclass = actor.properties.class;
|
||||
} else {
|
||||
rect.fill = '#eaeaea';
|
||||
}
|
||||
|
||||
if (isFooter) {
|
||||
cssclass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssclass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
|
||||
rect.x = actor.x;
|
||||
rect.y = actorY;
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = cssclass;
|
||||
rect.name = actor.name;
|
||||
|
||||
// Cylinder dimensions
|
||||
rect.x = actor.x;
|
||||
rect.y = actorY;
|
||||
const w = rect.width / 4;
|
||||
const h = rect.height * 0.8;
|
||||
const rx = w / 2;
|
||||
const ry = rx / (2.5 + w / 50);
|
||||
|
||||
// Cylinder base group
|
||||
const cylinderGroup = g.append('g');
|
||||
|
||||
const d = `
|
||||
M ${rect.x},${rect.y + ry}
|
||||
a ${rx},${ry} 0 0 0 ${w},0
|
||||
a ${rx},${ry} 0 0 0 -${w},0
|
||||
l 0,${h - 2 * ry}
|
||||
a ${rx},${ry} 0 0 0 ${w},0
|
||||
l 0,-${h - 2 * ry}
|
||||
`;
|
||||
// Draw the main cylinder body
|
||||
cylinderGroup
|
||||
.append('path')
|
||||
.attr('d', d)
|
||||
.attr('fill', '#eaeaea')
|
||||
.attr('stroke', '#000')
|
||||
.attr('stroke-width', 1)
|
||||
.attr('class', cssclass);
|
||||
|
||||
if (!isFooter) {
|
||||
cylinderGroup.attr('transform', `translate(${w * 1.5}, ${rect.height / 4 - 2 * ry})`);
|
||||
} else {
|
||||
cylinderGroup.attr('transform', `translate(${w * 1.5}, ${rect.height / 4 - 2 * ry})`);
|
||||
}
|
||||
actor.rectData = rect;
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
g,
|
||||
rect.x,
|
||||
rect.y + (!isFooter ? rect.height / 2 : h / 2 + ry),
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: `actor ${ACTOR_BOX_CLASS}` },
|
||||
conf
|
||||
);
|
||||
|
||||
const lastPath = cylinderGroup.select('path:last-child');
|
||||
if (lastPath.node()) {
|
||||
const bounds = lastPath.node().getBBox();
|
||||
actor.height = bounds.height + (conf.sequence.labelBoxHeight ?? 0);
|
||||
}
|
||||
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
const drawActorTypeBoundary = function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + 80;
|
||||
const radius = 30;
|
||||
const line = elem.append('g').lower();
|
||||
|
||||
if (!isFooter) {
|
||||
actorCnt++;
|
||||
line
|
||||
.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', centerY)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line 200')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
.attr('name', actor.name);
|
||||
|
||||
actor.actorCnt = actorCnt;
|
||||
}
|
||||
const actElem = elem.append('g');
|
||||
let cssClass = ACTOR_MAN_FIGURE_CLASS;
|
||||
if (isFooter) {
|
||||
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||
} else {
|
||||
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||
}
|
||||
actElem.attr('class', cssClass);
|
||||
actElem.attr('name', actor.name);
|
||||
|
||||
const rect = svgDrawCommon.getNoteRect();
|
||||
rect.x = actor.x;
|
||||
rect.y = actorY;
|
||||
rect.fill = '#eaeaea';
|
||||
rect.width = actor.width;
|
||||
rect.height = actor.height;
|
||||
rect.class = 'actor';
|
||||
rect.rx = 3;
|
||||
rect.ry = 3;
|
||||
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('id', 'actor-man-torso' + actorCnt)
|
||||
.attr('x1', actor.x + actor.width / 2 - radius * 2.5)
|
||||
.attr('y1', actorY + 10)
|
||||
.attr('x2', actor.x + actor.width / 2 - 15)
|
||||
.attr('y2', actorY + 10);
|
||||
|
||||
actElem
|
||||
.append('line')
|
||||
.attr('id', 'actor-man-arms' + actorCnt)
|
||||
.attr('x1', actor.x + actor.width / 2 - radius * 2.5)
|
||||
.attr('y1', actorY + 0) // starting Y
|
||||
.attr('x2', actor.x + actor.width / 2 - radius * 2.5)
|
||||
.attr('y2', actorY + 20); // ending Y (26px long, adjust as needed)
|
||||
|
||||
actElem
|
||||
.append('circle')
|
||||
.attr('cx', actor.x + actor.width / 2)
|
||||
.attr('cy', actorY + 10)
|
||||
.attr('r', radius)
|
||||
.attr('width', actor.width);
|
||||
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height + (conf.sequence.labelBoxHeight ?? 0);
|
||||
|
||||
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
actElem,
|
||||
rect.x,
|
||||
rect.y + (!isFooter ? radius / 2 : radius / 2),
|
||||
rect.width,
|
||||
rect.height,
|
||||
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
|
||||
conf
|
||||
);
|
||||
|
||||
if (!isFooter) {
|
||||
actElem.attr('transform', `translate(0,${radius / 2 + 7})`);
|
||||
} else {
|
||||
actElem.attr('transform', `translate(0,${radius / 2 + 7})`);
|
||||
}
|
||||
|
||||
// actElem.attr('transform', `translate(${rect.width / 2}, ${actorY + rect.height / 2})`);
|
||||
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
@@ -1365,22 +512,6 @@ export const drawActor = async function (elem, actor, conf, isFooter) {
|
||||
return await drawActorTypeActor(elem, actor, conf, isFooter);
|
||||
case 'participant':
|
||||
return await drawActorTypeParticipant(elem, actor, conf, isFooter);
|
||||
case 'boundary':
|
||||
return await drawActorTypeBoundary(elem, actor, conf, isFooter);
|
||||
case 'control':
|
||||
return await drawActorTypeControl(elem, actor, conf, isFooter);
|
||||
case 'entity':
|
||||
return await drawActorTypeEntity(elem, actor, conf, isFooter);
|
||||
case 'database':
|
||||
return await drawActorTypeDatabase(elem, actor, conf, isFooter);
|
||||
case 'collections':
|
||||
return await drawActorTypeCollections(elem, actor, conf, isFooter);
|
||||
case 'queue':
|
||||
return await drawActorTypeQueue(elem, actor, conf, isFooter);
|
||||
case 'icon':
|
||||
return await drawActorTypeIcon(elem, actor, conf, isFooter);
|
||||
case 'image':
|
||||
return await drawActorTypeImage(elem, actor, conf, isFooter);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -17,9 +17,6 @@ export interface Actor {
|
||||
actorCnt: number | null;
|
||||
rectData: unknown;
|
||||
type: string;
|
||||
doc?: ParticipantMetaData; // For documentation
|
||||
iconName?: string; // For icon type
|
||||
imgSrc?: string; // For img type
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
@@ -93,20 +90,3 @@ export interface Note {
|
||||
message: string;
|
||||
wrap: boolean;
|
||||
}
|
||||
|
||||
export interface ParticipantMetaData {
|
||||
type?:
|
||||
| 'actor'
|
||||
| 'participant'
|
||||
| 'boundary'
|
||||
| 'control'
|
||||
| 'entity'
|
||||
| 'database'
|
||||
| 'collections'
|
||||
| 'queue'
|
||||
| 'icon'
|
||||
| 'img';
|
||||
icon?: string;
|
||||
img?: string;
|
||||
form?: string;
|
||||
}
|
||||
|
@@ -46,102 +46,6 @@ sequenceDiagram
|
||||
Bob->>Alice: Hi Alice
|
||||
```
|
||||
|
||||
### Boundary
|
||||
|
||||
If you want to use the boundary symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "boundary" }
|
||||
participant Bob
|
||||
Alice->>Bob: Request from boundary
|
||||
Bob->>Alice: Response to boundary
|
||||
```
|
||||
|
||||
### Control
|
||||
|
||||
If you want to use the control symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "control" }
|
||||
participant Bob
|
||||
Alice->>Bob: Control request
|
||||
Bob->>Alice: Control response
|
||||
```
|
||||
|
||||
### Entity
|
||||
|
||||
If you want to use the entity symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "entity" }
|
||||
participant Bob
|
||||
Alice->>Bob: Entity request
|
||||
Bob->>Alice: Entity response
|
||||
```
|
||||
|
||||
### Database
|
||||
|
||||
If you want to use the database symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
participant Bob
|
||||
Alice->>Bob: DB query
|
||||
Bob->>Alice: DB result
|
||||
```
|
||||
|
||||
### Collections
|
||||
|
||||
If you want to use the collections symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "collections" }
|
||||
participant Bob
|
||||
Alice->>Bob: Collections request
|
||||
Bob->>Alice: Collections response
|
||||
```
|
||||
|
||||
### Queue
|
||||
|
||||
If you want to use the queue symbol for a participant, use the JSON configuration syntax as shown below.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "queue" }
|
||||
participant Bob
|
||||
Alice->>Bob: Queue message
|
||||
Bob->>Alice: Queue response
|
||||
```
|
||||
|
||||
### Icon
|
||||
|
||||
If you want to use a custom icon for a participant, use the JSON configuration syntax as shown below. The `icon` value can be a FontAwesome icon name, emoji, or other supported icon identifier.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "icon", "icon": "fa:bell" }
|
||||
participant Bob
|
||||
Alice->>Bob: Icon participant
|
||||
Bob->>Alice: Response to icon
|
||||
```
|
||||
|
||||
### Image
|
||||
|
||||
If you want to use a custom image for a participant, use the JSON configuration syntax as shown below. The `image` value should be a valid image URL.
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
|
||||
participant Bob
|
||||
Alice->>Bob: Image participant
|
||||
Bob->>Alice: Response to image
|
||||
```
|
||||
|
||||
### Aliases
|
||||
|
||||
The actor can have a convenient identifier and a descriptive label.
|
||||
|
@@ -285,7 +285,11 @@ test('markdownToHTML - Unsupported formatting', () => {
|
||||
- l1
|
||||
- l2
|
||||
- l3`)
|
||||
).toMatchInlineSnapshot('"<p>Hello</p>Unsupported markdown: list"');
|
||||
).toMatchInlineSnapshot(`
|
||||
"<p>Hello</p> - l1
|
||||
- l2
|
||||
- l3"
|
||||
`);
|
||||
});
|
||||
|
||||
test('markdownToHTML - no auto wrapping', () => {
|
||||
|
@@ -3,6 +3,7 @@ import { marked } from 'marked';
|
||||
import { dedent } from 'ts-dedent';
|
||||
import type { MarkdownLine, MarkdownWordType } from './types.js';
|
||||
import type { MermaidConfig } from '../config.type.js';
|
||||
import { log } from '../logger.js';
|
||||
|
||||
/**
|
||||
* @param markdown - markdown to process
|
||||
@@ -61,6 +62,8 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M
|
||||
});
|
||||
} else if (treeNode.type === 'html') {
|
||||
lines[currentLine].push({ content: treeNode.text, type: 'normal' });
|
||||
} else {
|
||||
lines[currentLine].push({ content: treeNode.raw, type: 'normal' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -89,7 +92,8 @@ export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidCo
|
||||
} else if (node.type === 'escape') {
|
||||
return node.text;
|
||||
}
|
||||
return `Unsupported markdown: ${node.type}`;
|
||||
log.warn(`Unsupported markdown: ${node.type}`);
|
||||
return node.raw;
|
||||
}
|
||||
|
||||
return nodes.map(output).join('');
|
||||
|
@@ -133,6 +133,21 @@ properties:
|
||||
- MODEL_ORDER
|
||||
- GREEDY_MODEL_ORDER
|
||||
default: GREEDY_MODEL_ORDER
|
||||
forceNodeModelOrder:
|
||||
description: |
|
||||
The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES.
|
||||
type: boolean
|
||||
default: false
|
||||
considerModelOrder:
|
||||
description: |
|
||||
Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting.
|
||||
type: string
|
||||
enum:
|
||||
- NONE
|
||||
- NODES_AND_EDGES
|
||||
- PREFER_EDGES
|
||||
- PREFER_NODES
|
||||
default: 'NODES_AND_EDGES'
|
||||
darkMode:
|
||||
type: boolean
|
||||
default: false
|
||||
|
Reference in New Issue
Block a user