mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-23 07:59:39 +02:00
Compare commits
38 Commits
feature/47
...
fix-markdo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
70c9e6e57a | ||
![]() |
18f51eb14e | ||
![]() |
2bb57bf7d2 | ||
![]() |
a6276daffd | ||
![]() |
1f8ba5591d | ||
![]() |
cd6f7a3019 | ||
![]() |
1f0cb96d16 | ||
![]() |
db4a837e9b | ||
![]() |
047b7fccc7 | ||
![]() |
a53c0d4305 | ||
![]() |
13f2040b37 | ||
![]() |
e351a7a488 | ||
![]() |
6fecb985e8 | ||
![]() |
69b338d8af | ||
![]() |
fe8597d0c8 | ||
![]() |
178ea182c3 | ||
![]() |
fa15ce8502 | ||
![]() |
6d0650918f | ||
![]() |
1a9d45abf0 | ||
![]() |
bbb93b263d | ||
![]() |
4240340a18 | ||
![]() |
ca10a259fa | ||
![]() |
0ed9c65572 | ||
![]() |
56cc12690f | ||
![]() |
3a23372af4 | ||
![]() |
cd6f7192bf | ||
![]() |
ebb77578e8 | ||
![]() |
4d76af679b | ||
![]() |
b7b05f4a55 | ||
![]() |
44a6434e59 | ||
![]() |
37269b47b5 | ||
![]() |
560abf4218 | ||
![]() |
0943edc114 | ||
![]() |
40402a2bd9 | ||
![]() |
dc51c027d6 | ||
![]() |
e6fb4a84da | ||
![]() |
32723b2de1 | ||
![]() |
18703782ee |
5
.changeset/brave-memes-flash.md
Normal file
5
.changeset/brave-memes-flash.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: Support edge animation in hand drawn look
|
5
.changeset/busy-mirrors-try.md
Normal file
5
.changeset/busy-mirrors-try.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: Resolved parsing error where direction TD was not recognized within subgraphs
|
5
.changeset/curly-apes-prove.md
Normal file
5
.changeset/curly-apes-prove.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: Improve participant parsing and prevent recursive loops on invalid syntax
|
5
.changeset/gold-ducks-sort.md
Normal file
5
.changeset/gold-ducks-sort.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: Proper separation between strings and markdown strings
|
@@ -6,6 +6,7 @@ interface CypressConfig {
|
|||||||
listUrl?: boolean;
|
listUrl?: boolean;
|
||||||
listId?: string;
|
listId?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
screenshot?: boolean;
|
||||||
}
|
}
|
||||||
type CypressMermaidConfig = MermaidConfig & CypressConfig;
|
type CypressMermaidConfig = MermaidConfig & CypressConfig;
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ export const renderGraph = (
|
|||||||
|
|
||||||
export const openURLAndVerifyRendering = (
|
export const openURLAndVerifyRendering = (
|
||||||
url: string,
|
url: string,
|
||||||
options: CypressMermaidConfig,
|
{ screenshot = true, ...options }: CypressMermaidConfig,
|
||||||
validation?: any
|
validation?: any
|
||||||
): void => {
|
): void => {
|
||||||
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
||||||
@@ -103,7 +104,9 @@ export const openURLAndVerifyRendering = (
|
|||||||
cy.get('svg').should(validation);
|
cy.get('svg').should(validation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (screenshot) {
|
||||||
verifyScreenshot(name);
|
verifyScreenshot(name);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const verifyScreenshot = (name: string): void => {
|
export const verifyScreenshot = (name: string): void => {
|
||||||
|
@@ -1029,4 +1029,19 @@ graph TD
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('FDH49: should add edge animation', () => {
|
||||||
|
renderGraph(
|
||||||
|
`
|
||||||
|
flowchart TD
|
||||||
|
A(["Start"]) L_A_B_0@--> B{"Decision"}
|
||||||
|
B --> C["Option A"] & D["Option B"]
|
||||||
|
style C stroke-width:4px,stroke-dasharray: 5
|
||||||
|
L_A_B_0@{ animation: slow }
|
||||||
|
L_B_D_0@{ animation: fast }`,
|
||||||
|
{ look: 'handDrawn', screenshot: false }
|
||||||
|
);
|
||||||
|
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
|
||||||
|
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1174,8 +1174,8 @@ end
|
|||||||
end
|
end
|
||||||
githost["Github, Gitlab, BitBucket, etc."]
|
githost["Github, Gitlab, BitBucket, etc."]
|
||||||
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
|
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
|
||||||
a["1."]
|
a["\`1.\`"]
|
||||||
b["- x"]
|
b["\`- x\`"]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
it('should render raw strings', () => {
|
it('should render raw strings', () => {
|
||||||
|
@@ -774,6 +774,21 @@ describe('Graph', () => {
|
|||||||
expect(svg).to.not.have.attr('style');
|
expect(svg).to.not.have.attr('style');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('40: should add edge animation', () => {
|
||||||
|
renderGraph(
|
||||||
|
`
|
||||||
|
flowchart TD
|
||||||
|
A(["Start"]) L_A_B_0@--> B{"Decision"}
|
||||||
|
B --> C["Option A"] & D["Option B"]
|
||||||
|
style C stroke-width:4px,stroke-dasharray: 5
|
||||||
|
L_A_B_0@{ animation: slow }
|
||||||
|
L_B_D_0@{ animation: fast }`,
|
||||||
|
{ screenshot: false }
|
||||||
|
);
|
||||||
|
// Verify animation classes are applied to both edges
|
||||||
|
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
|
||||||
|
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
|
||||||
|
});
|
||||||
it('58: handle styling with style expressions', () => {
|
it('58: handle styling with style expressions', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
@@ -973,4 +988,64 @@ graph TD
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('70: should render a subgraph with direction TD', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
flowchart LR
|
||||||
|
subgraph A
|
||||||
|
direction TD
|
||||||
|
a --> b
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
fontFamily: 'courier',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('#5824: should be able to render string and markdown labels', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
flowchart TB
|
||||||
|
mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"]
|
||||||
|
mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"]
|
||||||
|
subgraph subgraph1["\`How to fix **fix**\`"]
|
||||||
|
broken --> B["B"]
|
||||||
|
end
|
||||||
|
githost["Github, Gitlab, BitBucket, etc."]
|
||||||
|
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
|
||||||
|
a["1."]
|
||||||
|
b["- x"]
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
flowchart: { htmlLabels: true },
|
||||||
|
securityLevel: 'loose',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('69: should render subgraphs with adhoc list headings', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
graph TB
|
||||||
|
subgraph "1. first"
|
||||||
|
a1-->a2
|
||||||
|
end
|
||||||
|
subgraph 2. second
|
||||||
|
b1-->b2
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
{ fontFamily: 'courier' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('70: should render subgraphs with markdown headings', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
graph TB
|
||||||
|
subgraph "\`**strong**\`"
|
||||||
|
a1-->a2
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
{ fontFamily: 'courier' }
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: ParseOptions
|
# Interface: ParseOptions
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
|
Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mer
|
|||||||
|
|
||||||
> `optional` **suppressErrors**: `boolean`
|
> `optional` **suppressErrors**: `boolean`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93)
|
Defined in: [packages/mermaid/src/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L94)
|
||||||
|
|
||||||
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
|
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
|
||||||
The `parseError` function will not be called.
|
The `parseError` function will not be called.
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: ParseResult
|
# Interface: ParseResult
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L96)
|
Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L97)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mer
|
|||||||
|
|
||||||
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L104)
|
Defined in: [packages/mermaid/src/types.ts:105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L105)
|
||||||
|
|
||||||
The config passed as YAML frontmatter or directives
|
The config passed as YAML frontmatter or directives
|
||||||
|
|
||||||
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
|
|||||||
|
|
||||||
> **diagramType**: `string`
|
> **diagramType**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L100)
|
Defined in: [packages/mermaid/src/types.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L101)
|
||||||
|
|
||||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: RenderResult
|
# Interface: RenderResult
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L114)
|
Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L115)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/me
|
|||||||
|
|
||||||
> `optional` **bindFunctions**: (`element`) => `void`
|
> `optional` **bindFunctions**: (`element`) => `void`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L132)
|
Defined in: [packages/mermaid/src/types.ts:133](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L133)
|
||||||
|
|
||||||
Bind function to be called after the svg has been inserted into the DOM.
|
Bind function to be called after the svg has been inserted into the DOM.
|
||||||
This is necessary for adding event listeners to the elements in the svg.
|
This is necessary for adding event listeners to the elements in the svg.
|
||||||
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
|||||||
|
|
||||||
> **diagramType**: `string`
|
> **diagramType**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L122)
|
Defined in: [packages/mermaid/src/types.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L123)
|
||||||
|
|
||||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||||
|
|
||||||
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
|||||||
|
|
||||||
> **svg**: `string`
|
> **svg**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L118)
|
Defined in: [packages/mermaid/src/types.ts:119](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L119)
|
||||||
|
|
||||||
The svg code for the rendered graph.
|
The svg code for the rendered graph.
|
||||||
|
@@ -702,7 +702,6 @@ classDiagram
|
|||||||
It is possible to bind a click event to a node. The click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
It is possible to bind a click event to a node. The click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
||||||
|
|
||||||
You would define these actions on a separate line after all classes have been declared.
|
You would define these actions on a separate line after all classes have been declared.
|
||||||
If you have classes defined within a namespace, you can also add interaction definitions within the namespace definition, after the class(es) is defined
|
|
||||||
|
|
||||||
```
|
```
|
||||||
action className "reference" "tooltip"
|
action className "reference" "tooltip"
|
||||||
|
@@ -88,50 +88,6 @@ describe('given a basic class diagram, ', function () {
|
|||||||
expect(relations[0].title).toBe('generates');
|
expect(relations[0].title).toBe('generates');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle link statements within namespaces', function () {
|
|
||||||
spyOn(classDb, 'setLink');
|
|
||||||
const str = `classDiagram
|
|
||||||
namespace MyNamespace {
|
|
||||||
class UserService {
|
|
||||||
+createUser()
|
|
||||||
+deleteUser()
|
|
||||||
}
|
|
||||||
|
|
||||||
class PaymentService {
|
|
||||||
+processPayment()
|
|
||||||
+refund()
|
|
||||||
}
|
|
||||||
|
|
||||||
link UserService "https://example.com/user-service"
|
|
||||||
link PaymentService "https://example.com/payment-service" "Payment Service Documentation"
|
|
||||||
}`;
|
|
||||||
|
|
||||||
parser.parse(str);
|
|
||||||
|
|
||||||
// Verify setLink was called for both classes
|
|
||||||
expect(classDb.setLink).toHaveBeenCalledWith(
|
|
||||||
'UserService',
|
|
||||||
'https://example.com/user-service'
|
|
||||||
);
|
|
||||||
expect(classDb.setLink).toHaveBeenCalledWith(
|
|
||||||
'PaymentService',
|
|
||||||
'https://example.com/payment-service'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify the classes have the correct links and are in the namespace
|
|
||||||
const userService = classDb.getClass('UserService');
|
|
||||||
const paymentService = classDb.getClass('PaymentService');
|
|
||||||
|
|
||||||
expect(userService.parent).toBe('MyNamespace');
|
|
||||||
expect(userService.link).toBe('https://example.com/user-service');
|
|
||||||
expect(userService.cssClasses).toBe('default clickable');
|
|
||||||
|
|
||||||
expect(paymentService.parent).toBe('MyNamespace');
|
|
||||||
expect(paymentService.link).toBe('https://example.com/payment-service');
|
|
||||||
expect(paymentService.tooltip).toBe('Payment Service Documentation');
|
|
||||||
expect(paymentService.cssClasses).toBe('default clickable');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle accTitle and accDescr', function () {
|
it('should handle accTitle and accDescr', function () {
|
||||||
const str = `classDiagram
|
const str = `classDiagram
|
||||||
accTitle: My Title
|
accTitle: My Title
|
||||||
|
@@ -275,25 +275,14 @@ statement
|
|||||||
;
|
;
|
||||||
|
|
||||||
namespaceStatement
|
namespaceStatement
|
||||||
: namespaceIdentifier STRUCT_START namespaceBodyStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3); }
|
: namespaceIdentifier STRUCT_START classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3); }
|
||||||
| namespaceIdentifier STRUCT_START NEWLINE namespaceBodyStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4); }
|
| namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4); }
|
||||||
;
|
;
|
||||||
|
|
||||||
namespaceIdentifier
|
namespaceIdentifier
|
||||||
: NAMESPACE namespaceName { $$=$2; yy.addNamespace($2); }
|
: NAMESPACE namespaceName { $$=$2; yy.addNamespace($2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
namespaceBodyStatements
|
|
||||||
: namespaceBodyStatement { $$=[$1].filter(s => s !== null); }
|
|
||||||
| namespaceBodyStatement NEWLINE { $$=[$1].filter(s => s !== null); }
|
|
||||||
| namespaceBodyStatement NEWLINE namespaceBodyStatements { var filtered = [$1].filter(s => s !== null); $3.unshift(...filtered); $$=$3; }
|
|
||||||
;
|
|
||||||
|
|
||||||
namespaceBodyStatement
|
|
||||||
: classStatement { $$=$1; }
|
|
||||||
| clickStatement { $$=null; /* clickStatements don't return class names, but are processed for side effects */ }
|
|
||||||
;
|
|
||||||
|
|
||||||
classStatements
|
classStatements
|
||||||
: classStatement {$$=[$1]}
|
: classStatement {$$=[$1]}
|
||||||
| classStatement NEWLINE {$$=[$1]}
|
| classStatement NEWLINE {$$=[$1]}
|
||||||
|
@@ -85,6 +85,17 @@ export class FlowDB implements DiagramDB {
|
|||||||
return common.sanitizeText(txt, this.config);
|
return common.sanitizeText(txt, this.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sanitizeNodeLabelType(labelType?: string) {
|
||||||
|
switch (labelType) {
|
||||||
|
case 'markdown':
|
||||||
|
case 'string':
|
||||||
|
case 'text':
|
||||||
|
return labelType;
|
||||||
|
default:
|
||||||
|
return 'markdown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to lookup domId from id in the graph definition.
|
* Function to lookup domId from id in the graph definition.
|
||||||
*
|
*
|
||||||
@@ -208,6 +219,7 @@ export class FlowDB implements DiagramDB {
|
|||||||
|
|
||||||
if (doc?.label) {
|
if (doc?.label) {
|
||||||
vertex.text = doc?.label;
|
vertex.text = doc?.label;
|
||||||
|
vertex.labelType = this.sanitizeNodeLabelType(doc?.labelType);
|
||||||
}
|
}
|
||||||
if (doc?.icon) {
|
if (doc?.icon) {
|
||||||
vertex.icon = doc?.icon;
|
vertex.icon = doc?.icon;
|
||||||
@@ -267,7 +279,7 @@ export class FlowDB implements DiagramDB {
|
|||||||
if (edge.text.startsWith('"') && edge.text.endsWith('"')) {
|
if (edge.text.startsWith('"') && edge.text.endsWith('"')) {
|
||||||
edge.text = edge.text.substring(1, edge.text.length - 1);
|
edge.text = edge.text.substring(1, edge.text.length - 1);
|
||||||
}
|
}
|
||||||
edge.labelType = linkTextObj.type;
|
edge.labelType = this.sanitizeNodeLabelType(linkTextObj.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type !== undefined) {
|
if (type !== undefined) {
|
||||||
@@ -702,7 +714,7 @@ You have to call mermaid.initialize.`
|
|||||||
title: title.trim(),
|
title: title.trim(),
|
||||||
classes: [],
|
classes: [],
|
||||||
dir,
|
dir,
|
||||||
labelType: _title.type,
|
labelType: this.sanitizeNodeLabelType(_title?.type),
|
||||||
};
|
};
|
||||||
|
|
||||||
log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
|
log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
|
||||||
@@ -1012,6 +1024,7 @@ You have to call mermaid.initialize.`
|
|||||||
const baseNode = {
|
const baseNode = {
|
||||||
id: vertex.id,
|
id: vertex.id,
|
||||||
label: vertex.text,
|
label: vertex.text,
|
||||||
|
labelType: vertex.labelType,
|
||||||
labelStyle: '',
|
labelStyle: '',
|
||||||
parentId,
|
parentId,
|
||||||
padding: config.flowchart?.padding || 8,
|
padding: config.flowchart?.padding || 8,
|
||||||
@@ -1088,6 +1101,7 @@ You have to call mermaid.initialize.`
|
|||||||
id: subGraph.id,
|
id: subGraph.id,
|
||||||
label: subGraph.title,
|
label: subGraph.title,
|
||||||
labelStyle: '',
|
labelStyle: '',
|
||||||
|
labelType: subGraph.labelType,
|
||||||
parentId: parentDB.get(subGraph.id),
|
parentId: parentDB.get(subGraph.id),
|
||||||
padding: 8,
|
padding: 8,
|
||||||
cssCompiledStyles: this.getCompiledStyles(subGraph.classes),
|
cssCompiledStyles: this.getCompiledStyles(subGraph.classes),
|
||||||
@@ -1119,6 +1133,7 @@ You have to call mermaid.initialize.`
|
|||||||
end: rawEdge.end,
|
end: rawEdge.end,
|
||||||
type: rawEdge.type ?? 'normal',
|
type: rawEdge.type ?? 'normal',
|
||||||
label: rawEdge.text,
|
label: rawEdge.text,
|
||||||
|
labelType: rawEdge.labelType,
|
||||||
labelpos: 'c',
|
labelpos: 'c',
|
||||||
thickness: rawEdge.stroke,
|
thickness: rawEdge.stroke,
|
||||||
minlen: rawEdge.length,
|
minlen: rawEdge.length,
|
||||||
|
@@ -140,6 +140,7 @@ that id.
|
|||||||
.*direction\s+BT[^\n]* return 'direction_bt';
|
.*direction\s+BT[^\n]* return 'direction_bt';
|
||||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||||
.*direction\s+LR[^\n]* return 'direction_lr';
|
.*direction\s+LR[^\n]* return 'direction_lr';
|
||||||
|
.*direction\s+TD[^\n]* return 'direction_td';
|
||||||
|
|
||||||
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
|
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
|
||||||
[0-9]+ return 'NUM';
|
[0-9]+ return 'NUM';
|
||||||
@@ -626,6 +627,8 @@ direction
|
|||||||
{ $$={stmt:'dir', value:'RL'};}
|
{ $$={stmt:'dir', value:'RL'};}
|
||||||
| direction_lr
|
| direction_lr
|
||||||
{ $$={stmt:'dir', value:'LR'};}
|
{ $$={stmt:'dir', value:'LR'};}
|
||||||
|
| direction_td
|
||||||
|
{ $$={stmt:'dir', value:'TD'};}
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@@ -309,4 +309,21 @@ describe('when parsing subgraphs', function () {
|
|||||||
expect(subgraphA.nodes).toContain('a');
|
expect(subgraphA.nodes).toContain('a');
|
||||||
expect(subgraphA.nodes).not.toContain('c');
|
expect(subgraphA.nodes).not.toContain('c');
|
||||||
});
|
});
|
||||||
|
it('should correctly parse direction TD inside a subgraph', function () {
|
||||||
|
const res = flow.parser.parse(`
|
||||||
|
graph LR
|
||||||
|
subgraph WithTD
|
||||||
|
direction TD
|
||||||
|
A1 --> A2
|
||||||
|
end
|
||||||
|
`);
|
||||||
|
|
||||||
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
|
expect(subgraphs.length).toBe(1);
|
||||||
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
|
expect(subgraph.dir).toBe('TD');
|
||||||
|
expect(subgraph.nodes).toContain('A1');
|
||||||
|
expect(subgraph.nodes).toContain('A2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -29,7 +29,7 @@ export interface FlowVertex {
|
|||||||
domId: string;
|
domId: string;
|
||||||
haveCallback?: boolean;
|
haveCallback?: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
labelType: 'text';
|
labelType: 'markdown' | 'string' | 'text';
|
||||||
link?: string;
|
link?: string;
|
||||||
linkTarget?: string;
|
linkTarget?: string;
|
||||||
props?: any;
|
props?: any;
|
||||||
@@ -62,7 +62,7 @@ export interface FlowEdge {
|
|||||||
style?: string[];
|
style?: string[];
|
||||||
length?: number;
|
length?: number;
|
||||||
text: string;
|
text: string;
|
||||||
labelType: 'text';
|
labelType: 'markdown' | 'string' | 'text';
|
||||||
classes: string[];
|
classes: string[];
|
||||||
id?: string;
|
id?: string;
|
||||||
animation?: 'fast' | 'slow';
|
animation?: 'fast' | 'slow';
|
||||||
|
@@ -62,6 +62,7 @@ const getData = function () {
|
|||||||
const node = {
|
const node = {
|
||||||
id: section.id,
|
id: section.id,
|
||||||
label: sanitizeText(section.label ?? '', conf),
|
label: sanitizeText(section.label ?? '', conf),
|
||||||
|
labelType: 'markdown',
|
||||||
isGroup: true,
|
isGroup: true,
|
||||||
ticket: section.ticket,
|
ticket: section.ticket,
|
||||||
shape: 'kanbanSection',
|
shape: 'kanbanSection',
|
||||||
@@ -76,6 +77,7 @@ const getData = function () {
|
|||||||
id: item.id,
|
id: item.id,
|
||||||
parentId: section.id,
|
parentId: section.id,
|
||||||
label: sanitizeText(item.label ?? '', conf),
|
label: sanitizeText(item.label ?? '', conf),
|
||||||
|
labelType: 'markdown',
|
||||||
isGroup: false,
|
isGroup: false,
|
||||||
ticket: item?.ticket,
|
ticket: item?.ticket,
|
||||||
priority: item?.priority,
|
priority: item?.priority,
|
||||||
|
@@ -260,6 +260,7 @@ export class MindmapDB {
|
|||||||
id: node.id.toString(),
|
id: node.id.toString(),
|
||||||
domId: 'node_' + node.id.toString(),
|
domId: 'node_' + node.id.toString(),
|
||||||
label: node.descr,
|
label: node.descr,
|
||||||
|
labelType: 'markdown',
|
||||||
isGroup: false,
|
isGroup: false,
|
||||||
shape: getShapeFromType(node.type),
|
shape: getShapeFromType(node.type),
|
||||||
width: node.width,
|
width: node.width,
|
||||||
|
@@ -16,6 +16,7 @@ export interface MindmapNode {
|
|||||||
x?: number;
|
x?: number;
|
||||||
y?: number;
|
y?: number;
|
||||||
isRoot?: boolean;
|
isRoot?: boolean;
|
||||||
|
labelType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FilledMindMapNode = RequiredDeep<MindmapNode>;
|
export type FilledMindMapNode = RequiredDeep<MindmapNode>;
|
||||||
|
@@ -32,13 +32,14 @@
|
|||||||
<CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; }
|
<CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; }
|
||||||
<CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; }
|
<CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; }
|
||||||
<ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; }
|
<ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; }
|
||||||
<ID>[^\<->\->:\n,;@]+?([\-]*[^\<->\->:\n,;@]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
<ID>[^<>:\n,;@\s]+(?=\s+as\s) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
||||||
|
<ID>[^<>:\n,;@]+(?=\s*[\n;#]|$) { yytext = yytext.trim(); this.popState(); return 'ACTOR'; }
|
||||||
|
<ID>[^<>:\n,;@]*\<[^\n]* { this.popState(); return 'INVALID'; }
|
||||||
"box" { this.begin('LINE'); return 'box'; }
|
"box" { this.begin('LINE'); return 'box'; }
|
||||||
"participant" { this.begin('ID'); return 'participant'; }
|
"participant" { this.begin('ID'); return 'participant'; }
|
||||||
"actor" { this.begin('ID'); return 'participant_actor'; }
|
"actor" { this.begin('ID'); return 'participant_actor'; }
|
||||||
"create" return 'create';
|
"create" return 'create';
|
||||||
"destroy" { this.begin('ID'); return 'destroy'; }
|
"destroy" { this.begin('ID'); return 'destroy'; }
|
||||||
<ID>[^<\->\->:\n,;]+?([\-]*[^<\->\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
|
||||||
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
|
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
|
||||||
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
|
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
|
||||||
"loop" { this.begin('LINE'); return 'loop'; }
|
"loop" { this.begin('LINE'); return 'loop'; }
|
||||||
@@ -145,6 +146,7 @@ line
|
|||||||
: SPACE statement { $$ = $2 }
|
: SPACE statement { $$ = $2 }
|
||||||
| statement { $$ = $1 }
|
| statement { $$ = $1 }
|
||||||
| NEWLINE { $$=[]; }
|
| NEWLINE { $$=[]; }
|
||||||
|
| INVALID { $$=[]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
box_section
|
box_section
|
||||||
|
@@ -2609,5 +2609,17 @@ Bob->>Alice:Got it!
|
|||||||
expect(actors.get('E').type).toBe('entity');
|
expect(actors.get('E').type).toBe('entity');
|
||||||
expect(actors.get('E').description).toBe('E');
|
expect(actors.get('E').description).toBe('E');
|
||||||
});
|
});
|
||||||
|
it('should handle fail parsing when alias token causes conflicts in participant definition', async () => {
|
||||||
|
let error = false;
|
||||||
|
try {
|
||||||
|
await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant SAS MyServiceWithMoreThan20Chars <br> service decription
|
||||||
|
`);
|
||||||
|
} catch (e) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
expect(error).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -452,7 +452,6 @@ classDiagram
|
|||||||
It is possible to bind a click event to a node. The click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
It is possible to bind a click event to a node. The click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
||||||
|
|
||||||
You would define these actions on a separate line after all classes have been declared.
|
You would define these actions on a separate line after all classes have been declared.
|
||||||
If you have classes defined within a namespace, you can also add interaction definitions within the namespace definition, after the class(es) is defined
|
|
||||||
|
|
||||||
```
|
```
|
||||||
action className "reference" "tooltip"
|
action className "reference" "tooltip"
|
||||||
|
@@ -30,11 +30,17 @@ const rect = async (parent, node) => {
|
|||||||
// Create the label and insert it after the rect
|
// Create the label and insert it after the rect
|
||||||
const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label ');
|
const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label ');
|
||||||
|
|
||||||
const text = await createText(labelEl, node.label, {
|
let text;
|
||||||
|
if (node.labelType === 'markdown') {
|
||||||
|
text = await createText(labelEl, node.label, {
|
||||||
style: node.labelStyle,
|
style: node.labelStyle,
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
isNode: true,
|
isNode: true,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const labelElement = await createLabel(node.label, node.labelStyle, false, true);
|
||||||
|
text = labelEl.node()?.appendChild(labelElement);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
|
@@ -29,7 +29,7 @@ import {
|
|||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import createLabel from './createLabel.js';
|
import createLabel from './createLabel.js';
|
||||||
import { addEdgeMarkers } from './edgeMarker.ts';
|
import { addEdgeMarkers } from './edgeMarker.ts';
|
||||||
import { isLabelStyle, styles2String } from './shapes/handDrawnShapeStyles.js';
|
import { isLabelStyle } from './shapes/handDrawnShapeStyles.js';
|
||||||
|
|
||||||
export const edgeLabels = new Map();
|
export const edgeLabels = new Map();
|
||||||
export const terminalLabels = new Map();
|
export const terminalLabels = new Map();
|
||||||
@@ -47,14 +47,16 @@ export const getLabelStyles = (styleArray) => {
|
|||||||
export const insertEdgeLabel = async (elem, edge) => {
|
export const insertEdgeLabel = async (elem, edge) => {
|
||||||
let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
||||||
|
|
||||||
const { labelStyles } = styles2String(edge);
|
const labelElement =
|
||||||
edge.labelStyle = labelStyles;
|
edge.labelType === 'markdown'
|
||||||
const labelElement = await createText(elem, edge.label, {
|
? await createText(elem, edge.label, {
|
||||||
style: edge.labelStyle,
|
style: getLabelStyles(edge.labelStyle),
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
addSvgBackground: true,
|
addSvgBackground: true,
|
||||||
isNode: false,
|
isNode: false,
|
||||||
});
|
})
|
||||||
|
: await createLabel(edge.label, getLabelStyles(edge.labelStyle), undefined, false);
|
||||||
|
|
||||||
log.info('abc82', edge, edge.labelType);
|
log.info('abc82', edge, edge.labelType);
|
||||||
|
|
||||||
// Create outer g, edgeLabel, this will be positioned after graph layout
|
// Create outer g, edgeLabel, this will be positioned after graph layout
|
||||||
@@ -605,6 +607,14 @@ export const insertEdge = function (
|
|||||||
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
||||||
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
|
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
|
||||||
|
|
||||||
|
let animationClass = '';
|
||||||
|
if (edge.animate) {
|
||||||
|
animationClass = 'edge-animation-fast';
|
||||||
|
}
|
||||||
|
if (edge.animation) {
|
||||||
|
animationClass = 'edge-animation-' + edge.animation;
|
||||||
|
}
|
||||||
|
|
||||||
let animatedEdge = false;
|
let animatedEdge = false;
|
||||||
if (edge.look === 'handDrawn') {
|
if (edge.look === 'handDrawn') {
|
||||||
const rc = rough.svg(elem);
|
const rc = rough.svg(elem);
|
||||||
@@ -620,7 +630,13 @@ export const insertEdge = function (
|
|||||||
svgPath = select(svgPathNode)
|
svgPath = select(svgPathNode)
|
||||||
.select('path')
|
.select('path')
|
||||||
.attr('id', edge.id)
|
.attr('id', edge.id)
|
||||||
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
|
.attr(
|
||||||
|
'class',
|
||||||
|
' ' +
|
||||||
|
strokeClasses +
|
||||||
|
(edge.classes ? ' ' + edge.classes : '') +
|
||||||
|
(animationClass ? ' ' + animationClass : '')
|
||||||
|
)
|
||||||
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
||||||
let d = svgPath.attr('d');
|
let d = svgPath.attr('d');
|
||||||
svgPath.attr('d', d);
|
svgPath.attr('d', d);
|
||||||
@@ -628,13 +644,6 @@ export const insertEdge = function (
|
|||||||
} else {
|
} else {
|
||||||
const stylesFromClasses = edgeClassStyles.join(';');
|
const stylesFromClasses = edgeClassStyles.join(';');
|
||||||
const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : '';
|
const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : '';
|
||||||
let animationClass = '';
|
|
||||||
if (edge.animate) {
|
|
||||||
animationClass = ' edge-animation-fast';
|
|
||||||
}
|
|
||||||
if (edge.animation) {
|
|
||||||
animationClass = ' edge-animation-' + edge.animation;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathStyle =
|
const pathStyle =
|
||||||
(stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) +
|
(stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) +
|
||||||
@@ -646,7 +655,10 @@ export const insertEdge = function (
|
|||||||
.attr('id', edge.id)
|
.attr('id', edge.id)
|
||||||
.attr(
|
.attr(
|
||||||
'class',
|
'class',
|
||||||
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '')
|
' ' +
|
||||||
|
strokeClasses +
|
||||||
|
(edge.classes ? ' ' + edge.classes : '') +
|
||||||
|
(animationClass ? ' ' + animationClass : '')
|
||||||
)
|
)
|
||||||
.attr('style', pathStyle);
|
.attr('style', pathStyle);
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import createLabel from '../createLabel.js';
|
||||||
import { createText } from '../../createText.js';
|
import { createText } from '../../createText.js';
|
||||||
import type { Node } from '../../types.js';
|
import type { Node } from '../../types.js';
|
||||||
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||||
@@ -13,7 +14,7 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
|||||||
_classes?: string
|
_classes?: string
|
||||||
) => {
|
) => {
|
||||||
let cssClasses;
|
let cssClasses;
|
||||||
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.htmlLabels);
|
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels);
|
||||||
if (!_classes) {
|
if (!_classes) {
|
||||||
cssClasses = 'node default';
|
cssClasses = 'node default';
|
||||||
} else {
|
} else {
|
||||||
@@ -40,7 +41,9 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
|||||||
label = typeof node.label === 'string' ? node.label : node.label[0];
|
label = typeof node.label === 'string' ? node.label : node.label[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
let text;
|
||||||
|
if (node.labelType === 'markdown') {
|
||||||
|
text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
width: node.width || getConfig().flowchart?.wrappingWidth,
|
width: node.width || getConfig().flowchart?.wrappingWidth,
|
||||||
// @ts-expect-error -- This is currently not used. Should this be `classes` instead?
|
// @ts-expect-error -- This is currently not used. Should this be `classes` instead?
|
||||||
@@ -48,6 +51,16 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
|||||||
style: node.labelStyle,
|
style: node.labelStyle,
|
||||||
addSvgBackground: !!node.icon || !!node.img,
|
addSvgBackground: !!node.icon || !!node.img,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const labelElement = await createLabel(
|
||||||
|
sanitizeText(decodeEntities(label), getConfig()),
|
||||||
|
node.labelStyle,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
text = labelEl.node()?.appendChild(labelElement);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
const halfPadding = (node?.padding ?? 0) / 2;
|
const halfPadding = (node?.padding ?? 0) / 2;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
export interface NodeMetaData {
|
export interface NodeMetaData {
|
||||||
shape?: string;
|
shape?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
|
labelType?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
form?: string;
|
form?: string;
|
||||||
pos?: 't' | 'b';
|
pos?: 't' | 'b';
|
||||||
|
Reference in New Issue
Block a user