mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-06 13:54:09 +01:00
Compare commits
1 Commits
mindmap-no
...
architectu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a23304d1a |
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': minor
|
|
||||||
---
|
|
||||||
|
|
||||||
feat: Add half-arrowheads (solid & stick) and central connection support
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'@mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix: Mindmap breaking in ELK layout
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names
|
|
||||||
@@ -369,92 +369,4 @@ ORDER ||--|{ LINE-ITEM : contains
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Special characters and numbers syntax', () => {
|
|
||||||
it('should render ER diagram with numeric entity names', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
erDiagram
|
|
||||||
1 ||--|| ORDER : places
|
|
||||||
ORDER ||--|{ 2 : contains
|
|
||||||
2 ||--o{ 3.5 : references
|
|
||||||
`,
|
|
||||||
{ logLevel: 1 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render ER diagram with "u" character in entity names and cardinality', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
erDiagram
|
|
||||||
CUSTOMER ||--|| u : has
|
|
||||||
u ||--|| ORDER : places
|
|
||||||
PROJECT u--o{ TEAM_MEMBER : "parent"
|
|
||||||
`,
|
|
||||||
{ logLevel: 1 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render ER diagram with decimal numbers in relationships', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
erDiagram
|
|
||||||
2.5 ||--|| 1.5 : has
|
|
||||||
CUSTOMER ||--o{ 3.14 : references
|
|
||||||
1.0 ||--|{ ORDER : contains
|
|
||||||
`,
|
|
||||||
{ logLevel: 1 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render ER diagram with numeric entity names and attributes', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
erDiagram
|
|
||||||
1 {
|
|
||||||
string name
|
|
||||||
int value
|
|
||||||
}
|
|
||||||
1 ||--|| ORDER : places
|
|
||||||
ORDER {
|
|
||||||
float price
|
|
||||||
string description
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{ logLevel: 1 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render complex ER diagram with mixed special entity names', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
erDiagram
|
|
||||||
CUSTOMER ||--o{ 1 : places
|
|
||||||
1 ||--|{ u : contains
|
|
||||||
1.5
|
|
||||||
u ||--|| 2.5 : processes
|
|
||||||
2.5 {
|
|
||||||
string id
|
|
||||||
float value
|
|
||||||
}
|
|
||||||
u {
|
|
||||||
varchar(50) name
|
|
||||||
int count
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{ logLevel: 1 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should render ER diagram with numeric entity names and attributes', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`erDiagram
|
|
||||||
PRODUCT ||--o{ ORDER-ITEM : has
|
|
||||||
1.5
|
|
||||||
u
|
|
||||||
1
|
|
||||||
`,
|
|
||||||
{ logLevel: 1 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -655,126 +655,5 @@ describe('Sequence Diagram Special Cases', () => {
|
|||||||
expect(svg).to.not.have.attr('style');
|
expect(svg).to.not.have.attr('style');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Central Connection Rendering Tests', () => {
|
|
||||||
it('should render central connection circles on actor vertical lines', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Central connection
|
|
||||||
Bob ()-->> Charlie: Reverse central connection
|
|
||||||
Charlie ()<<-->>() Alice: Dual central connection`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with different arrow types', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()->>() Bob: Solid open arrow
|
|
||||||
Alice ()-->>() Bob: Dotted open arrow
|
|
||||||
Alice ()-x() Bob: Solid cross
|
|
||||||
Alice ()--x() Bob: Dotted cross
|
|
||||||
Alice ()->() Bob: Solid arrow`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with bidirectional arrows', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()<<->>() Bob: Bidirectional solid
|
|
||||||
Alice ()<<-->>() Bob: Bidirectional dotted`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with activations', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Activate Bob
|
|
||||||
activate Bob
|
|
||||||
Bob ()-->> Charlie: Message to Charlie
|
|
||||||
Bob ()->>() Alice: Response to Alice
|
|
||||||
deactivate Bob`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections mixed with normal messages', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ->> Bob: Normal message
|
|
||||||
Bob ()->>() Charlie: Central connection
|
|
||||||
Charlie -->> Alice: Normal dotted message
|
|
||||||
Alice ()<<-->>() Bob: Dual central connection
|
|
||||||
Bob -x Charlie: Normal cross message`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with notes', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Central connection
|
|
||||||
Note over Alice,Bob: Central connection note
|
|
||||||
Bob ()-->> Charlie: Reverse central connection
|
|
||||||
Note right of Charlie: Response note
|
|
||||||
Charlie ()<<-->>() Alice: Dual central connection`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with loops and alternatives', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
loop Every minute
|
|
||||||
Alice ()->>() Bob: Central heartbeat
|
|
||||||
Bob ()-->> Charlie: Forward heartbeat
|
|
||||||
end
|
|
||||||
alt Success
|
|
||||||
Charlie ()<<-->>() Alice: Success response
|
|
||||||
else Failure
|
|
||||||
Charlie ()-x() Alice: Failure response
|
|
||||||
end`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with different participant types', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
actor Bob
|
|
||||||
participant Charlie@{"type":"boundary"}
|
|
||||||
participant David@{"type":"control"}
|
|
||||||
participant Eve@{"type":"entity"}
|
|
||||||
Alice ()->>() Bob: To actor
|
|
||||||
Bob ()-->> Charlie: To boundary
|
|
||||||
Charlie ()->>() David: To control
|
|
||||||
David ()<<-->>() Eve: To entity
|
|
||||||
Eve ()-x() Alice: Back to participant`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1053,167 +1053,4 @@ describe('Sequence diagram', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('render new arrow type', () => {
|
|
||||||
it('should render Solid half arrow top', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice -|\\ John: Hello John, how are you?
|
|
||||||
Alice-|\\ John: Hi Alice, I can hear you!
|
|
||||||
Alice -|\\ John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should render Solid half arrow bottom', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice-|/John: Hello John, how are you?
|
|
||||||
Alice-|/John: Hi Alice, I can hear you!
|
|
||||||
Alice-|/John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice-\\\\John: Hello John, how are you?
|
|
||||||
Alice-\\\\John: Hi Alice, I can hear you!
|
|
||||||
Alice-\\\\John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should render Stick half arrow bottom ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice-//John: Hello John, how are you?
|
|
||||||
Alice-//John: Hi Alice, I can hear you!
|
|
||||||
Alice-//John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should render Solid half arrow top reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice/|-John: Hello Alice, how are you?
|
|
||||||
Alice/|-John: Hi Alice, I can hear you!
|
|
||||||
Alice/|-John: Test
|
|
||||||
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow bottom reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
Alice \\|- John: Hello Alice, how are you?
|
|
||||||
Alice \\|- John: Hi Alice, I can hear you!
|
|
||||||
Alice \\|- John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice //-John: Hello Alice, how are you?
|
|
||||||
Alice //-John: Hi Alice, I can hear you!
|
|
||||||
Alice //-John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow bottom reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice \\\\-John: Hello Alice, how are you?
|
|
||||||
Alice \\\\-John: Hi Alice, I can hear you!
|
|
||||||
Alice \\\\-John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow top dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice --|\\John: Hello John, how are you?
|
|
||||||
Alice --|\\John: Hi Alice, I can hear you!
|
|
||||||
Alice --|\\John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow bottom dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice --|/John: Hello John, how are you?
|
|
||||||
Alice --|/John: Hi Alice, I can hear you!
|
|
||||||
Alice --|/John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice--\\\\John: Hello John, how are you?
|
|
||||||
Alice--\\\\John: Hi Alice, I can hear you!
|
|
||||||
Alice--\\\\John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow bottom dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice--//John: Hello John, how are you?
|
|
||||||
Alice--//John: Hi Alice, I can hear you!
|
|
||||||
Alice--//John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow top reverse dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice/|--John: Hello Alice, how are you?
|
|
||||||
Alice/|--John: Hi Alice, I can hear you!
|
|
||||||
Alice/|--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow bottom reverse dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice\\|--John: Hello Alice, how are you?
|
|
||||||
Alice\\|--John: Hi Alice, I can hear you!
|
|
||||||
Alice\\|--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top reverse dotted ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice//--John: Hello Alice, how are you?
|
|
||||||
Alice//--John: Hi Alice, I can hear you!
|
|
||||||
Alice//--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow bottom reverse dotted ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice\\\\--John: Hello Alice, how are you?
|
|
||||||
Alice\\\\--John: Hi Alice, I can hear you!
|
|
||||||
Alice\\\\--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,14 +6,6 @@
|
|||||||
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<style>
|
<style>
|
||||||
svg:not(svg svg) {
|
svg:not(svg svg) {
|
||||||
border: 2px solid darkred;
|
border: 2px solid darkred;
|
||||||
|
|||||||
@@ -110,48 +110,6 @@
|
|||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
---
|
---
|
||||||
mindmap
|
|
||||||
root((mindmap))
|
|
||||||
Origins
|
|
||||||
Long history
|
|
||||||
::icon(fa fa-book)
|
|
||||||
Popularisation
|
|
||||||
British popular psychology author Tony Buzan
|
|
||||||
Research
|
|
||||||
On effectiveness<br/>and features
|
|
||||||
On Automatic creation
|
|
||||||
Uses
|
|
||||||
Creative techniques
|
|
||||||
Strategic planning
|
|
||||||
Argument mapping
|
|
||||||
Tools
|
|
||||||
id)I am a cloud(
|
|
||||||
id))I am a bang((
|
|
||||||
Tools
|
|
||||||
</pre>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
---
|
|
||||||
flowchart
|
|
||||||
aid0
|
|
||||||
</pre
|
|
||||||
>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
---
|
|
||||||
mindmap
|
|
||||||
aid0
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: ogdc
|
|
||||||
---
|
|
||||||
flowchart-elk TB
|
flowchart-elk TB
|
||||||
c1-->a2
|
c1-->a2
|
||||||
subgraph one
|
subgraph one
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: LayoutData
|
# Interface: LayoutData
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L169)
|
Defined in: [packages/mermaid/src/rendering-util/types.ts:168](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L168)
|
||||||
|
|
||||||
## Indexable
|
## Indexable
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:169](https://github.co
|
|||||||
|
|
||||||
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L172)
|
Defined in: [packages/mermaid/src/rendering-util/types.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L171)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:172](https://github.co
|
|||||||
|
|
||||||
> **edges**: `Edge`\[]
|
> **edges**: `Edge`\[]
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L171)
|
Defined in: [packages/mermaid/src/rendering-util/types.ts:170](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L170)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -38,4 +38,4 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:171](https://github.co
|
|||||||
|
|
||||||
> **nodes**: `Node`\[]
|
> **nodes**: `Node`\[]
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:170](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L170)
|
Defined in: [packages/mermaid/src/rendering-util/types.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L169)
|
||||||
|
|||||||
@@ -329,11 +329,7 @@ Messages can be of two displayed either solid or with a dotted line.
|
|||||||
[Actor][Arrow][Actor]:Message text
|
[Actor][Arrow][Actor]:Message text
|
||||||
```
|
```
|
||||||
|
|
||||||
Lines can be solid or dotted, and can end with various types of arrowheads, crosses, or open arrows.
|
There are ten types of arrows currently supported:
|
||||||
|
|
||||||
#### Supported Arrow Types
|
|
||||||
|
|
||||||
**Standard Arrow Types**
|
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| -------- | ---------------------------------------------------- |
|
| -------- | ---------------------------------------------------- |
|
||||||
@@ -348,58 +344,6 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros
|
|||||||
| `-)` | Solid line with an open arrow at the end (async) |
|
| `-)` | Solid line with an open arrow at the end (async) |
|
||||||
| `--)` | Dotted line with a open arrow at the end (async) |
|
| `--)` | Dotted line with a open arrow at the end (async) |
|
||||||
|
|
||||||
**Half-Arrows (v\<MERMAID_RELEASE_VERSION>+)**
|
|
||||||
|
|
||||||
The following half-arrow types are supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
| Type | Description |
|
|
||||||
| ------- | ---------------------------------------------------- |
|
|
||||||
| `-\|\` | Solid line with top half arrowhead |
|
|
||||||
| `--\|\` | Dotted line with top half arrowhead |
|
|
||||||
| `-\|/` | Solid line with bottom half arrowhead |
|
|
||||||
| `--\|/` | Dotted line with bottom half arrowhead |
|
|
||||||
| `/\|-` | Solid line with reverse top half arrowhead |
|
|
||||||
| `/\|--` | Dotted line with reverse top half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom half arrowhead |
|
|
||||||
| `-\\` | Solid line with top stick half arrowhead |
|
|
||||||
| `--\\` | Dotted line with top stick half arrowhead |
|
|
||||||
| `-//` | Solid line with bottom stick half arrowhead |
|
|
||||||
| `--//` | Dotted line with bottom stick half arrowhead |
|
|
||||||
| `//-` | Solid line with reverse top stick half arrowhead |
|
|
||||||
| `//--` | Dotted line with reverse top stick half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom stick half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom stick half arrowhead |
|
|
||||||
|
|
||||||
## Central Connections (v\<MERMAID_RELEASE_VERSION>+)
|
|
||||||
|
|
||||||
Mermaid sequence diagrams support **central lifeline connections** using a `()`.
|
|
||||||
This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another.
|
|
||||||
|
|
||||||
To indicate a central connection, append `()` to the arrow syntax.
|
|
||||||
|
|
||||||
#### Basic Syntax
|
|
||||||
|
|
||||||
```mermaid-example
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant John
|
|
||||||
Alice->>()John: Hello John
|
|
||||||
Alice()->>John: How are you?
|
|
||||||
John()->>()Alice: Great!
|
|
||||||
```
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant John
|
|
||||||
Alice->>()John: Hello John
|
|
||||||
Alice()->>John: How are you?
|
|
||||||
John()->>()Alice: Great!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Activations
|
## Activations
|
||||||
|
|
||||||
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
||||||
|
|||||||
@@ -67,22 +67,7 @@ export const render = async (
|
|||||||
|
|
||||||
// Add the element to the DOM
|
// Add the element to the DOM
|
||||||
if (!node.isGroup) {
|
if (!node.isGroup) {
|
||||||
// const child = node as NodeWithVertex;
|
const child = node as NodeWithVertex;
|
||||||
const child: NodeWithVertex = {
|
|
||||||
id: node.id,
|
|
||||||
width: node.width,
|
|
||||||
height: node.height,
|
|
||||||
// Store the original node data for later use
|
|
||||||
label: node.label,
|
|
||||||
isGroup: node.isGroup,
|
|
||||||
shape: node.shape,
|
|
||||||
padding: node.padding,
|
|
||||||
cssClasses: node.cssClasses,
|
|
||||||
cssStyles: node.cssStyles,
|
|
||||||
look: node.look,
|
|
||||||
// Include parentId for subgraph processing
|
|
||||||
parentId: node.parentId,
|
|
||||||
};
|
|
||||||
graph.children.push(child);
|
graph.children.push(child);
|
||||||
nodeDb[node.id] = node;
|
nodeDb[node.id] = node;
|
||||||
|
|
||||||
@@ -165,7 +150,7 @@ export const render = async (
|
|||||||
domId: { node: () => any; attr: (arg0: string, arg1: string) => void };
|
domId: { node: () => any; attr: (arg0: string, arg1: string) => void };
|
||||||
}) {
|
}) {
|
||||||
if (node) {
|
if (node) {
|
||||||
nodeDb[node.id] ??= {};
|
nodeDb[node.id] = node;
|
||||||
nodeDb[node.id].offset = {
|
nodeDb[node.id].offset = {
|
||||||
posX: node.x + relX,
|
posX: node.x + relX,
|
||||||
posY: node.y + relY,
|
posY: node.y + relY,
|
||||||
@@ -875,13 +860,11 @@ export const render = async (
|
|||||||
log.info('APA01 layout result:', JSON.stringify(g, null, 2));
|
log.info('APA01 layout result:', JSON.stringify(g, null, 2));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('APA01 ELK layout error:', error);
|
log.error('APA01 ELK layout error:', error);
|
||||||
log.error('APA01 elkGraph that caused error:', JSON.stringify(elkGraph, null, 2));
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// debugger;
|
// debugger;
|
||||||
await drawNodes(0, 0, g.children, svg, subGraphsEl, 0);
|
await drawNodes(0, 0, g.children, svg, subGraphsEl, 0);
|
||||||
|
|
||||||
g.edges?.map(
|
g.edges?.map(
|
||||||
(edge: {
|
(edge: {
|
||||||
sources: (string | number)[];
|
sources: (string | number)[];
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import type { ArchitectureDiagramConfig } from '../../config.type.js';
|
|||||||
import DEFAULT_CONFIG from '../../defaultConfig.js';
|
import DEFAULT_CONFIG from '../../defaultConfig.js';
|
||||||
import type { DiagramDB } from '../../diagram-api/types.js';
|
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||||
import type { D3Element } from '../../types.js';
|
import type { D3Element } from '../../types.js';
|
||||||
import { cleanAndMerge } from '../../utils.js';
|
import { cleanAndMerge, getEdgeId } from '../../utils.js';
|
||||||
|
import type { LayoutData, Node, Edge } from '../../rendering-util/types.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
clear as commonClear,
|
clear as commonClear,
|
||||||
getAccDescription,
|
getAccDescription,
|
||||||
@@ -351,15 +353,147 @@ export class ArchitectureDB implements DiagramDB {
|
|||||||
public getDiagramTitle = getDiagramTitle;
|
public getDiagramTitle = getDiagramTitle;
|
||||||
public getAccDescription = getAccDescription;
|
public getAccDescription = getAccDescription;
|
||||||
public setAccDescription = setAccDescription;
|
public setAccDescription = setAccDescription;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typed wrapper for resolving an architecture diagram's config fields. Returns the default value if undefined
|
* Converts architecture diagram data to LayoutData format for unified rendering
|
||||||
* @param field - the config field to access
|
*/
|
||||||
* @returns
|
public getData(): LayoutData {
|
||||||
*/
|
const config = commonGetConfig();
|
||||||
// export function getConfigField<T extends keyof ArchitectureDiagramConfig>(
|
const nodes: Node[] = [];
|
||||||
// field: T
|
const edges: Edge[] = [];
|
||||||
// ): Required<ArchitectureDiagramConfig>[T] {
|
|
||||||
// return db.getConfig()[field];
|
const groups = this.getGroups();
|
||||||
// }
|
for (const group of groups) {
|
||||||
|
const padding = this.getConfigField('padding');
|
||||||
|
const fontSize = this.getConfigField('fontSize');
|
||||||
|
|
||||||
|
const groupWidth = 200;
|
||||||
|
let groupHeight = 150;
|
||||||
|
|
||||||
|
if (group.title || group.icon) {
|
||||||
|
groupHeight += fontSize + padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.push({
|
||||||
|
id: group.id,
|
||||||
|
label: group.title,
|
||||||
|
parentId: group.in,
|
||||||
|
isGroup: true,
|
||||||
|
shape: 'rect',
|
||||||
|
icon: group.icon,
|
||||||
|
width: groupWidth,
|
||||||
|
height: groupHeight,
|
||||||
|
padding: padding,
|
||||||
|
cssClasses: 'architecture-group',
|
||||||
|
cssCompiledStyles: [
|
||||||
|
'stroke: #cccccc',
|
||||||
|
'stroke-width: 2px',
|
||||||
|
'stroke-dasharray: 8,8',
|
||||||
|
'fill: transparent',
|
||||||
|
],
|
||||||
|
labelStyle: '',
|
||||||
|
look: config.look || 'classic',
|
||||||
|
rx: 5,
|
||||||
|
ry: 5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const services = this.getServices();
|
||||||
|
for (const service of services) {
|
||||||
|
const iconSize = this.getConfigField('iconSize');
|
||||||
|
let nodeWidth = iconSize;
|
||||||
|
let nodeHeight = iconSize;
|
||||||
|
|
||||||
|
if (service.title) {
|
||||||
|
nodeHeight += iconSize * 0.3;
|
||||||
|
nodeWidth = Math.max(nodeWidth, iconSize * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.push({
|
||||||
|
id: service.id,
|
||||||
|
label: service.title,
|
||||||
|
parentId: service.in,
|
||||||
|
isGroup: false,
|
||||||
|
shape: service.icon || (service as any).iconText ? 'icon' : 'squareRect',
|
||||||
|
icon: service.icon ? `mermaid-architecture:${service.icon}` : 'mermaid-architecture:blank',
|
||||||
|
width: service.width || nodeWidth,
|
||||||
|
height: service.height || nodeHeight,
|
||||||
|
cssClasses: 'architecture-service',
|
||||||
|
look: config.look,
|
||||||
|
padding: this.getConfigField('padding') / 4,
|
||||||
|
description: (service as any).iconText ? [(service as any).iconText] : undefined,
|
||||||
|
assetWidth: iconSize,
|
||||||
|
assetHeight: iconSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const junctions = this.getJunctions();
|
||||||
|
for (const junction of junctions) {
|
||||||
|
nodes.push({
|
||||||
|
id: junction.id,
|
||||||
|
parentId: junction.in,
|
||||||
|
isGroup: false,
|
||||||
|
shape: 'squareRect',
|
||||||
|
width: 2,
|
||||||
|
height: 2,
|
||||||
|
cssClasses: 'architecture-junction',
|
||||||
|
look: config.look,
|
||||||
|
type: 'junction' as any,
|
||||||
|
padding: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const architectureEdges = this.getEdges();
|
||||||
|
let edgeCounter = 0;
|
||||||
|
for (const edge of architectureEdges) {
|
||||||
|
const edgeData = {
|
||||||
|
id: getEdgeId(edge.lhsId, edge.rhsId, { counter: edgeCounter, prefix: 'L' }),
|
||||||
|
start: edge.lhsId,
|
||||||
|
end: edge.rhsId,
|
||||||
|
source: edge.lhsId,
|
||||||
|
target: edge.rhsId,
|
||||||
|
label: edge.title || '',
|
||||||
|
labelpos: 'c',
|
||||||
|
type: 'normal',
|
||||||
|
minlen: 2,
|
||||||
|
weight: 1,
|
||||||
|
classes: 'edge-thickness-normal edge-pattern-solid architecture-edge',
|
||||||
|
look: config.look || 'classic',
|
||||||
|
curve: 'linear',
|
||||||
|
arrowTypeStart: edge.lhsInto ? 'point' : 'none',
|
||||||
|
arrowTypeEnd: edge.rhsInto ? 'point' : 'none',
|
||||||
|
arrowheadStyle: 'fill: #333',
|
||||||
|
thickness: 'normal',
|
||||||
|
pattern: 'solid',
|
||||||
|
style: ['stroke: #333333', 'stroke-width: 3px', 'fill: none'],
|
||||||
|
cssCompiledStyles: [],
|
||||||
|
labelStyle: [],
|
||||||
|
lhsDir: edge.lhsDir,
|
||||||
|
rhsDir: edge.rhsDir,
|
||||||
|
lhsInto: edge.lhsInto,
|
||||||
|
rhsInto: edge.rhsInto,
|
||||||
|
lhsGroup: edge.lhsGroup,
|
||||||
|
rhsGroup: edge.rhsGroup,
|
||||||
|
} as Edge & {
|
||||||
|
lhsDir: any;
|
||||||
|
rhsDir: any;
|
||||||
|
lhsInto?: boolean;
|
||||||
|
rhsInto?: boolean;
|
||||||
|
lhsGroup?: boolean;
|
||||||
|
rhsGroup?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
edges.push(edgeData);
|
||||||
|
edgeCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
config,
|
||||||
|
dataStructures: this.getDataStructures(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { DiagramDefinition } from '../../diagram-api/types.js';
|
|||||||
import { parser } from './architectureParser.js';
|
import { parser } from './architectureParser.js';
|
||||||
import { ArchitectureDB } from './architectureDb.js';
|
import { ArchitectureDB } from './architectureDb.js';
|
||||||
import styles from './architectureStyles.js';
|
import styles from './architectureStyles.js';
|
||||||
import { renderer } from './architectureRenderer.js';
|
import { renderer } from './architectureRenderer-unified.js';
|
||||||
|
|
||||||
export const diagram: DiagramDefinition = {
|
export const diagram: DiagramDefinition = {
|
||||||
parser,
|
parser,
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
|
import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
|
||||||
|
import { log } from '../../logger.js';
|
||||||
|
import { getDiagramElement } from '../../rendering-util/insertElementsForSize.js';
|
||||||
|
import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/render.js';
|
||||||
|
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
||||||
|
import type { LayoutData } from '../../rendering-util/types.js';
|
||||||
|
import utils from '../../utils.js';
|
||||||
|
|
||||||
|
import { registerIconPacks } from '../../rendering-util/icons.js';
|
||||||
|
import { architectureIcons } from './architectureIcons.js';
|
||||||
|
|
||||||
|
export const getClasses = function (
|
||||||
|
_text: string,
|
||||||
|
_diagramObj: any
|
||||||
|
): Map<string, DiagramStyleClassDef> {
|
||||||
|
return new Map();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const draw = async function (_text: string, id: string, _version: string, diag: any) {
|
||||||
|
registerIconPacks([
|
||||||
|
{
|
||||||
|
name: architectureIcons.prefix,
|
||||||
|
icons: architectureIcons,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const { securityLevel, architecture: conf, layout } = getConfig();
|
||||||
|
|
||||||
|
const data4Layout = diag.db.getData() as LayoutData;
|
||||||
|
|
||||||
|
const svg = getDiagramElement(id, securityLevel);
|
||||||
|
|
||||||
|
data4Layout.type = diag.type;
|
||||||
|
data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout, { fallback: 'dagre' });
|
||||||
|
|
||||||
|
data4Layout.nodeSpacing = 100;
|
||||||
|
data4Layout.rankSpacing = 100;
|
||||||
|
data4Layout.markers = ['point'];
|
||||||
|
data4Layout.diagramId = id;
|
||||||
|
|
||||||
|
log.debug('Architecture layout data:', data4Layout);
|
||||||
|
await render(data4Layout, svg);
|
||||||
|
|
||||||
|
const padding = conf?.padding ?? 8;
|
||||||
|
utils.insertTitle(svg, 'architectureTitleText', 0, diag.db.getDiagramTitle());
|
||||||
|
|
||||||
|
setupViewPortForSVG(svg, padding, 'architecture', conf?.useMaxWidth ?? true);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderer = { draw };
|
||||||
@@ -2,6 +2,7 @@ import type { DiagramDBBase } from '../../diagram-api/types.js';
|
|||||||
import type { ArchitectureDiagramConfig } from '../../config.type.js';
|
import type { ArchitectureDiagramConfig } from '../../config.type.js';
|
||||||
import type { D3Element } from '../../types.js';
|
import type { D3Element } from '../../types.js';
|
||||||
import type cytoscape from 'cytoscape';
|
import type cytoscape from 'cytoscape';
|
||||||
|
import type { LayoutData } from '../../rendering-util/types.js';
|
||||||
|
|
||||||
/*=======================================*\
|
/*=======================================*\
|
||||||
| Architecture Diagram Types |
|
| Architecture Diagram Types |
|
||||||
@@ -256,7 +257,8 @@ export interface ArchitectureDB extends DiagramDBBase<ArchitectureDiagramConfig>
|
|||||||
getEdges: () => ArchitectureEdge[];
|
getEdges: () => ArchitectureEdge[];
|
||||||
setElementForId: (id: string, element: D3Element) => void;
|
setElementForId: (id: string, element: D3Element) => void;
|
||||||
getElementById: (id: string) => D3Element;
|
getElementById: (id: string) => D3Element;
|
||||||
getDataStructures: () => ArchitectureDataStructures;
|
getData: () => LayoutData;
|
||||||
|
getDirection: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ArchitectureAdjacencyList = Record<string, ArchitectureDirectionPairMap>;
|
export type ArchitectureAdjacencyList = Record<string, ArchitectureDirectionPairMap>;
|
||||||
|
|||||||
@@ -66,15 +66,12 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
\}\| return 'ONE_OR_MORE';
|
\}\| return 'ONE_OR_MORE';
|
||||||
"one" return 'ONLY_ONE';
|
"one" return 'ONLY_ONE';
|
||||||
"only one" return 'ONLY_ONE';
|
"only one" return 'ONLY_ONE';
|
||||||
[0-9]+\.[0-9]+ return 'DECIMAL_NUM';
|
"1" return 'ONLY_ONE';
|
||||||
"1"(?=\s+[A-Za-z_"']) return 'ONLY_ONE';
|
|
||||||
"1" return 'ENTITY_ONE';
|
|
||||||
[0-9]+ return 'NUM';
|
|
||||||
\|\| return 'ONLY_ONE';
|
\|\| return 'ONLY_ONE';
|
||||||
o\| return 'ZERO_OR_ONE';
|
o\| return 'ZERO_OR_ONE';
|
||||||
o\{ return 'ZERO_OR_MORE';
|
o\{ return 'ZERO_OR_MORE';
|
||||||
\|\{ return 'ONE_OR_MORE';
|
\|\{ return 'ONE_OR_MORE';
|
||||||
u(?=[\.\-\|]) return 'MD_PARENT';
|
\s*u return 'MD_PARENT';
|
||||||
\.\. return 'NON_IDENTIFYING';
|
\.\. return 'NON_IDENTIFYING';
|
||||||
\-\- return 'IDENTIFYING';
|
\-\- return 'IDENTIFYING';
|
||||||
"to" return 'IDENTIFYING';
|
"to" return 'IDENTIFYING';
|
||||||
@@ -83,15 +80,13 @@ u(?=[\.\-\|]) return 'MD_PARENT';
|
|||||||
\-\. return 'NON_IDENTIFYING';
|
\-\. return 'NON_IDENTIFYING';
|
||||||
<style>([^\x00-\x7F]|\w|\-|\*)+ return 'STYLE_TEXT';
|
<style>([^\x00-\x7F]|\w|\-|\*)+ return 'STYLE_TEXT';
|
||||||
<style>';' return 'SEMI';
|
<style>';' return 'SEMI';
|
||||||
([^\x00-\x7F]|\w|\-|\*|\.)+ return 'UNICODE_TEXT';
|
([^\x00-\x7F]|\w|\-|\*)+ return 'UNICODE_TEXT';
|
||||||
|
[0-9] return 'NUM';
|
||||||
. return yytext[0];
|
. return yytext[0];
|
||||||
<<EOF>> return 'EOF';
|
<<EOF>> return 'EOF';
|
||||||
|
|
||||||
/lex
|
/lex
|
||||||
|
|
||||||
%left 'ONLY_ONE'
|
|
||||||
%left 'ZERO_OR_ONE' 'ZERO_OR_MORE' 'ONE_OR_MORE' 'MD_PARENT'
|
|
||||||
|
|
||||||
%start start
|
%start start
|
||||||
%% /* language grammar */
|
%% /* language grammar */
|
||||||
|
|
||||||
@@ -233,9 +228,6 @@ styleComponent: STYLE_TEXT | NUM | COLON | BRKT;
|
|||||||
entityName
|
entityName
|
||||||
: 'ENTITY_NAME' { $$ = $1.replace(/"/g, ''); }
|
: 'ENTITY_NAME' { $$ = $1.replace(/"/g, ''); }
|
||||||
| 'UNICODE_TEXT' { $$ = $1; }
|
| 'UNICODE_TEXT' { $$ = $1; }
|
||||||
| 'NUM' { $$ = $1; }
|
|
||||||
| 'DECIMAL_NUM' { $$ = $1; }
|
|
||||||
| 'ENTITY_ONE' { $$ = $1; }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
attributes
|
attributes
|
||||||
|
|||||||
@@ -1001,90 +1001,4 @@ describe('when parsing ER diagram it...', function () {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('syntax fixes for special characters and numbers', function () {
|
|
||||||
describe('standalone entity names', function () {
|
|
||||||
it('should allow number "1" as standalone entity', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n1`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow character "u" as standalone entity', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\nu`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow decimal numbers as standalone entities', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n2.5`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n1.5`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n0.1`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n99.99`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('entity names with attributes', function () {
|
|
||||||
it('should allow "u" as entity name with attributes', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\nu {\nstring name\nint id\n}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow number "1" as entity name with attributes', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\n1 {\nstring name\nint id\n}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow decimal numbers as entity names with attributes', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\n2.5 {\nstring name\nint id\n}`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\n1.5 {\nstring value\n}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('entity names in relationships', function () {
|
|
||||||
it('should allow "u" in relationships', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| u : has`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\nu ||--|| ORDER : places`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\nu ||--|| v : connects`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow numbers in relationships', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| 1 : has`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\n1 ||--|| ORDER : places`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\n1 ||--|| 2 : connects`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow decimal numbers in relationships', function () {
|
|
||||||
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| 2.5 : has`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\n1.5 ||--|| ORDER : places`);
|
|
||||||
erDiagram.parser.parse(`erDiagram\n2.5 ||--|| 5.5 : connects`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mixed scenarios', function () {
|
|
||||||
it('should handle complex diagram with special entity names', function () {
|
|
||||||
erDiagram.parser.parse(
|
|
||||||
`erDiagram
|
|
||||||
CUSTOMER ||--o{ 1 : places
|
|
||||||
1 ||--|{ u : contains
|
|
||||||
u {
|
|
||||||
string name
|
|
||||||
int quantity
|
|
||||||
}
|
|
||||||
"2.5" ||--|| ORDER : processes
|
|
||||||
ORDER {
|
|
||||||
int id
|
|
||||||
date created
|
|
||||||
}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle attributes with numbers in names (but not starting)', function () {
|
|
||||||
erDiagram.parser.parse(
|
|
||||||
`erDiagram
|
|
||||||
ENTITY {
|
|
||||||
string name1
|
|
||||||
int value2
|
|
||||||
float point3_5
|
|
||||||
}
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,223 +0,0 @@
|
|||||||
import { log } from '../../logger.js';
|
|
||||||
import type { D3Selection } from '../../types.js';
|
|
||||||
import type { Node } from '../../rendering-util/types.js';
|
|
||||||
import { getIconSVG, isIconAvailable } from '../../rendering-util/icons.js';
|
|
||||||
|
|
||||||
export interface MindmapIconConfig {
|
|
||||||
iconSize: number;
|
|
||||||
iconPadding: number;
|
|
||||||
shapeType: 'circle' | 'rect' | 'rounded' | 'bang' | 'cloud' | 'hexagon' | 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MindmapDimensions {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
labelOffset: { x: number; y: number };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get icon configuration for different mindmap shape types
|
|
||||||
*/
|
|
||||||
export function getMindmapIconConfig(shapeType: string): MindmapIconConfig {
|
|
||||||
const baseConfig = {
|
|
||||||
iconSize: 30,
|
|
||||||
iconPadding: 15,
|
|
||||||
shapeType: shapeType as MindmapIconConfig['shapeType'],
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (shapeType) {
|
|
||||||
case 'bang':
|
|
||||||
return { ...baseConfig, iconPadding: 1 };
|
|
||||||
case 'rect':
|
|
||||||
case 'default':
|
|
||||||
return { ...baseConfig, iconPadding: 10 };
|
|
||||||
default:
|
|
||||||
return baseConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate dimensions and label positioning for mindmap nodes with icons
|
|
||||||
*/
|
|
||||||
export function calculateMindmapDimensions(
|
|
||||||
node: Node,
|
|
||||||
bbox: any,
|
|
||||||
baseWidth: number,
|
|
||||||
baseHeight: number,
|
|
||||||
basePadding: number,
|
|
||||||
config: MindmapIconConfig
|
|
||||||
): MindmapDimensions {
|
|
||||||
const hasIcon = Boolean(node.icon);
|
|
||||||
|
|
||||||
if (!hasIcon) {
|
|
||||||
return {
|
|
||||||
width: baseWidth,
|
|
||||||
height: baseHeight,
|
|
||||||
labelOffset: { x: -bbox.width / 2, y: -bbox.height / 2 },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const { iconSize, iconPadding, shapeType } = config;
|
|
||||||
let width = baseWidth;
|
|
||||||
let height = baseHeight;
|
|
||||||
let labelXOffset = -bbox.width / 2;
|
|
||||||
const labelYOffset = -bbox.height / 2;
|
|
||||||
|
|
||||||
switch (shapeType) {
|
|
||||||
case 'circle': {
|
|
||||||
const totalWidthNeeded = bbox.width + iconSize + iconPadding * 2;
|
|
||||||
const minRadiusWithIcon = totalWidthNeeded / 2 + basePadding;
|
|
||||||
const radius = Math.max(baseWidth / 2, minRadiusWithIcon);
|
|
||||||
width = radius * 2;
|
|
||||||
height = radius * 2;
|
|
||||||
labelXOffset = -radius + iconSize + iconPadding;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'rect':
|
|
||||||
case 'rounded':
|
|
||||||
case 'default': {
|
|
||||||
const minWidthWithIcon = bbox.width + iconSize + iconPadding * 2 + basePadding * 2;
|
|
||||||
width = Math.max(baseWidth, minWidthWithIcon);
|
|
||||||
height = Math.max(baseHeight, iconSize + basePadding * 2);
|
|
||||||
|
|
||||||
const availableTextSpace = width - iconSize - iconPadding * 2;
|
|
||||||
labelXOffset = -width / 2 + iconSize + iconPadding + availableTextSpace / 2 - bbox.width / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'bang':
|
|
||||||
case 'cloud': {
|
|
||||||
const minWidthWithIcon = bbox.width + iconSize + iconPadding * 2 + basePadding;
|
|
||||||
width = Math.max(baseWidth, minWidthWithIcon);
|
|
||||||
height = Math.max(baseHeight, iconSize + basePadding);
|
|
||||||
|
|
||||||
const availableTextSpace = width - iconSize - iconPadding * 2;
|
|
||||||
labelXOffset = -width / 2 + iconSize + iconPadding + availableTextSpace / 2 - bbox.width / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
const minWidthWithIcon = bbox.width + iconSize + iconPadding * 2 + basePadding * 2;
|
|
||||||
width = Math.max(baseWidth, minWidthWithIcon);
|
|
||||||
height = Math.max(baseHeight, iconSize + basePadding * 2);
|
|
||||||
|
|
||||||
const availableTextSpace = width - iconSize - iconPadding * 2;
|
|
||||||
labelXOffset = -width / 2 + iconSize + iconPadding + availableTextSpace / 2 - bbox.width / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
labelOffset: { x: labelXOffset, y: labelYOffset },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert mindmap icon into the shape SVG element
|
|
||||||
*/
|
|
||||||
export async function insertMindmapIcon(
|
|
||||||
parentElement: D3Selection<SVGGraphicsElement>,
|
|
||||||
node: Node,
|
|
||||||
config: MindmapIconConfig
|
|
||||||
): Promise<void> {
|
|
||||||
if (!node.icon) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { iconSize, iconPadding, shapeType } = config;
|
|
||||||
const section = node.section === undefined ? -1 : node.section;
|
|
||||||
|
|
||||||
let iconName = node.icon;
|
|
||||||
const isCssFormat = iconName.includes(' ');
|
|
||||||
|
|
||||||
if (isCssFormat) {
|
|
||||||
iconName = iconName.replace(' ', ':');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (await isIconAvailable(iconName)) {
|
|
||||||
const iconSvg = await getIconSVG(
|
|
||||||
iconName,
|
|
||||||
{
|
|
||||||
height: iconSize,
|
|
||||||
width: iconSize,
|
|
||||||
},
|
|
||||||
{ class: 'label-icon' }
|
|
||||||
);
|
|
||||||
|
|
||||||
const iconElem = parentElement.append('g');
|
|
||||||
iconElem.html(`<g>${iconSvg}</g>`);
|
|
||||||
|
|
||||||
let iconX = 0;
|
|
||||||
let iconY = 0;
|
|
||||||
|
|
||||||
switch (shapeType) {
|
|
||||||
case 'circle': {
|
|
||||||
const nodeWidth = node.width || 100;
|
|
||||||
const radius = nodeWidth / 2;
|
|
||||||
iconX = -radius + iconSize / 2 + iconPadding;
|
|
||||||
iconY = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
const nodeWidth = node.width || 100;
|
|
||||||
iconX = -nodeWidth / 2 + iconSize / 2 + iconPadding;
|
|
||||||
iconY = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iconElem.attr('transform', `translate(${iconX}, ${iconY})`);
|
|
||||||
// Use currentColor to inherit label color - works for all shapes including bang
|
|
||||||
iconElem.attr('style', 'color: currentColor;');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
log.debug('SVG icon rendering failed, falling back to CSS:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to CSS approach (original mindmap behavior)
|
|
||||||
const iconClass = isCssFormat ? node.icon : node.icon.replace(':', ' ');
|
|
||||||
|
|
||||||
let iconX = 0;
|
|
||||||
const iconY = -iconSize / 2;
|
|
||||||
|
|
||||||
switch (shapeType) {
|
|
||||||
case 'circle': {
|
|
||||||
const nodeWidth = node.width || 100;
|
|
||||||
const radius = nodeWidth / 2;
|
|
||||||
iconX = -radius + iconPadding;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
const nodeWidth = node.width || 100;
|
|
||||||
iconX = -nodeWidth / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const icon = parentElement
|
|
||||||
.append('foreignObject')
|
|
||||||
.attr('height', `${iconSize}px`)
|
|
||||||
.attr('width', `${iconSize}px`)
|
|
||||||
.attr('x', iconX)
|
|
||||||
.attr('y', iconY)
|
|
||||||
.attr(
|
|
||||||
'style',
|
|
||||||
'text-align: center; display: flex; align-items: center; justify-content: center;'
|
|
||||||
);
|
|
||||||
|
|
||||||
icon
|
|
||||||
.append('div')
|
|
||||||
.attr('class', 'icon-container')
|
|
||||||
.attr(
|
|
||||||
'style',
|
|
||||||
'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;'
|
|
||||||
)
|
|
||||||
.append('i')
|
|
||||||
.attr('class', `node-icon-${section} ${iconClass}`)
|
|
||||||
.attr('style', `font-size: ${iconSize}px;`);
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ import type { LayoutData } from '../../rendering-util/types.js';
|
|||||||
import type { FilledMindMapNode } from './mindmapTypes.js';
|
import type { FilledMindMapNode } from './mindmapTypes.js';
|
||||||
import defaultConfig from '../../defaultConfig.js';
|
import defaultConfig from '../../defaultConfig.js';
|
||||||
import type { MindmapDB } from './mindmapDb.js';
|
import type { MindmapDB } from './mindmapDb.js';
|
||||||
import { getMindmapIconConfig, insertMindmapIcon } from './mindmapIconHelper.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the layout data with actual node dimensions after drawing
|
* Update the layout data with actual node dimensions after drawing
|
||||||
@@ -72,28 +71,6 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
|||||||
|
|
||||||
// Use the unified rendering system
|
// Use the unified rendering system
|
||||||
await render(data4Layout, svg);
|
await render(data4Layout, svg);
|
||||||
const genericShapes = ['hexagon', 'rect', 'rounded', 'squareRect'];
|
|
||||||
const nodesWithIcons = data4Layout.nodes.filter(
|
|
||||||
(node) => node.icon && genericShapes.includes(node.shape || '')
|
|
||||||
);
|
|
||||||
|
|
||||||
if (nodesWithIcons.length > 0) {
|
|
||||||
for (const node of nodesWithIcons) {
|
|
||||||
const nodeId = node.domId || node.id;
|
|
||||||
const nodeElement = svg.select(`g[id="${nodeId}"]`);
|
|
||||||
|
|
||||||
if (!nodeElement.empty()) {
|
|
||||||
try {
|
|
||||||
const iconConfig = getMindmapIconConfig(node.shape || 'default');
|
|
||||||
await insertMindmapIcon(nodeElement, node, iconConfig);
|
|
||||||
} catch (error) {
|
|
||||||
log.warn(`Failed to add icon to ${nodeId}:`, error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.warn(`Could not find DOM element for node ${nodeId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the view box and size of the svg element using config from data4Layout
|
// Setup the view box and size of the svg element using config from data4Layout
|
||||||
setupViewPortForSVG(
|
setupViewPortForSVG(
|
||||||
|
|||||||
@@ -67,9 +67,6 @@ const getStyles: DiagramStylesProvider = (options) =>
|
|||||||
.section-root span {
|
.section-root span {
|
||||||
color: ${options.gitBranchLabel0};
|
color: ${options.gitBranchLabel0};
|
||||||
}
|
}
|
||||||
.node-icon--1 {
|
|
||||||
color: ${options.gitBranchLabel0};
|
|
||||||
}
|
|
||||||
.section-2 span {
|
.section-2 span {
|
||||||
color: ${options.gitBranchLabel0};
|
color: ${options.gitBranchLabel0};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
"off" return 'off';
|
"off" return 'off';
|
||||||
"," return ',';
|
"," return ',';
|
||||||
";" return 'NEWLINE';
|
";" return 'NEWLINE';
|
||||||
[^\/\\\+\()\+<\->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)|\-\|\\|\-\\|\-\/|\-\/\/|\-\|\/|\/\|\-|\\\|\-|\/\/\-|\\\\\-|\/\|\-|\-\-\|\\|\-\-|\(\)))[\-]*[^\+<\->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; } //final_4.11
|
[^+<\->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)))[\-]*[^\+<\->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; }
|
||||||
"->>" return 'SOLID_ARROW';
|
"->>" return 'SOLID_ARROW';
|
||||||
"<<->>" return 'BIDIRECTIONAL_SOLID_ARROW';
|
"<<->>" return 'BIDIRECTIONAL_SOLID_ARROW';
|
||||||
"-->>" return 'DOTTED_ARROW';
|
"-->>" return 'DOTTED_ARROW';
|
||||||
@@ -89,36 +89,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
\-\-[x] return 'DOTTED_CROSS';
|
\-\-[x] return 'DOTTED_CROSS';
|
||||||
\-[\)] return 'SOLID_POINT';
|
\-[\)] return 'SOLID_POINT';
|
||||||
\-\-[\)] return 'DOTTED_POINT';
|
\-\-[\)] return 'DOTTED_POINT';
|
||||||
|
|
||||||
//normal-dotted
|
|
||||||
\-\-\|\\ return 'SOLID_ARROW_TOP_DOTTED';
|
|
||||||
\-\-\|\/ return 'SOLID_ARROW_BOTTOM_DOTTED';
|
|
||||||
\-\-\\\\ return 'STICK_ARROW_TOP_DOTTED';
|
|
||||||
\-\-\/\/ return 'STICK_ARROW_BOTTOM_DOTTED';
|
|
||||||
|
|
||||||
//reverse-dotted
|
|
||||||
\/\|\-\- return 'SOLID_ARROW_TOP_REVERSE_DOTTED';
|
|
||||||
\\\|\-\- return 'SOLID_ARROW_BOTTOM_REVERSE_DOTTED';
|
|
||||||
\/\/\-\- return 'STICK_ARROW_TOP_REVERSE_DOTTED';
|
|
||||||
\\\\\-\- return 'STICK_ARROW_BOTTOM_REVERSE_DOTTED';
|
|
||||||
|
|
||||||
//normal
|
|
||||||
\-\|\\ return 'SOLID_ARROW_TOP';
|
|
||||||
\-\|\/ return 'SOLID_ARROW_BOTTOM';
|
|
||||||
\-\\\\ return 'STICK_ARROW_TOP';
|
|
||||||
\-\/\/ return 'STICK_ARROW_BOTTOM';
|
|
||||||
|
|
||||||
//reverse
|
|
||||||
\/\|\- return 'SOLID_ARROW_TOP_REVERSE';
|
|
||||||
\\\|\- return 'SOLID_ARROW_BOTTOM_REVERSE';
|
|
||||||
\/\/\- return 'STICK_ARROW_TOP_REVERSE';
|
|
||||||
\\\\\- return 'STICK_ARROW_BOTTOM_REVERSE';
|
|
||||||
|
|
||||||
":"(?:(?:no)?wrap:)?[^#\n;]* return 'TXT';
|
":"(?:(?:no)?wrap:)?[^#\n;]* return 'TXT';
|
||||||
":" return 'TXT';
|
":" return 'TXT';
|
||||||
"+" return '+';
|
"+" return '+';
|
||||||
"-" return '-';
|
"-" return '-';
|
||||||
"()" return '()';
|
|
||||||
<<EOF>> return 'NEWLINE';
|
<<EOF>> return 'NEWLINE';
|
||||||
. return 'INVALID';
|
. return 'INVALID';
|
||||||
|
|
||||||
@@ -330,20 +304,6 @@ signal
|
|||||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
|
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
|
||||||
{type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1.actor}
|
{type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1.actor}
|
||||||
]}
|
]}
|
||||||
| actor signaltype '()' actor text2
|
|
||||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5, activate: true, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION},
|
|
||||||
{type: 'centralConnection', signalType: yy.LINETYPE.CENTRAL_CONNECTION, actor: $4.actor, }
|
|
||||||
]}
|
|
||||||
|
|
||||||
| actor '()' signaltype actor text2
|
|
||||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$3, msg:$5, activate: false, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE},
|
|
||||||
{type: 'centralConnectionReverse', signalType: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE, actor: $1.actor}
|
|
||||||
]}
|
|
||||||
| actor '()' signaltype '()' actor text2
|
|
||||||
{ $$ = [$1,$5,{type: 'addMessage', from:$1.actor, to:$5.actor, signalType:$3, msg:$6, activate: true, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION_DUAL},
|
|
||||||
{type: 'centralConnection', signalType: yy.LINETYPE.CENTRAL_CONNECTION, actor: $5.actor, },
|
|
||||||
{type: 'centralConnectionReverse', signalType: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE, actor: $1.actor}
|
|
||||||
]}
|
|
||||||
| actor signaltype actor text2
|
| actor signaltype actor text2
|
||||||
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
||||||
;
|
;
|
||||||
@@ -377,28 +337,7 @@ signaltype
|
|||||||
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
||||||
| DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
|
| DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
|
||||||
| SOLID_ARROW { $$ = yy.LINETYPE.SOLID; }
|
| SOLID_ARROW { $$ = yy.LINETYPE.SOLID; }
|
||||||
|
| BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; }
|
||||||
| SOLID_ARROW_TOP { $$ = yy.LINETYPE.SOLID_TOP; }
|
|
||||||
| SOLID_ARROW_BOTTOM { $$ = yy.LINETYPE.SOLID_BOTTOM; }
|
|
||||||
| STICK_ARROW_TOP { $$ = yy.LINETYPE.STICK_TOP; }
|
|
||||||
| STICK_ARROW_BOTTOM { $$ = yy.LINETYPE.STICK_BOTTOM; }
|
|
||||||
|
|
||||||
| SOLID_ARROW_TOP_DOTTED { $$ = yy.LINETYPE.SOLID_TOP_DOTTED; }
|
|
||||||
| SOLID_ARROW_BOTTOM_DOTTED { $$ = yy.LINETYPE.SOLID_BOTTOM_DOTTED; }
|
|
||||||
| STICK_ARROW_TOP_DOTTED { $$ = yy.LINETYPE.STICK_TOP_DOTTED; }
|
|
||||||
| STICK_ARROW_BOTTOM_DOTTED { $$ = yy.LINETYPE.STICK_BOTTOM_DOTTED; }
|
|
||||||
|
|
||||||
| SOLID_ARROW_TOP_REVERSE { $$ = yy.LINETYPE.SOLID_ARROW_TOP_REVERSE; }
|
|
||||||
| SOLID_ARROW_BOTTOM_REVERSE { $$ = yy.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE; }
|
|
||||||
| STICK_ARROW_TOP_REVERSE { $$ = yy.LINETYPE.STICK_ARROW_TOP_REVERSE; }
|
|
||||||
| STICK_ARROW_BOTTOM_REVERSE { $$ = yy.LINETYPE.STICK_ARROW_BOTTOM_REVERSE; }
|
|
||||||
|
|
||||||
| SOLID_ARROW_TOP_REVERSE_DOTTED { $$ = yy.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED; }
|
|
||||||
| SOLID_ARROW_BOTTOM_REVERSE_DOTTED { $$ = yy.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED; }
|
|
||||||
| STICK_ARROW_TOP_REVERSE_DOTTED { $$ = yy.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED; }
|
|
||||||
| STICK_ARROW_BOTTOM_REVERSE_DOTTED { $$ = yy.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED; }
|
|
||||||
|
|
||||||
| BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; }
|
|
||||||
| DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; }
|
| DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; }
|
||||||
| BIDIRECTIONAL_DOTTED_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_DOTTED; }
|
| BIDIRECTIONAL_DOTTED_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_DOTTED; }
|
||||||
| SOLID_CROSS { $$ = yy.LINETYPE.SOLID_CROSS; }
|
| SOLID_CROSS { $$ = yy.LINETYPE.SOLID_CROSS; }
|
||||||
|
|||||||
@@ -64,30 +64,6 @@ const LINETYPE = {
|
|||||||
PAR_OVER_START: 32,
|
PAR_OVER_START: 32,
|
||||||
BIDIRECTIONAL_SOLID: 33,
|
BIDIRECTIONAL_SOLID: 33,
|
||||||
BIDIRECTIONAL_DOTTED: 34,
|
BIDIRECTIONAL_DOTTED: 34,
|
||||||
|
|
||||||
SOLID_TOP: 41,
|
|
||||||
SOLID_BOTTOM: 42,
|
|
||||||
STICK_TOP: 43,
|
|
||||||
STICK_BOTTOM: 44,
|
|
||||||
|
|
||||||
SOLID_ARROW_TOP_REVERSE: 45,
|
|
||||||
SOLID_ARROW_BOTTOM_REVERSE: 46,
|
|
||||||
STICK_ARROW_TOP_REVERSE: 47,
|
|
||||||
STICK_ARROW_BOTTOM_REVERSE: 48,
|
|
||||||
|
|
||||||
SOLID_TOP_DOTTED: 51,
|
|
||||||
SOLID_BOTTOM_DOTTED: 52,
|
|
||||||
STICK_TOP_DOTTED: 53,
|
|
||||||
STICK_BOTTOM_DOTTED: 54,
|
|
||||||
|
|
||||||
SOLID_ARROW_TOP_REVERSE_DOTTED: 55,
|
|
||||||
SOLID_ARROW_BOTTOM_REVERSE_DOTTED: 56,
|
|
||||||
STICK_ARROW_TOP_REVERSE_DOTTED: 57,
|
|
||||||
STICK_ARROW_BOTTOM_REVERSE_DOTTED: 58,
|
|
||||||
|
|
||||||
CENTRAL_CONNECTION: 59,
|
|
||||||
CENTRAL_CONNECTION_REVERSE: 60,
|
|
||||||
CENTRAL_CONNECTION_DUAL: 61,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const ARROWTYPE = {
|
const ARROWTYPE = {
|
||||||
@@ -268,8 +244,7 @@ export class SequenceDB implements DiagramDB {
|
|||||||
idTo?: Message['to'],
|
idTo?: Message['to'],
|
||||||
message?: { text: string; wrap: boolean },
|
message?: { text: string; wrap: boolean },
|
||||||
messageType?: number,
|
messageType?: number,
|
||||||
activate = false,
|
activate = false
|
||||||
centralConnection?: number
|
|
||||||
) {
|
) {
|
||||||
if (messageType === this.LINETYPE.ACTIVE_END) {
|
if (messageType === this.LINETYPE.ACTIVE_END) {
|
||||||
const cnt = this.activationCount(idFrom ?? '');
|
const cnt = this.activationCount(idFrom ?? '');
|
||||||
@@ -296,7 +271,6 @@ export class SequenceDB implements DiagramDB {
|
|||||||
wrap: message?.wrap ?? this.autoWrap(),
|
wrap: message?.wrap ?? this.autoWrap(),
|
||||||
type: messageType,
|
type: messageType,
|
||||||
activate,
|
activate,
|
||||||
centralConnection: centralConnection ?? 0,
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -589,12 +563,6 @@ export class SequenceDB implements DiagramDB {
|
|||||||
case 'activeStart':
|
case 'activeStart':
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
||||||
break;
|
break;
|
||||||
case 'centralConnection':
|
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
|
||||||
break;
|
|
||||||
case 'centralConnectionReverse':
|
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
|
||||||
break;
|
|
||||||
case 'activeEnd':
|
case 'activeEnd':
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
||||||
break;
|
break;
|
||||||
@@ -638,14 +606,7 @@ export class SequenceDB implements DiagramDB {
|
|||||||
this.state.records.lastDestroyed = undefined;
|
this.state.records.lastDestroyed = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.addSignal(
|
this.addSignal(param.from, param.to, param.msg, param.signalType, param.activate);
|
||||||
param.from,
|
|
||||||
param.to,
|
|
||||||
param.msg,
|
|
||||||
param.signalType,
|
|
||||||
param.activate,
|
|
||||||
param.centralConnection
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 'boxStart':
|
case 'boxStart':
|
||||||
this.addBox(param.boxData);
|
this.addBox(param.boxData);
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Alice",
|
"from": "Alice",
|
||||||
"id": "0",
|
"id": "0",
|
||||||
"message": "Hello Bob, how are you?",
|
"message": "Hello Bob, how are you?",
|
||||||
@@ -114,7 +113,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Bob",
|
"from": "Bob",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"message": "I am good thanks!",
|
"message": "I am good thanks!",
|
||||||
@@ -133,7 +131,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Alice",
|
"from": "Alice",
|
||||||
"id": "0",
|
"id": "0",
|
||||||
"message": "Hello Bob, how are you?",
|
"message": "Hello Bob, how are you?",
|
||||||
@@ -143,7 +140,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Bob",
|
"from": "Bob",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"message": "I am good thanks!",
|
"message": "I am good thanks!",
|
||||||
@@ -164,7 +160,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Alice",
|
"from": "Alice",
|
||||||
"id": "0",
|
"id": "0",
|
||||||
"message": "Hello John, how are you?",
|
"message": "Hello John, how are you?",
|
||||||
@@ -174,7 +169,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "John",
|
"from": "John",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"message": "I am good thanks!",
|
"message": "I am good thanks!",
|
||||||
@@ -187,254 +181,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Central Connection Parsing', () => {
|
|
||||||
describe('when parsing central connection syntax', () => {
|
|
||||||
it('should parse actor ()->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
// Find the actual message (type: 'addMessage')
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 0, // SOLID (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()-->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()-->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 1, // DOTTED (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ->>() actor syntax as CENTRAL_CONNECTION', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(2); // addMessage + centralConnection (no activation for this pattern)
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 59, // CENTRAL_CONNECTION
|
|
||||||
activate: true,
|
|
||||||
type: 0, // SOLID (based on actual parsing)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()-->> actor syntax as CENTRAL_CONNECTION_REVERSE', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()-->> Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(2); // addMessage + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 60, // CENTRAL_CONNECTION_REVERSE
|
|
||||||
activate: false,
|
|
||||||
type: 1, // DOTTED (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()->> actor syntax as CENTRAL_CONNECTION_REVERSE', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()->> Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(2); // addMessage + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 60, // CENTRAL_CONNECTION_REVERSE
|
|
||||||
activate: false,
|
|
||||||
type: 0, // SOLID (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()<<-->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()<<-->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 34, // BIDIRECTIONAL_DOTTED
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()<<->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()<<->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 33, // BIDIRECTIONAL_SOLID
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle multiple central connection types in one diagram', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Message 1
|
|
||||||
Bob ()-->> Charlie: Message 2
|
|
||||||
Charlie ()<<-->>() Alice: Message 3
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(8); // 3 addMessages + 5 central connection markers
|
|
||||||
|
|
||||||
// Filter to get only the actual messages
|
|
||||||
const actualMessages = messages.filter((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessages).toHaveLength(3);
|
|
||||||
|
|
||||||
expect(actualMessages[0]).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()->>())
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(actualMessages[1]).toMatchObject({
|
|
||||||
from: 'Bob',
|
|
||||||
to: 'Charlie',
|
|
||||||
centralConnection: 60, // CENTRAL_CONNECTION_REVERSE (()-->>)
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(actualMessages[2]).toMatchObject({
|
|
||||||
from: 'Charlie',
|
|
||||||
to: 'Alice',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()<<-->>())
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle central connections with different arrow types', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()-x() Bob: Cross message
|
|
||||||
Alice ()--x() Bob: Dotted cross message
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(6); // 2 addMessages + 4 central connection markers
|
|
||||||
|
|
||||||
const actualMessages = messages.filter((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessages).toHaveLength(2);
|
|
||||||
|
|
||||||
expect(actualMessages[0]).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()-x())
|
|
||||||
type: 3, // SOLID_CROSS
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(actualMessages[1]).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()--x())
|
|
||||||
type: 4, // DOTTED_CROSS
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not break existing parsing without central connections', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ->> Bob: Normal message
|
|
||||||
Bob -->> Alice: Normal dotted message
|
|
||||||
Alice -x Bob: Normal cross message
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3);
|
|
||||||
|
|
||||||
messages.forEach((msg) => {
|
|
||||||
expect(msg.centralConnection).toBe(0); // No central connection
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(messages[0].type).toBe(0); // SOLID (based on actual parsing)
|
|
||||||
expect(messages[1].type).toBe(1); // DOTTED (based on actual parsing)
|
|
||||||
expect(messages[2].type).toBe(3); // SOLID_CROSS
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when parsing a sequenceDiagram', function () {
|
describe('when parsing a sequenceDiagram', function () {
|
||||||
let diagram;
|
let diagram;
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
@@ -2312,36 +2058,6 @@ Bob->>Alice:Got it!
|
|||||||
expect(messages[0].from).toBe('Alice');
|
expect(messages[0].from).toBe('Alice');
|
||||||
expect(messages[0].to).toBe('Bob');
|
expect(messages[0].to).toBe('Bob');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('1 should parse ', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
actor Bob
|
|
||||||
actor Alice
|
|
||||||
Bob -|\\ Alice: Hello Alice, how are you?
|
|
||||||
Bob -|/ Alice: Hello Alice, how are you?
|
|
||||||
Bob -// Alice: Hello Alice, how are you?
|
|
||||||
Bob -\\\\ Alice: Hello Alice, how are you?
|
|
||||||
|
|
||||||
Bob \\|- Alice: Hello Alice, how are you?
|
|
||||||
Bob /|- Alice: Hello Alice, how are you?
|
|
||||||
Bob //- Alice: Hello Alice, how are you?
|
|
||||||
Bob \\\\- Alice: Hello Alice, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('2 should parse ', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
actor Bob
|
|
||||||
actor Alice
|
|
||||||
Alice ()<<->>() Bob: hey?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
});
|
|
||||||
describe('when parsing extended participant syntax', () => {
|
describe('when parsing extended participant syntax', () => {
|
||||||
it('should parse participants with different quote styles and whitespace', async () => {
|
it('should parse participants with different quote styles and whitespace', async () => {
|
||||||
const diagram = await Diagram.fromText(`
|
const diagram = await Diagram.fromText(`
|
||||||
|
|||||||
@@ -282,49 +282,6 @@ const drawNote = async function (elem: any, noteModel: NoteModel) {
|
|||||||
bounds.models.addNote(noteModel);
|
bounds.models.addNote(noteModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawCentralConnection = function (
|
|
||||||
elem: any,
|
|
||||||
msg: any,
|
|
||||||
msgModel: any,
|
|
||||||
diagObj: Diagram,
|
|
||||||
startx: number,
|
|
||||||
stopx: number,
|
|
||||||
lineStartY: number
|
|
||||||
) {
|
|
||||||
const actors = diagObj.db.getActors();
|
|
||||||
const fromActor = actors.get(msg.from);
|
|
||||||
const toActor = actors.get(msg.to);
|
|
||||||
const fromCenter = fromActor.x + fromActor.width / 2;
|
|
||||||
const toCenter = toActor.x + toActor.width / 2;
|
|
||||||
|
|
||||||
const g = elem.append('g');
|
|
||||||
|
|
||||||
const drawCircle = (cx: number) => {
|
|
||||||
g.append('circle')
|
|
||||||
.attr('cx', cx)
|
|
||||||
.attr('cy', lineStartY)
|
|
||||||
.attr('r', 5)
|
|
||||||
.attr('width', 10)
|
|
||||||
.attr('height', 10);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL } =
|
|
||||||
diagObj.db.LINETYPE;
|
|
||||||
|
|
||||||
switch (msg.centralConnection) {
|
|
||||||
case CENTRAL_CONNECTION:
|
|
||||||
drawCircle(toCenter);
|
|
||||||
break;
|
|
||||||
case CENTRAL_CONNECTION_REVERSE:
|
|
||||||
drawCircle(fromCenter);
|
|
||||||
break;
|
|
||||||
case CENTRAL_CONNECTION_DUAL:
|
|
||||||
drawCircle(fromCenter);
|
|
||||||
drawCircle(toCenter);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const messageFont = (cnf) => {
|
const messageFont = (cnf) => {
|
||||||
return {
|
return {
|
||||||
fontFamily: cnf.messageFontFamily,
|
fontFamily: cnf.messageFontFamily,
|
||||||
@@ -410,7 +367,7 @@ async function boundMessage(_diagram, msgModel): Promise<number> {
|
|||||||
* @param lineStartY - The Y coordinate at which the message line starts
|
* @param lineStartY - The Y coordinate at which the message line starts
|
||||||
* @param diagObj - The diagram object.
|
* @param diagObj - The diagram object.
|
||||||
*/
|
*/
|
||||||
const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram, msg) {
|
const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram) {
|
||||||
const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel;
|
const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel;
|
||||||
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
||||||
const textObj = svgDrawCommon.getTextObj();
|
const textObj = svgDrawCommon.getTextObj();
|
||||||
@@ -476,9 +433,6 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
line.attr('y1', lineStartY);
|
line.attr('y1', lineStartY);
|
||||||
line.attr('x2', stopx);
|
line.attr('x2', stopx);
|
||||||
line.attr('y2', lineStartY);
|
line.attr('y2', lineStartY);
|
||||||
if (hasCentralConnection(msg, diagObj)) {
|
|
||||||
drawCentralConnection(diagram, msg, msgModel, diagObj, startx, stopx, lineStartY);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Make an SVG Container
|
// Make an SVG Container
|
||||||
// Draw the line
|
// Draw the line
|
||||||
@@ -487,15 +441,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
type === diagObj.db.LINETYPE.DOTTED_CROSS ||
|
type === diagObj.db.LINETYPE.DOTTED_CROSS ||
|
||||||
type === diagObj.db.LINETYPE.DOTTED_POINT ||
|
type === diagObj.db.LINETYPE.DOTTED_POINT ||
|
||||||
type === diagObj.db.LINETYPE.DOTTED_OPEN ||
|
type === diagObj.db.LINETYPE.DOTTED_OPEN ||
|
||||||
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED ||
|
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED
|
||||||
type === diagObj.db.LINETYPE.SOLID_TOP_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_TOP_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED
|
|
||||||
) {
|
) {
|
||||||
line.style('stroke-dasharray', '3, 3');
|
line.style('stroke-dasharray', '3, 3');
|
||||||
line.attr('class', 'messageLine1');
|
line.attr('class', 'messageLine1');
|
||||||
@@ -511,51 +457,6 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
line.attr('stroke-width', 2);
|
line.attr('stroke-width', 2);
|
||||||
line.attr('stroke', 'none'); // handled by theme/css anyway
|
line.attr('stroke', 'none'); // handled by theme/css anyway
|
||||||
line.style('fill', 'none'); // remove any fill colour
|
line.style('fill', 'none'); // remove any fill colour
|
||||||
|
|
||||||
if (type === diagObj.db.LINETYPE.SOLID_TOP || type === diagObj.db.LINETYPE.SOLID_TOP_DOTTED) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#solidTopArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_BOTTOM ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#solidBottomArrowHead)');
|
|
||||||
}
|
|
||||||
if (type === diagObj.db.LINETYPE.STICK_TOP || type === diagObj.db.LINETYPE.STICK_TOP_DOTTED) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#stickTopArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.STICK_BOTTOM ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#stickBottomArrowHead)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#solidBottomArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#solidTopArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#stickBottomArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#stickTopArrowHead)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === diagObj.db.LINETYPE.SOLID || type === diagObj.db.LINETYPE.DOTTED) {
|
if (type === diagObj.db.LINETYPE.SOLID || type === diagObj.db.LINETYPE.DOTTED) {
|
||||||
line.attr('marker-end', 'url(' + url + '#arrowhead)');
|
line.attr('marker-end', 'url(' + url + '#arrowhead)');
|
||||||
}
|
}
|
||||||
@@ -580,18 +481,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
type === diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID ||
|
type === diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID ||
|
||||||
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED;
|
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED;
|
||||||
|
|
||||||
const isReverseArrowType =
|
if (isBidirectional) {
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED;
|
|
||||||
|
|
||||||
let x = 0;
|
|
||||||
if (isBidirectional || isReverseArrowType) {
|
|
||||||
const SEQUENCE_NUMBER_RADIUS = 6;
|
const SEQUENCE_NUMBER_RADIUS = 6;
|
||||||
|
|
||||||
if (startx < stopx) {
|
if (startx < stopx) {
|
||||||
@@ -599,7 +489,6 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
} else {
|
} else {
|
||||||
line.attr('x1', startx + SEQUENCE_NUMBER_RADIUS);
|
line.attr('x1', startx + SEQUENCE_NUMBER_RADIUS);
|
||||||
}
|
}
|
||||||
x = 3.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
diagram
|
diagram
|
||||||
@@ -609,8 +498,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
.attr('x2', startx)
|
.attr('x2', startx)
|
||||||
.attr('y2', lineStartY)
|
.attr('y2', lineStartY)
|
||||||
.attr('stroke-width', 0)
|
.attr('stroke-width', 0)
|
||||||
.attr('marker-start', 'url(' + url + '#sequencenumber)')
|
.attr('marker-start', 'url(' + url + '#sequencenumber)');
|
||||||
.attr('transform', `translate(-${x}, 0)`);
|
|
||||||
|
|
||||||
diagram
|
diagram
|
||||||
.append('text')
|
.append('text')
|
||||||
@@ -620,8 +508,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
.attr('font-size', '12px')
|
.attr('font-size', '12px')
|
||||||
.attr('text-anchor', 'middle')
|
.attr('text-anchor', 'middle')
|
||||||
.attr('class', 'sequenceNumber')
|
.attr('class', 'sequenceNumber')
|
||||||
.text(sequenceIndex)
|
.text(sequenceIndex);
|
||||||
.attr('transform', `translate(-${x}, 0)`);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -970,10 +857,6 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
svgDraw.insertArrowCrossHead(diagram);
|
svgDraw.insertArrowCrossHead(diagram);
|
||||||
svgDraw.insertArrowFilledHead(diagram);
|
svgDraw.insertArrowFilledHead(diagram);
|
||||||
svgDraw.insertSequenceNumber(diagram);
|
svgDraw.insertSequenceNumber(diagram);
|
||||||
svgDraw.insertSolidTopArrowHead(diagram);
|
|
||||||
svgDraw.insertSolidBottomArrowHead(diagram);
|
|
||||||
svgDraw.insertStickTopArrowHead(diagram);
|
|
||||||
svgDraw.insertStickBottomArrowHead(diagram);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param msg - The message to draw.
|
* @param msg - The message to draw.
|
||||||
@@ -1014,12 +897,6 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
case diagObj.db.LINETYPE.ACTIVE_START:
|
case diagObj.db.LINETYPE.ACTIVE_START:
|
||||||
bounds.newActivation(msg, diagram, actors);
|
bounds.newActivation(msg, diagram, actors);
|
||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.CENTRAL_CONNECTION:
|
|
||||||
bounds.newActivation(msg, diagram, actors);
|
|
||||||
break;
|
|
||||||
case diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE:
|
|
||||||
bounds.newActivation(msg, diagram, actors);
|
|
||||||
break;
|
|
||||||
case diagObj.db.LINETYPE.ACTIVE_END:
|
case diagObj.db.LINETYPE.ACTIVE_END:
|
||||||
activeEnd(msg, bounds.getVerticalPos());
|
activeEnd(msg, bounds.getVerticalPos());
|
||||||
break;
|
break;
|
||||||
@@ -1178,7 +1055,7 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
createdActors,
|
createdActors,
|
||||||
destroyedActors
|
destroyedActors
|
||||||
);
|
);
|
||||||
messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY, msg });
|
messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY });
|
||||||
bounds.models.addMessage(msgModel);
|
bounds.models.addMessage(msgModel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('error while drawing message', e);
|
log.error('error while drawing message', e);
|
||||||
@@ -1191,27 +1068,6 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
diagObj.db.LINETYPE.SOLID_OPEN,
|
diagObj.db.LINETYPE.SOLID_OPEN,
|
||||||
diagObj.db.LINETYPE.DOTTED_OPEN,
|
diagObj.db.LINETYPE.DOTTED_OPEN,
|
||||||
diagObj.db.LINETYPE.SOLID,
|
diagObj.db.LINETYPE.SOLID,
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.DOTTED,
|
diagObj.db.LINETYPE.DOTTED,
|
||||||
diagObj.db.LINETYPE.SOLID_CROSS,
|
diagObj.db.LINETYPE.SOLID_CROSS,
|
||||||
diagObj.db.LINETYPE.DOTTED_CROSS,
|
diagObj.db.LINETYPE.DOTTED_CROSS,
|
||||||
@@ -1231,7 +1087,7 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
await drawActors(diagram, actors, actorKeys, false);
|
await drawActors(diagram, actors, actorKeys, false);
|
||||||
|
|
||||||
for (const e of messagesToDraw) {
|
for (const e of messagesToDraw) {
|
||||||
await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj, e.msg);
|
await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj);
|
||||||
}
|
}
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
await drawActors(diagram, actors, actorKeys, true);
|
await drawActors(diagram, actors, actorKeys, true);
|
||||||
@@ -1605,85 +1461,12 @@ const buildNoteModel = async function (msg, actors, diagObj) {
|
|||||||
return noteModel;
|
return noteModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Central connection positioning constants
|
|
||||||
const CENTRAL_CONNECTION_BASE_OFFSET = 4;
|
|
||||||
const CENTRAL_CONNECTION_BIDIRECTIONAL_OFFSET = 6;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a message has central connection
|
|
||||||
* @param msg - The message object
|
|
||||||
* @param diagObj - The diagram object containing LINETYPE constants
|
|
||||||
* @returns True if the message has any type of central connection
|
|
||||||
*/
|
|
||||||
const hasCentralConnection = function (msg, diagObj) {
|
|
||||||
const { CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL } =
|
|
||||||
diagObj.db.LINETYPE;
|
|
||||||
return [CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL].includes(
|
|
||||||
msg.centralConnection
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the positioning offset for central connection arrows
|
|
||||||
* @param msg - The message object
|
|
||||||
* @param diagObj - The diagram object containing LINETYPE constants
|
|
||||||
* @param isArrowToRight - Whether the arrow is pointing to the right
|
|
||||||
* @returns The offset to apply to startx position
|
|
||||||
*/
|
|
||||||
const calculateCentralConnectionOffset = function (msg, diagObj, isArrowToRight) {
|
|
||||||
const {
|
|
||||||
CENTRAL_CONNECTION_REVERSE,
|
|
||||||
CENTRAL_CONNECTION_DUAL,
|
|
||||||
BIDIRECTIONAL_SOLID,
|
|
||||||
BIDIRECTIONAL_DOTTED,
|
|
||||||
} = diagObj.db.LINETYPE;
|
|
||||||
|
|
||||||
let offset = 0;
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.centralConnection === CENTRAL_CONNECTION_REVERSE ||
|
|
||||||
msg.centralConnection === CENTRAL_CONNECTION_DUAL
|
|
||||||
) {
|
|
||||||
offset += CENTRAL_CONNECTION_BASE_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.centralConnection === CENTRAL_CONNECTION_DUAL &&
|
|
||||||
(msg.type === BIDIRECTIONAL_SOLID || msg.type === BIDIRECTIONAL_DOTTED)
|
|
||||||
) {
|
|
||||||
offset += isArrowToRight ? 0 : -CENTRAL_CONNECTION_BIDIRECTIONAL_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildMessageModel = function (msg, actors, diagObj) {
|
const buildMessageModel = function (msg, actors, diagObj) {
|
||||||
if (
|
if (
|
||||||
![
|
![
|
||||||
diagObj.db.LINETYPE.SOLID_OPEN,
|
diagObj.db.LINETYPE.SOLID_OPEN,
|
||||||
diagObj.db.LINETYPE.DOTTED_OPEN,
|
diagObj.db.LINETYPE.DOTTED_OPEN,
|
||||||
diagObj.db.LINETYPE.SOLID,
|
diagObj.db.LINETYPE.SOLID,
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.DOTTED,
|
diagObj.db.LINETYPE.DOTTED,
|
||||||
diagObj.db.LINETYPE.SOLID_CROSS,
|
diagObj.db.LINETYPE.SOLID_CROSS,
|
||||||
diagObj.db.LINETYPE.DOTTED_CROSS,
|
diagObj.db.LINETYPE.DOTTED_CROSS,
|
||||||
@@ -1701,8 +1484,6 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
|||||||
let startx = isArrowToRight ? fromRight : fromLeft;
|
let startx = isArrowToRight ? fromRight : fromLeft;
|
||||||
let stopx = isArrowToRight ? toLeft : toRight;
|
let stopx = isArrowToRight ? toLeft : toRight;
|
||||||
|
|
||||||
// Apply central connection positioning adjustments
|
|
||||||
startx += calculateCentralConnectionOffset(msg, diagObj, isArrowToRight);
|
|
||||||
// As the line width is considered, the left and right values will be off by 2.
|
// As the line width is considered, the left and right values will be off by 2.
|
||||||
const isArrowToActivation = Math.abs(toLeft - toRight) > 2;
|
const isArrowToActivation = Math.abs(toLeft - toRight) > 2;
|
||||||
|
|
||||||
@@ -1736,30 +1517,7 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
|||||||
* Shorten the length of arrow at the end and move the marker forward (using refX) to have a clean arrowhead
|
* Shorten the length of arrow at the end and move the marker forward (using refX) to have a clean arrowhead
|
||||||
* This is not required for open arrows that don't have arrowheads
|
* This is not required for open arrows that don't have arrowheads
|
||||||
*/
|
*/
|
||||||
if (
|
if (![diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN].includes(msg.type)) {
|
||||||
![
|
|
||||||
diagObj.db.LINETYPE.SOLID_OPEN,
|
|
||||||
diagObj.db.LINETYPE.DOTTED_OPEN,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
].includes(msg.type)
|
|
||||||
) {
|
|
||||||
stopx += adjustValue(3);
|
stopx += adjustValue(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1767,14 +1525,9 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
|||||||
* Shorten start position of bidirectional arrow to accommodate for second arrowhead
|
* Shorten start position of bidirectional arrow to accommodate for second arrowhead
|
||||||
*/
|
*/
|
||||||
if (
|
if (
|
||||||
[
|
[diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID, diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED].includes(
|
||||||
diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID,
|
msg.type
|
||||||
diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED,
|
)
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
].includes(msg.type)
|
|
||||||
) {
|
) {
|
||||||
startx -= adjustValue(3);
|
startx -= adjustValue(3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1709,77 +1709,6 @@ const _drawMenuItemTextCandidateFunc = (function () {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
|
||||||
*
|
|
||||||
* @param elem
|
|
||||||
*/
|
|
||||||
export const insertSolidTopArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'solidTopArrowHead')
|
|
||||||
.attr('refX', 7.9)
|
|
||||||
.attr('refY', 7.25)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 0 L 10 8 L 0 8 z'); // this is actual shape for arrowhead
|
|
||||||
};
|
|
||||||
|
|
||||||
export const insertSolidBottomArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'solidBottomArrowHead')
|
|
||||||
.attr('refX', 7.9)
|
|
||||||
.attr('refY', 0.75)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 0 L 10 0 L 0 8 z');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const insertStickTopArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'stickTopArrowHead')
|
|
||||||
.attr('refX', 7.5)
|
|
||||||
.attr('refY', 7)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 0 L 7 7')
|
|
||||||
.attr('stroke', 'black')
|
|
||||||
.attr('stroke-width', 1.5)
|
|
||||||
.attr('fill', 'none');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const insertStickBottomArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'stickBottomArrowHead')
|
|
||||||
.attr('refX', 7.5)
|
|
||||||
.attr('refY', 0)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 7 L 7 0')
|
|
||||||
.attr('stroke', 'black')
|
|
||||||
.attr('stroke-width', 1.5)
|
|
||||||
.attr('fill', 'none');
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
drawRect,
|
drawRect,
|
||||||
drawText,
|
drawText,
|
||||||
@@ -1802,8 +1731,4 @@ export default {
|
|||||||
getNoteRect,
|
getNoteRect,
|
||||||
fixLifeLineHeights,
|
fixLifeLineHeights,
|
||||||
sanitizeUrl,
|
sanitizeUrl,
|
||||||
insertSolidTopArrowHead,
|
|
||||||
insertSolidBottomArrowHead,
|
|
||||||
insertStickTopArrowHead,
|
|
||||||
insertStickBottomArrowHead,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export interface Message {
|
|||||||
type?: number;
|
type?: number;
|
||||||
activate?: boolean;
|
activate?: boolean;
|
||||||
placement?: string;
|
placement?: string;
|
||||||
centralConnection?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddMessageParams {
|
export interface AddMessageParams {
|
||||||
@@ -51,8 +50,6 @@ export interface AddMessageParams {
|
|||||||
| 'destroyParticipant'
|
| 'destroyParticipant'
|
||||||
| 'activeStart'
|
| 'activeStart'
|
||||||
| 'activeEnd'
|
| 'activeEnd'
|
||||||
| 'centralConnection'
|
|
||||||
| 'centralConnectionReverse'
|
|
||||||
| 'addNote'
|
| 'addNote'
|
||||||
| 'addLinks'
|
| 'addLinks'
|
||||||
| 'addALink'
|
| 'addALink'
|
||||||
|
|||||||
@@ -216,11 +216,7 @@ Messages can be of two displayed either solid or with a dotted line.
|
|||||||
[Actor][Arrow][Actor]:Message text
|
[Actor][Arrow][Actor]:Message text
|
||||||
```
|
```
|
||||||
|
|
||||||
Lines can be solid or dotted, and can end with various types of arrowheads, crosses, or open arrows.
|
There are ten types of arrows currently supported:
|
||||||
|
|
||||||
#### Supported Arrow Types
|
|
||||||
|
|
||||||
**Standard Arrow Types**
|
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| -------- | ---------------------------------------------------- |
|
| -------- | ---------------------------------------------------- |
|
||||||
@@ -235,49 +231,6 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros
|
|||||||
| `-)` | Solid line with an open arrow at the end (async) |
|
| `-)` | Solid line with an open arrow at the end (async) |
|
||||||
| `--)` | Dotted line with a open arrow at the end (async) |
|
| `--)` | Dotted line with a open arrow at the end (async) |
|
||||||
|
|
||||||
**Half-Arrows (v<MERMAID_RELEASE_VERSION>+)**
|
|
||||||
|
|
||||||
The following half-arrow types are supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
| Type | Description |
|
|
||||||
| ------- | ---------------------------------------------------- |
|
|
||||||
| `-\|\` | Solid line with top half arrowhead |
|
|
||||||
| `--\|\` | Dotted line with top half arrowhead |
|
|
||||||
| `-\|/` | Solid line with bottom half arrowhead |
|
|
||||||
| `--\|/` | Dotted line with bottom half arrowhead |
|
|
||||||
| `/\|-` | Solid line with reverse top half arrowhead |
|
|
||||||
| `/\|--` | Dotted line with reverse top half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom half arrowhead |
|
|
||||||
| `-\\` | Solid line with top stick half arrowhead |
|
|
||||||
| `--\\` | Dotted line with top stick half arrowhead |
|
|
||||||
| `-//` | Solid line with bottom stick half arrowhead |
|
|
||||||
| `--//` | Dotted line with bottom stick half arrowhead |
|
|
||||||
| `//-` | Solid line with reverse top stick half arrowhead |
|
|
||||||
| `//--` | Dotted line with reverse top stick half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom stick half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom stick half arrowhead |
|
|
||||||
|
|
||||||
## Central Connections (v<MERMAID_RELEASE_VERSION>+)
|
|
||||||
|
|
||||||
Mermaid sequence diagrams support **central lifeline connections** using a `()`.
|
|
||||||
This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another.
|
|
||||||
|
|
||||||
To indicate a central connection, append `()` to the arrow syntax.
|
|
||||||
|
|
||||||
#### Basic Syntax
|
|
||||||
|
|
||||||
```mermaid-example
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant John
|
|
||||||
Alice->>()John: Hello John
|
|
||||||
Alice()->>John: How are you?
|
|
||||||
John()->>()Alice: Great!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Activations
|
## Activations
|
||||||
|
|
||||||
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ describe('when using mermaid and ', () => {
|
|||||||
[Error: Parse error on line 2:
|
[Error: Parse error on line 2:
|
||||||
...equenceDiagramAlice:->Bob: Hello Bob, h...
|
...equenceDiagramAlice:->Bob: Hello Bob, h...
|
||||||
----------------------^
|
----------------------^
|
||||||
Expecting '()', 'SOLID_OPEN_ARROW', 'DOTTED_OPEN_ARROW', 'SOLID_ARROW', 'SOLID_ARROW_TOP', 'SOLID_ARROW_BOTTOM', 'STICK_ARROW_TOP', 'STICK_ARROW_BOTTOM', 'SOLID_ARROW_TOP_DOTTED', 'SOLID_ARROW_BOTTOM_DOTTED', 'STICK_ARROW_TOP_DOTTED', 'STICK_ARROW_BOTTOM_DOTTED', 'SOLID_ARROW_TOP_REVERSE', 'SOLID_ARROW_BOTTOM_REVERSE', 'STICK_ARROW_TOP_REVERSE', 'STICK_ARROW_BOTTOM_REVERSE', 'SOLID_ARROW_TOP_REVERSE_DOTTED', 'SOLID_ARROW_BOTTOM_REVERSE_DOTTED', 'STICK_ARROW_TOP_REVERSE_DOTTED', 'STICK_ARROW_BOTTOM_REVERSE_DOTTED', 'BIDIRECTIONAL_SOLID_ARROW', 'DOTTED_ARROW', 'BIDIRECTIONAL_DOTTED_ARROW', 'SOLID_CROSS', 'DOTTED_CROSS', 'SOLID_POINT', 'DOTTED_POINT', got 'TXT']
|
Expecting 'SOLID_OPEN_ARROW', 'DOTTED_OPEN_ARROW', 'SOLID_ARROW', 'BIDIRECTIONAL_SOLID_ARROW', 'DOTTED_ARROW', 'BIDIRECTIONAL_DOTTED_ARROW', 'SOLID_CROSS', 'DOTTED_CROSS', 'SOLID_POINT', 'DOTTED_POINT', got 'TXT']
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ import rough from 'roughjs';
|
|||||||
import type { D3Selection } from '../../../types.js';
|
import type { D3Selection } from '../../../types.js';
|
||||||
import { handleUndefinedAttr } from '../../../utils.js';
|
import { handleUndefinedAttr } from '../../../utils.js';
|
||||||
import type { Bounds, Point } from '../../../types.js';
|
import type { Bounds, Point } from '../../../types.js';
|
||||||
import {
|
|
||||||
calculateMindmapDimensions,
|
|
||||||
getMindmapIconConfig,
|
|
||||||
insertMindmapIcon,
|
|
||||||
} from '../../../diagrams/mindmap/mindmapIconHelper.js';
|
|
||||||
|
|
||||||
export async function bang<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
export async function bang<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
@@ -22,34 +17,20 @@ export async function bang<T extends SVGGraphicsElement>(parent: D3Selection<T>,
|
|||||||
getNodeClasses(node)
|
getNodeClasses(node)
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseWidth = bbox.width + 10 * halfPadding;
|
const w = bbox.width + 10 * halfPadding;
|
||||||
const baseHeight = bbox.height + 8 * halfPadding;
|
const h = bbox.height + 8 * halfPadding;
|
||||||
|
const r = 0.15 * w;
|
||||||
const iconConfig = getMindmapIconConfig('bang');
|
const { cssStyles } = node;
|
||||||
const dimensions = calculateMindmapDimensions(
|
|
||||||
node,
|
|
||||||
bbox,
|
|
||||||
baseWidth,
|
|
||||||
baseHeight,
|
|
||||||
halfPadding,
|
|
||||||
iconConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
const w = dimensions.width;
|
|
||||||
const h = dimensions.height;
|
|
||||||
|
|
||||||
node.width = w;
|
|
||||||
node.height = h;
|
|
||||||
|
|
||||||
const minWidth = bbox.width + 20;
|
const minWidth = bbox.width + 20;
|
||||||
const minHeight = bbox.height + 20;
|
const minHeight = bbox.height + 20;
|
||||||
const effectiveWidth = Math.max(w, minWidth);
|
const effectiveWidth = Math.max(w, minWidth);
|
||||||
const effectiveHeight = Math.max(h, minHeight);
|
const effectiveHeight = Math.max(h, minHeight);
|
||||||
|
|
||||||
label.attr('transform', `translate(${dimensions.labelOffset.x}, ${dimensions.labelOffset.y})`);
|
label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`);
|
||||||
|
|
||||||
const r = 0.15 * effectiveWidth;
|
let bangElem;
|
||||||
const path = `M0 0
|
const path = `M0 0
|
||||||
a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${-1 * effectiveHeight * 0.1}
|
a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${-1 * effectiveHeight * 0.1}
|
||||||
a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${0}
|
a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${0}
|
||||||
a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${0}
|
a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${0}
|
||||||
@@ -69,16 +50,13 @@ export async function bang<T extends SVGGraphicsElement>(parent: D3Selection<T>,
|
|||||||
a${r},${r} 1 0,0 ${effectiveWidth * 0.1},${-1 * effectiveHeight * 0.33}
|
a${r},${r} 1 0,0 ${effectiveWidth * 0.1},${-1 * effectiveHeight * 0.33}
|
||||||
H0 V0 Z`;
|
H0 V0 Z`;
|
||||||
|
|
||||||
let bangElem;
|
|
||||||
if (node.look === 'handDrawn') {
|
if (node.look === 'handDrawn') {
|
||||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, {});
|
const options = userNodeOverrides(node, {});
|
||||||
const roughNode = rc.path(path, options);
|
const roughNode = rc.path(path, options);
|
||||||
bangElem = shapeSvg.insert(() => roughNode, ':first-child');
|
bangElem = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
bangElem
|
bangElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles));
|
||||||
.attr('class', 'basic label-container')
|
|
||||||
.attr('style', handleUndefinedAttr(node.cssStyles));
|
|
||||||
} else {
|
} else {
|
||||||
bangElem = shapeSvg
|
bangElem = shapeSvg
|
||||||
.insert('path', ':first-child')
|
.insert('path', ':first-child')
|
||||||
@@ -90,10 +68,6 @@ export async function bang<T extends SVGGraphicsElement>(parent: D3Selection<T>,
|
|||||||
// Translate the path (center the shape)
|
// Translate the path (center the shape)
|
||||||
bangElem.attr('transform', `translate(${-effectiveWidth / 2}, ${-effectiveHeight / 2})`);
|
bangElem.attr('transform', `translate(${-effectiveWidth / 2}, ${-effectiveHeight / 2})`);
|
||||||
|
|
||||||
if (node.icon) {
|
|
||||||
await insertMindmapIcon(shapeSvg, node, iconConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNodeBounds(node, bangElem);
|
updateNodeBounds(node, bangElem);
|
||||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||||
return intersect.rect(bounds, point);
|
return intersect.rect(bounds, point);
|
||||||
|
|||||||
@@ -14,25 +14,9 @@ export async function circle<T extends SVGGraphicsElement>(
|
|||||||
) {
|
) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
|
const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
|
||||||
parent,
|
|
||||||
node,
|
|
||||||
getNodeClasses(node)
|
|
||||||
);
|
|
||||||
|
|
||||||
const padding = options?.padding ?? halfPadding;
|
const padding = options?.padding ?? halfPadding;
|
||||||
|
|
||||||
const radius = bbox.width / 2 + padding;
|
const radius = bbox.width / 2 + padding;
|
||||||
|
|
||||||
node.width = radius * 2;
|
|
||||||
node.height = radius * 2;
|
|
||||||
|
|
||||||
const labelXOffset = -bbox.width / 2;
|
|
||||||
const labelYOffset = -bbox.height / 2;
|
|
||||||
if (node.icon) {
|
|
||||||
label.attr('transform', `translate(${labelXOffset}, ${labelYOffset})`);
|
|
||||||
}
|
|
||||||
let circleElem;
|
let circleElem;
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,6 @@ import type { Node } from '../../types.js';
|
|||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||||
import {
|
|
||||||
getMindmapIconConfig,
|
|
||||||
calculateMindmapDimensions,
|
|
||||||
insertMindmapIcon,
|
|
||||||
} from '../../../diagrams/mindmap/mindmapIconHelper.js';
|
|
||||||
|
|
||||||
export async function cloud<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
export async function cloud<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
@@ -22,26 +17,8 @@ export async function cloud<T extends SVGGraphicsElement>(parent: D3Selection<T>
|
|||||||
getNodeClasses(node)
|
getNodeClasses(node)
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseWidth = bbox.width + 2 * halfPadding;
|
const w = bbox.width + 2 * halfPadding;
|
||||||
const baseHeight = bbox.height + 2 * halfPadding;
|
const h = bbox.height + 2 * halfPadding;
|
||||||
|
|
||||||
const iconConfig = getMindmapIconConfig('cloud');
|
|
||||||
const dimensions = calculateMindmapDimensions(
|
|
||||||
node,
|
|
||||||
bbox,
|
|
||||||
baseWidth,
|
|
||||||
baseHeight,
|
|
||||||
halfPadding,
|
|
||||||
iconConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
const w = dimensions.width;
|
|
||||||
const h = dimensions.height;
|
|
||||||
|
|
||||||
node.width = w;
|
|
||||||
node.height = h;
|
|
||||||
|
|
||||||
label.attr('transform', `translate(${dimensions.labelOffset.x}, ${dimensions.labelOffset.y})`);
|
|
||||||
|
|
||||||
// Cloud radii
|
// Cloud radii
|
||||||
const r1 = 0.15 * w;
|
const r1 = 0.15 * w;
|
||||||
@@ -84,13 +61,11 @@ export async function cloud<T extends SVGGraphicsElement>(parent: D3Selection<T>
|
|||||||
.attr('d', path);
|
.attr('d', path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`);
|
||||||
|
|
||||||
// Center the shape
|
// Center the shape
|
||||||
cloudElem.attr('transform', `translate(${-w / 2}, ${-h / 2})`);
|
cloudElem.attr('transform', `translate(${-w / 2}, ${-h / 2})`);
|
||||||
|
|
||||||
if (node.icon) {
|
|
||||||
await insertMindmapIcon(shapeSvg, node, iconConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNodeBounds(node, cloudElem);
|
updateNodeBounds(node, cloudElem);
|
||||||
|
|
||||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||||
|
|||||||
@@ -3,11 +3,6 @@ import type { Node } from '../../types.js';
|
|||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import { styles2String } from './handDrawnShapeStyles.js';
|
import { styles2String } from './handDrawnShapeStyles.js';
|
||||||
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||||
import {
|
|
||||||
getMindmapIconConfig,
|
|
||||||
calculateMindmapDimensions,
|
|
||||||
insertMindmapIcon,
|
|
||||||
} from '../../../diagrams/mindmap/mindmapIconHelper.js';
|
|
||||||
|
|
||||||
export async function defaultMindmapNode<T extends SVGGraphicsElement>(
|
export async function defaultMindmapNode<T extends SVGGraphicsElement>(
|
||||||
parent: D3Selection<T>,
|
parent: D3Selection<T>,
|
||||||
@@ -22,38 +17,22 @@ export async function defaultMindmapNode<T extends SVGGraphicsElement>(
|
|||||||
getNodeClasses(node)
|
getNodeClasses(node)
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseWidth = bbox.width + 8 * halfPadding;
|
const w = bbox.width + 8 * halfPadding;
|
||||||
const baseHeight = bbox.height + 2 * halfPadding;
|
const h = bbox.height + 2 * halfPadding;
|
||||||
|
const rd = 5;
|
||||||
|
|
||||||
const iconConfig = getMindmapIconConfig('default');
|
const rectPath = `
|
||||||
const dimensions = calculateMindmapDimensions(
|
M${-w / 2} ${h / 2 - rd}
|
||||||
node,
|
v${-h + 2 * rd}
|
||||||
bbox,
|
q0,-${rd} ${rd},-${rd}
|
||||||
baseWidth,
|
h${w - 2 * rd}
|
||||||
baseHeight,
|
q${rd},0 ${rd},${rd}
|
||||||
halfPadding,
|
v${h - 2 * rd}
|
||||||
iconConfig
|
q0,${rd} -${rd},${rd}
|
||||||
);
|
h${-w + 2 * rd}
|
||||||
|
q-${rd},0 -${rd},-${rd}
|
||||||
const w = dimensions.width;
|
Z
|
||||||
const h = dimensions.height;
|
`;
|
||||||
|
|
||||||
node.width = w;
|
|
||||||
node.height = h;
|
|
||||||
|
|
||||||
label.attr('transform', `translate(${dimensions.labelOffset.x}, ${dimensions.labelOffset.y})`);
|
|
||||||
|
|
||||||
const RD = 5;
|
|
||||||
const rectPath = `M${-w / 2} ${h / 2 - RD}
|
|
||||||
v${-h + 2 * RD}
|
|
||||||
q0,-${RD} ${RD},-${RD}
|
|
||||||
h${w - 2 * RD}
|
|
||||||
q${RD},0 ${RD},${RD}
|
|
||||||
v${h - 2 * RD}
|
|
||||||
q0,${RD} -${RD},${RD}
|
|
||||||
h${-w + 2 * RD}
|
|
||||||
q-${RD},0 -${RD},-${RD}
|
|
||||||
Z`;
|
|
||||||
|
|
||||||
const bg = shapeSvg
|
const bg = shapeSvg
|
||||||
.append('path')
|
.append('path')
|
||||||
@@ -70,12 +49,9 @@ export async function defaultMindmapNode<T extends SVGGraphicsElement>(
|
|||||||
.attr('x2', w / 2)
|
.attr('x2', w / 2)
|
||||||
.attr('y2', h / 2);
|
.attr('y2', h / 2);
|
||||||
|
|
||||||
|
label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`);
|
||||||
shapeSvg.append(() => label.node());
|
shapeSvg.append(() => label.node());
|
||||||
|
|
||||||
if (node.icon) {
|
|
||||||
await insertMindmapIcon(shapeSvg, node, iconConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNodeBounds(node, bg);
|
updateNodeBounds(node, bg);
|
||||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||||
return intersect.rect(bounds, point);
|
return intersect.rect(bounds, point);
|
||||||
|
|||||||
@@ -15,21 +15,11 @@ export async function drawRect<T extends SVGGraphicsElement>(
|
|||||||
) {
|
) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
|
// console.log('IPI labelStyles:', labelStyles);
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
|
|
||||||
const totalWidth = Math.max(bbox.width + options.labelPaddingX * 2, node?.width || 0);
|
const totalWidth = Math.max(bbox.width + options.labelPaddingX * 2, node?.width || 0);
|
||||||
const totalHeight = Math.max(bbox.height + options.labelPaddingY * 2, node?.height || 0);
|
const totalHeight = Math.max(bbox.height + options.labelPaddingY * 2, node?.height || 0);
|
||||||
|
|
||||||
node.width = totalWidth;
|
|
||||||
node.height = totalHeight;
|
|
||||||
|
|
||||||
const labelXOffset = -bbox.width / 2;
|
|
||||||
|
|
||||||
const labelYOffset = -bbox.height / 2;
|
|
||||||
if (node.icon) {
|
|
||||||
label.attr('transform', `translate(${labelXOffset}, ${labelYOffset})`);
|
|
||||||
}
|
|
||||||
const x = -totalWidth / 2;
|
const x = -totalWidth / 2;
|
||||||
const y = -totalHeight / 2;
|
const y = -totalHeight / 2;
|
||||||
|
|
||||||
|
|||||||
@@ -26,22 +26,10 @@ export const createHexagonPathD = (
|
|||||||
export async function hexagon<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
export async function hexagon<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
|
||||||
|
|
||||||
const h = bbox.height + (node.padding ?? 0);
|
const h = bbox.height + (node.padding ?? 0);
|
||||||
const w = bbox.width + (node.padding ?? 0) * 2.5;
|
const w = bbox.width + (node.padding ?? 0) * 2.5;
|
||||||
|
|
||||||
node.width = w;
|
|
||||||
node.height = h;
|
|
||||||
|
|
||||||
const labelXOffset = -bbox.width / 2;
|
|
||||||
|
|
||||||
const labelYOffset = -bbox.height / 2;
|
|
||||||
|
|
||||||
if (node.icon) {
|
|
||||||
label.attr('transform', `translate(${labelXOffset}, ${labelYOffset})`);
|
|
||||||
}
|
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
@@ -86,6 +74,9 @@ export async function hexagon<T extends SVGGraphicsElement>(parent: D3Selection<
|
|||||||
polygon.selectChildren('path').attr('style', nodeStyles);
|
polygon.selectChildren('path').attr('style', nodeStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.width = w;
|
||||||
|
node.height = h;
|
||||||
|
|
||||||
updateNodeBounds(node, polygon);
|
updateNodeBounds(node, polygon);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
|
|||||||
@@ -1,57 +1,13 @@
|
|||||||
import type { Node } from '../../types.js';
|
import { circle } from './circle.js';
|
||||||
|
import type { Node, MindmapOptions } from '../../types.js';
|
||||||
import type { D3Selection } from '../../../types.js';
|
import type { D3Selection } from '../../../types.js';
|
||||||
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
|
||||||
import { styles2String } from './handDrawnShapeStyles.js';
|
|
||||||
import intersect from '../intersect/index.js';
|
|
||||||
import {
|
|
||||||
getMindmapIconConfig,
|
|
||||||
calculateMindmapDimensions,
|
|
||||||
insertMindmapIcon,
|
|
||||||
} from '../../../diagrams/mindmap/mindmapIconHelper.js';
|
|
||||||
|
|
||||||
export async function mindmapCircle<T extends SVGGraphicsElement>(
|
export async function mindmapCircle<T extends SVGGraphicsElement>(
|
||||||
parent: D3Selection<T>,
|
parent: D3Selection<T>,
|
||||||
node: Node
|
node: Node
|
||||||
) {
|
) {
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
const options = {
|
||||||
const halfPadding = (node.padding ?? 0) / 2;
|
padding: node.padding ?? 0,
|
||||||
|
} as MindmapOptions;
|
||||||
const iconConfig = getMindmapIconConfig('circle');
|
return circle(parent, node, options);
|
||||||
const baseRadius = bbox.width / 2 + halfPadding;
|
|
||||||
const baseDiameter = baseRadius * 2;
|
|
||||||
|
|
||||||
const dimensions = calculateMindmapDimensions(
|
|
||||||
node,
|
|
||||||
bbox,
|
|
||||||
baseDiameter,
|
|
||||||
baseDiameter,
|
|
||||||
halfPadding,
|
|
||||||
iconConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
const radius = dimensions.width / 2;
|
|
||||||
node.width = dimensions.width;
|
|
||||||
node.height = dimensions.height;
|
|
||||||
|
|
||||||
label.attr('transform', `translate(${dimensions.labelOffset.x}, ${dimensions.labelOffset.y})`);
|
|
||||||
|
|
||||||
const circle = shapeSvg
|
|
||||||
.insert('circle', ':first-child')
|
|
||||||
.attr('r', radius)
|
|
||||||
.attr('class', 'basic label-container')
|
|
||||||
.attr('style', styles2String(node).nodeStyles);
|
|
||||||
|
|
||||||
shapeSvg.append(() => label.node());
|
|
||||||
|
|
||||||
if (node.icon) {
|
|
||||||
await insertMindmapIcon(shapeSvg, node, iconConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNodeBounds(node, circle);
|
|
||||||
|
|
||||||
node.intersect = function (point) {
|
|
||||||
return intersect.circle(node, radius, point);
|
|
||||||
};
|
|
||||||
|
|
||||||
return shapeSvg;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } f
|
|||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.js';
|
import type { Node } from '../../types.js';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import type { D3Selection } from '../../../types.js';
|
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
|
import type { D3Selection } from '../../../types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates evenly spaced points along an elliptical arc connecting two points.
|
* Generates evenly spaced points along an elliptical arc connecting two points.
|
||||||
@@ -91,20 +91,13 @@ export async function roundedRect<T extends SVGGraphicsElement>(
|
|||||||
) {
|
) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
|
|
||||||
const labelPaddingX = node?.padding ?? 0;
|
const labelPaddingX = node?.padding ?? 0;
|
||||||
const labelPaddingY = node?.padding ?? 0;
|
const labelPaddingY = node?.padding ?? 0;
|
||||||
|
|
||||||
const w = (node?.width ? node?.width : bbox.width) + labelPaddingX * 2;
|
const w = (node?.width ? node?.width : bbox.width) + labelPaddingX * 2;
|
||||||
const h = (node?.height ? node?.height : bbox.height) + labelPaddingY * 2;
|
const h = (node?.height ? node?.height : bbox.height) + labelPaddingY * 2;
|
||||||
|
|
||||||
const labelXOffset = -bbox.width / 2;
|
|
||||||
|
|
||||||
if (node.icon) {
|
|
||||||
label.attr('transform', `translate(${labelXOffset}, ${-bbox.height / 2})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const radius = node.radius || 5;
|
const radius = node.radius || 5;
|
||||||
const taper = node.taper || 5; // Taper width for the rounded corners
|
const taper = node.taper || 5; // Taper width for the rounded corners
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ interface BaseNode {
|
|||||||
radius?: number;
|
radius?: number;
|
||||||
taper?: number;
|
taper?: number;
|
||||||
stroke?: string;
|
stroke?: string;
|
||||||
section?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user