Merge branch 'develop' into fix-node16-module-resolution

This commit is contained in:
Remco Haszing
2023-04-01 12:05:19 +02:00
38 changed files with 490 additions and 143 deletions

1
.npmrc
View File

@@ -1,3 +1,2 @@
auto-install-peers=true auto-install-peers=true
strict-peer-dependencies=false strict-peer-dependencies=false
use-inline-specifiers-lockfile-format=true

View File

@@ -10,7 +10,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render an ER diagram with a recursive relationship', () => { it('should render an ER diagram with a recursive relationship', () => {
@@ -23,7 +22,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render an ER diagram with multiple relationships between the same two entities', () => { it('should render an ER diagram with multiple relationships between the same two entities', () => {
@@ -35,7 +33,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render a cyclical ER diagram', () => { it('should render a cyclical ER diagram', () => {
@@ -48,7 +45,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render a not-so-simple ER diagram', () => { it('should render a not-so-simple ER diagram', () => {
@@ -66,7 +62,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render multiple ER diagrams', () => { it('should render multiple ER diagrams', () => {
@@ -85,7 +80,6 @@ describe('Entity Relationship Diagram', () => {
], ],
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render an ER diagram with blank or empty labels', () => { it('should render an ER diagram with blank or empty labels', () => {
@@ -98,7 +92,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render an ER diagrams when useMaxWidth is true (default)', () => { it('should render an ER diagrams when useMaxWidth is true (default)', () => {
@@ -151,7 +144,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ er: { useMaxWidth: false } } { er: { useMaxWidth: false } }
); );
cy.get('svg');
}); });
it('should render entities with and without attributes', () => { it('should render entities with and without attributes', () => {
@@ -164,7 +156,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render entities with generic and array attributes', () => { it('should render entities with generic and array attributes', () => {
@@ -179,7 +170,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render entities with length in attributes type', () => { it('should render entities with length in attributes type', () => {
@@ -193,7 +183,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render entities and attributes with big and small entity names', () => { it('should render entities and attributes with big and small entity names', () => {
@@ -209,7 +198,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render entities with keys', () => { it('should render entities with keys', () => {
@@ -228,7 +216,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render entities with comments', () => { it('should render entities with comments', () => {
@@ -247,7 +234,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render entities with keys and comments', () => { it('should render entities with keys and comments', () => {
@@ -267,7 +253,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('should render entities with aliases', () => { it('should render entities with aliases', () => {
@@ -285,7 +270,6 @@ describe('Entity Relationship Diagram', () => {
`, `,
{ logLevel: 1 } { logLevel: 1 }
); );
cy.get('svg');
}); });
it('1433: should render a simple ER diagram with a title', () => { it('1433: should render a simple ER diagram with a title', () => {

View File

@@ -0,0 +1,45 @@
import { imgSnapshotTest } from '../../helpers/util';
describe('Error Diagrams', () => {
beforeEach(() => {
cy.on('uncaught:exception', (err) => {
expect(err.message).to.include('Parse error');
// return false to prevent the error from
// failing this test
return false;
});
});
it('should render a simple ER diagram', () => {
imgSnapshotTest(
`
error
`,
{ logLevel: 1 }
);
});
it('should render error diagram for actual errors', () => {
imgSnapshotTest(
`
flowchart TD
A[Christmas] --|Get money| B(Go shopping)
`,
{ logLevel: 1 }
);
});
it('should render error for wrong ER diagram', () => {
imgSnapshotTest(
`
erDiagram
ATLAS-ORGANIZATION ||--|{ ATLAS-PROJECTS : "has many"
ATLAS-PROJECTS ||--|{ MONGODB-CLUSTERS : "has many"
ATLAS-PROJECTS ||--|{ ATLAS-TEAMS : "has many"
MONGODB-CLUSTERS ||..|{
ATLAS-TEAMS ||..|{
`,
{ logLevel: 1 }
);
});
});

View File

@@ -133,6 +133,24 @@ describe('Gantt diagram', () => {
); );
}); });
it('should default to showing today marker', () => {
// This test only works if the environment thinks today is 1010-10-10
imgSnapshotTest(
`
gantt
title Show today marker (vertical line should be visible)
dateFormat YYYY-MM-DD
axisFormat %d
%% Should default to being on
%% todayMarker on
section Section1
Yesterday: 1010-10-09, 1d
Today: 1010-10-10, 1d
`,
{}
);
});
it('should hide today marker', () => { it('should hide today marker', () => {
imgSnapshotTest( imgSnapshotTest(
` `
@@ -142,7 +160,8 @@ describe('Gantt diagram', () => {
axisFormat %d axisFormat %d
todayMarker off todayMarker off
section Section1 section Section1
Today: 1, -1h Yesterday: 1010-10-09, 1d
Today: 1010-10-10, 1d
`, `,
{} {}
); );
@@ -157,7 +176,8 @@ describe('Gantt diagram', () => {
axisFormat %d axisFormat %d
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5 todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
section Section1 section Section1
Today: 1, -1h Yesterday: 1010-10-09, 1d
Today: 1010-10-10, 1d
`, `,
{} {}
); );
@@ -435,4 +455,39 @@ describe('Gantt diagram', () => {
{ gantt: { topAxis: true } } { gantt: { topAxis: true } }
); );
}); });
it('should render when compact is true', () => {
imgSnapshotTest(
`
---
displayMode: compact
---
gantt
title GANTT compact
dateFormat HH:mm:ss
axisFormat %Hh%M
section DB Clean
Clean: 12:00:00, 10m
Clean: 12:30:00, 12m
Clean: 13:00:00, 8m
Clean: 13:30:00, 9m
Clean: 14:00:00, 13m
Clean: 14:30:00, 10m
Clean: 15:00:00, 11m
section Sessions
A: 12:00:00, 63m
B: 12:30:00, 12m
C: 13:05:00, 12m
D: 13:06:00, 33m
E: 13:15:00, 55m
F: 13:20:00, 12m
G: 13:32:00, 18m
H: 13:50:00, 20m
I: 14:10:00, 10m
`,
{}
);
});
}); });

View File

@@ -47,7 +47,6 @@ const contentLoaded = async function () {
await mermaid2.registerExternalDiagrams([externalExample]); await mermaid2.registerExternalDiagrams([externalExample]);
mermaid2.initialize(graphObj.mermaid); mermaid2.initialize(graphObj.mermaid);
await mermaid2.run(); await mermaid2.run();
markRendered();
} }
}; };
@@ -123,7 +122,6 @@ const contentLoadedApi = async function () {
bindFunctions(div); bindFunctions(div);
} }
} }
markRendered();
}; };
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
@@ -135,10 +133,10 @@ if (typeof document !== 'undefined') {
function () { function () {
if (this.location.href.match('xss.html')) { if (this.location.href.match('xss.html')) {
this.console.log('Using api'); this.console.log('Using api');
void contentLoadedApi(); void contentLoadedApi().finally(markRendered);
} else { } else {
this.console.log('Not using api'); this.console.log('Not using api');
void contentLoaded(); void contentLoaded().finally(markRendered);
} }
}, },
false false

38
demos/error.html Normal file
View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Error | Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
</head>
<body>
<pre class="mermaid">
erDiagram
ATLAS-ORGANIZATION ||--|{ ATLAS-PROJECTS : "has many"
ATLAS-PROJECTS ||--|{ MONGODB-CLUSTERS : "has many"
ATLAS-PROJECTS ||--|{ ATLAS-TEAMS : "has many"
</pre>
<pre class="mermaid">
erDiagram
ATLAS-ORGANIZATION ||--|{ ATLAS-PROJECTS : "has many"
ATLAS-PROJECTS ||--|{ MONGODB-CLUSTERS : "has many"
ATLAS-PROJECTS ||--|{ ATLAS-TEAMS : "has many"
MONGODB-CLUSTERS ||..|{
ATLAS-TEAMS ||..|{
</pre>
<hr />
<pre class="mermaid">
flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
</pre>
<pre class="mermaid">
flowchart TD
A[Christmas] --|Get money| B(Go shopping)
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
</script>
</body>
</html>

View File

@@ -78,7 +78,7 @@
axisFormat %d/%m axisFormat %d/%m
todayMarker off todayMarker off
section Section1 section Section1
Today: 1, -01:00, 5min Today: 1, 08-08-09-01:00, 5min
</pre> </pre>
<hr /> <hr />
@@ -89,7 +89,7 @@
axisFormat %d/%m axisFormat %d/%m
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5 todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
section Section1 section Section1
Today: 1, -01:00, 5min Today: 1, 08-08-09-01:00, 5min
</pre> </pre>
<hr /> <hr />
@@ -166,6 +166,37 @@
</pre> </pre>
<hr /> <hr />
<pre class="mermaid">
---
displayMode: compact
---
gantt
title GANTT compact
dateFormat HH:mm:ss
axisFormat %Hh%M
section DB Clean
Clean: 12:00:00, 10m
Clean: 12:30:00, 12m
Clean: 13:00:00, 8m
Clean: 13:30:00, 9m
Clean: 14:00:00, 13m
Clean: 14:30:00, 10m
Clean: 15:00:00, 11m
section Sessions
A: 12:00:00, 63m
B: 12:30:00, 12m
C: 13:05:00, 12m
D: 13:06:00, 33m
E: 13:15:00, 55m
F: 13:20:00, 12m
G: 13:32:00, 18m
H: 13:50:00, 20m
I: 14:10:00, 10m
</pre>
<hr />
<script> <script>
function ganttTestClick(a, b, c) { function ganttTestClick(a, b, c) {
console.log('a:', a); console.log('a:', a);

View File

@@ -14,7 +14,7 @@
#### Defined in #### Defined in
[defaultConfig.ts:2093](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2093) [defaultConfig.ts:2103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2103)
--- ---

View File

@@ -88,6 +88,7 @@ const config = {
numberSectionStyles: 4, numberSectionStyles: 4,
axisFormat: '%Y-%m-%d', axisFormat: '%Y-%m-%d',
topAxis: false, topAxis: false,
displayMode: '',
}, },
}; };
mermaid.initialize(config); mermaid.initialize(config);
@@ -95,7 +96,7 @@ mermaid.initialize(config);
#### Defined in #### Defined in
[mermaidAPI.ts:659](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L659) [mermaidAPI.ts:660](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L660)
## Functions ## Functions

View File

@@ -183,8 +183,6 @@ flowchart LR
### A hexagon node ### A hexagon node
Code:
```mermaid-example ```mermaid-example
flowchart LR flowchart LR
id1{{This is the text in the box}} id1{{This is the text in the box}}

View File

@@ -257,9 +257,41 @@ The pattern is:
More info in: <https://github.com/d3/d3-time#interval_every> More info in: <https://github.com/d3/d3-time#interval_every>
## Output in compact mode
The compact mode allows you to display multiple tasks in the same row. Compact mode can be enabled for a gantt chart by setting the display mode of the graph via preceeding YAML settings.
```mermaid-example
---
displayMode: compact
---
gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :a2, 2014-01-20, 25d
Another one :a3, 2014-02-10, 20d
```
```mermaid
---
displayMode: compact
---
gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :a2, 2014-01-20, 25d
Another one :a3, 2014-02-10, 20d
```
## Comments ## Comments
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax.
```mermaid-example ```mermaid-example
gantt gantt

View File

@@ -8,7 +8,7 @@
> Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part. > Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
"A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia "A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia
### An example of a timeline. ### An example of a timeline.
@@ -213,7 +213,7 @@ However, if there is no section defined, then we have two possibilities:
``` ```
Note that this is no, section defined, and each time period and its corresponding events will have its own color scheme. Note that there are no sections defined, and each time period and its corresponding events will have its own color scheme.
2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme. 2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme.
@@ -257,7 +257,7 @@ let us look at same example, where we have disabled the multiColor option.
### Customizing Color scheme ### Customizing Color scheme
You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on. You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12 sections, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on.
In case you have more than 12 sections, the color scheme will start to repeat. In case you have more than 12 sections, the color scheme will start to repeat.
NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values. NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values.

View File

@@ -4,7 +4,7 @@
"version": "10.0.2", "version": "10.0.2",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module", "type": "module",
"packageManager": "pnpm@7.30.0", "packageManager": "pnpm@7.30.1",
"keywords": [ "keywords": [
"diagram", "diagram",
"markdown", "markdown",

View File

@@ -5,6 +5,7 @@ import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
import { extractFrontMatter } from './diagram-api/frontmatter.js'; import { extractFrontMatter } from './diagram-api/frontmatter.js';
import { UnknownDiagramError } from './errors.js'; import { UnknownDiagramError } from './errors.js';
import { DetailedError } from './utils.js'; import { DetailedError } from './utils.js';
import { cleanupComments } from './diagram-api/comments.js';
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void; export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
@@ -43,7 +44,8 @@ export class Diagram {
// Similarly, we can't do this in getDiagramFromText() because some code // Similarly, we can't do this in getDiagramFromText() because some code
// calls diagram.db.clear(), which would reset anything set by // calls diagram.db.clear(), which would reset anything set by
// extractFrontMatter(). // extractFrontMatter().
this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db)); this.parser.parse = (text: string) =>
originalParse(cleanupComments(extractFrontMatter(text, this.db)));
this.parser.parser.yy = this.db; this.parser.parser.yy = this.db;
if (diagram.init) { if (diagram.init) {
diagram.init(cnf); diagram.init(cnf);

View File

@@ -3,6 +3,7 @@ import { getConfig } from './config.js';
let title = ''; let title = '';
let diagramTitle = ''; let diagramTitle = '';
let description = ''; let description = '';
const sanitizeText = (txt: string): string => _sanitizeText(txt, getConfig()); const sanitizeText = (txt: string): string => _sanitizeText(txt, getConfig());
export const clear = function (): void { export const clear = function (): void {
@@ -36,10 +37,10 @@ export const getDiagramTitle = function (): string {
}; };
export default { export default {
setAccTitle,
getAccTitle, getAccTitle,
setAccTitle,
getDiagramTitle,
setDiagramTitle, setDiagramTitle,
getDiagramTitle: getDiagramTitle,
getAccDescription, getAccDescription,
setAccDescription, setAccDescription,
clear, clear,

View File

@@ -335,6 +335,7 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
axisFormat?: string; axisFormat?: string;
tickInterval?: string; tickInterval?: string;
topAxis?: boolean; topAxis?: boolean;
displayMode?: string;
} }
export interface SequenceDiagramConfig extends BaseDiagramConfig { export interface SequenceDiagramConfig extends BaseDiagramConfig {

View File

@@ -659,6 +659,17 @@ const config: Partial<MermaidConfig> = {
*/ */
numberSectionStyles: 4, numberSectionStyles: 4,
/**
* | Parameter | Description | Type | Required | Values |
* | ----------- | ------------------------- | ------ | -------- | --------- |
* | displayMode | Controls the display mode | string | 4 | 'compact' |
*
* **Notes**:
*
* - **compact**: Enables displaying multiple tasks on the same row.
*/
displayMode: '',
/** /**
* | Parameter | Description | Type | Required | Values | * | Parameter | Description | Type | Required | Values |
* | ---------- | ---------------------------- | ---- | -------- | ---------------- | * | ---------- | ---------------------------- | ---- | -------- | ---------------- |
@@ -684,7 +695,6 @@ const config: Partial<MermaidConfig> = {
* Default value: undefined * Default value: undefined
*/ */
tickInterval: undefined, tickInterval: undefined,
/** /**
* | Parameter | Description | Type | Required | Values | * | Parameter | Description | Type | Required | Values |
* | ----------- | ----------- | ------- | -------- | ----------- | * | ----------- | ----------- | ------- | -------- | ----------- |

View File

@@ -0,0 +1,94 @@
// tests to check that comments are removed
import { cleanupComments } from './comments.js';
import { describe, it, expect } from 'vitest';
describe('comments', () => {
it('should remove comments', () => {
const text = `
%% This is a comment
%% This is another comment
graph TD
A-->B
%% This is a comment
`;
expect(cleanupComments(text)).toMatchInlineSnapshot(`
"graph TD
A-->B
"
`);
});
it('should keep init statements when removing comments', () => {
const text = `
%% This is a comment
%% This is another comment
%%{init: {'theme': 'forest'}}%%
%%{ init: {'theme': 'space before init'}}%%
%%{init: {'theme': 'space after ending'}}%%
graph TD
A-->B
B-->C
%% This is a comment
`;
expect(cleanupComments(text)).toMatchInlineSnapshot(`
"%%{init: {'theme': 'forest'}}%%
%%{ init: {'theme': 'space before init'}}%%
%%{init: {'theme': 'space after ending'}}%%
graph TD
A-->B
B-->C
"
`);
});
it('should remove indented comments', () => {
const text = `
%% This is a comment
graph TD
A-->B
%% This is a comment
C-->D
`;
expect(cleanupComments(text)).toMatchInlineSnapshot(`
"graph TD
A-->B
C-->D
"
`);
});
it('should remove empty newlines from start', () => {
const text = `
%% This is a comment
graph TD
A-->B
`;
expect(cleanupComments(text)).toMatchInlineSnapshot(`
"graph TD
A-->B
"
`);
});
it('should remove comments at end of text with no newline', () => {
const text = `
graph TD
A-->B
%% This is a comment`;
expect(cleanupComments(text)).toMatchInlineSnapshot(`
"graph TD
A-->B
"
`);
});
});

View File

@@ -0,0 +1,8 @@
/**
* Remove all lines starting with `%%` from the text that don't contain a `%%{`
* @param text - The text to remove comments from
* @returns cleaned text
*/
export const cleanupComments = (text: string): string => {
return text.trimStart().replace(/^\s*%%(?!{)[^\n]+\n?/gm, '');
};

View File

@@ -13,7 +13,7 @@ import classDiagramV2 from '../diagrams/class/classDetector-V2.js';
import state from '../diagrams/state/stateDetector.js'; import state from '../diagrams/state/stateDetector.js';
import stateV2 from '../diagrams/state/stateDetector-V2.js'; import stateV2 from '../diagrams/state/stateDetector-V2.js';
import journey from '../diagrams/user-journey/journeyDetector.js'; import journey from '../diagrams/user-journey/journeyDetector.js';
import error from '../diagrams/error/errorDetector.js'; import errorDiagram from '../diagrams/error/errorDiagram.js';
import flowchartElk from '../diagrams/flowchart/elk/detector.js'; import flowchartElk from '../diagrams/flowchart/elk/detector.js';
import timeline from '../diagrams/timeline/detector.js'; import timeline from '../diagrams/timeline/detector.js';
import mindmap from '../diagrams/mindmap/detector.js'; import mindmap from '../diagrams/mindmap/detector.js';
@@ -28,6 +28,9 @@ export const addDiagrams = () => {
// This is added here to avoid race-conditions. // This is added here to avoid race-conditions.
// We could optimize the loading logic somehow. // We could optimize the loading logic somehow.
hasLoadedDiagrams = true; hasLoadedDiagrams = true;
registerDiagram('error', errorDiagram, (text) => {
return text.toLowerCase().trim() === 'error';
});
registerDiagram( registerDiagram(
'---', '---',
// --- diagram type may appear if YAML front-matter is not parsed correctly // --- diagram type may appear if YAML front-matter is not parsed correctly
@@ -57,7 +60,6 @@ export const addDiagrams = () => {
); );
// Ordering of detectors is important. The first one to return true will be used. // Ordering of detectors is important. The first one to return true will be used.
registerLazyLoadedDiagrams( registerLazyLoadedDiagrams(
error,
c4, c4,
classDiagramV2, classDiagramV2,
classDiagram, classDiagram,

View File

@@ -11,6 +11,8 @@ export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
type FrontMatterMetadata = { type FrontMatterMetadata = {
title?: string; title?: string;
// Allows custom display modes. Currently used for compact mode in gantt charts.
displayMode?: string;
}; };
/** /**
@@ -33,6 +35,10 @@ export function extractFrontMatter(text: string, db: DiagramDb): string {
db.setDiagramTitle?.(parsed.title); db.setDiagramTitle?.(parsed.title);
} }
if (parsed?.displayMode) {
db.setDisplayMode?.(parsed.displayMode);
}
return text.slice(matches[0].length); return text.slice(matches[0].length);
} else { } else {
return text; return text;

View File

@@ -16,6 +16,7 @@ export interface InjectUtils {
export interface DiagramDb { export interface DiagramDb {
clear?: () => void; clear?: () => void;
setDiagramTitle?: (title: string) => void; setDiagramTitle?: (title: string) => void;
setDisplayMode?: (title: string) => void;
getAccTitle?: () => string; getAccTitle?: () => string;
getAccDescription?: () => string; getAccDescription?: () => string;
bindFunctions?: (element: Element) => void; bindFunctions?: (element: Element) => void;

View File

@@ -19,8 +19,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; } <type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; } <type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive'; <arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[\n]+ return 'NEWLINE'; [\n]+ return 'NEWLINE';
\s+ /* skip whitespace */ \s+ /* skip whitespace */
[\s]+ return 'SPACE'; [\s]+ return 'SPACE';
@@ -35,8 +33,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<block>[A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD' <block>[A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD'
<block>\"[^"]*\" return 'COMMENT'; <block>\"[^"]*\" return 'COMMENT';
<block>[\n]+ /* nothing */ <block>[\n]+ /* nothing */
<block>\%%(?!\{)[^\n]* /* skip comments in attribute block */
<block>[^\}]\%\%[^\n]* /* skip comments in attribute block */
<block>"}" { this.popState(); return 'BLOCK_STOP'; } <block>"}" { this.popState(); return 'BLOCK_STOP'; }
<block>. return yytext[0]; <block>. return yytext[0];

View File

@@ -1,20 +0,0 @@
import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-api/types.js';
const id = 'error';
const detector: DiagramDetector = (text) => {
return text.toLowerCase().trim() === 'error';
};
const loader = async () => {
const { diagram } = await import('./errorDiagram.js');
return { id, diagram };
};
const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

@@ -19,3 +19,5 @@ export const diagram: DiagramDefinition = {
// no op // no op
}, },
}; };
export default diagram;

View File

@@ -4,15 +4,13 @@ import { select } from 'd3';
import { log } from '../../logger.js'; import { log } from '../../logger.js';
import { getErrorMessage } from '../../utils.js'; import { getErrorMessage } from '../../utils.js';
let conf = {};
/** /**
* Merges the value of `conf` with the passed `cnf` * Merges the value of `conf` with the passed `cnf`
* *
* @param cnf - Config to merge * @param cnf - Config to merge
*/ */
export const setConf = function (cnf: any) { export const setConf = function () {
conf = { ...conf, ...cnf }; // no-op
}; };
/** /**
@@ -78,7 +76,7 @@ export const draw = (_text: string, id: string, mermaidVersion: string) => {
.attr('y', 250) .attr('y', 250)
.attr('font-size', '150px') .attr('font-size', '150px')
.style('text-anchor', 'middle') .style('text-anchor', 'middle')
.text('Syntax error in graph'); .text('Syntax error in text');
g.append('text') // text label for the x axis g.append('text') // text label for the x axis
.attr('class', 'error-text') .attr('class', 'error-text')
.attr('x', 1250) .attr('x', 1250)

View File

@@ -1,6 +1,7 @@
import flowDb from '../flowDb.js'; import flowDb from '../flowDb.js';
import flow from './flow.jison'; import flow from './flow.jison';
import { setConfig } from '../../../config.js'; import { setConfig } from '../../../config.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
setConfig({ setConfig({
securityLevel: 'strict', securityLevel: 'strict',
@@ -13,7 +14,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle comments', function () { it('should handle comments', function () {
const res = flow.parser.parse('graph TD;\n%% Comment\n A-->B;'); const res = flow.parser.parse(cleanupComments('graph TD;\n%% Comment\n A-->B;'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -28,7 +29,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle comments at the start', function () { it('should handle comments at the start', function () {
const res = flow.parser.parse('%% Comment\ngraph TD;\n A-->B;'); const res = flow.parser.parse(cleanupComments('%% Comment\ngraph TD;\n A-->B;'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -43,7 +44,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle comments at the end', function () { it('should handle comments at the end', function () {
const res = flow.parser.parse('graph TD;\n A-->B\n %% Comment at the end\n'); const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n %% Comment at the end\n'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -58,7 +59,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle comments at the end no trailing newline', function () { it('should handle comments at the end no trailing newline', function () {
const res = flow.parser.parse('graph TD;\n A-->B\n%% Comment'); const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -73,7 +74,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle comments at the end many trailing newlines', function () { it('should handle comments at the end many trailing newlines', function () {
const res = flow.parser.parse('graph TD;\n A-->B\n%% Comment\n\n\n'); const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment\n\n\n'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -88,7 +89,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle no trailing newlines', function () { it('should handle no trailing newlines', function () {
const res = flow.parser.parse('graph TD;\n A-->B'); const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -103,7 +104,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle many trailing newlines', function () { it('should handle many trailing newlines', function () {
const res = flow.parser.parse('graph TD;\n A-->B\n\n'); const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n\n'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -118,7 +119,7 @@ describe('[Comments] when parsing', () => {
}); });
it('should handle a comment with blank rows in-between', function () { it('should handle a comment with blank rows in-between', function () {
const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B;'); const res = flow.parser.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B;'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@@ -134,7 +135,9 @@ describe('[Comments] when parsing', () => {
it('should handle a comment with mermaid flowchart code in them', function () { it('should handle a comment with mermaid flowchart code in them', function () {
const res = flow.parser.parse( const res = flow.parser.parse(
cleanupComments(
'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line<br>edge comment|ro;\n A-->B;' 'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line<br>edge comment|ro;\n A-->B;'
)
); );
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();

View File

@@ -27,8 +27,6 @@
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; } <type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; } <type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive'; <arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; } <acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; } accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }

View File

@@ -1,6 +1,7 @@
import flowDb from '../flowDb.js'; import flowDb from '../flowDb.js';
import flow from './flow.jison'; import flow from './flow.jison';
import { setConfig } from '../../../config.js'; import { setConfig } from '../../../config.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
setConfig({ setConfig({
securityLevel: 'strict', securityLevel: 'strict',
@@ -13,7 +14,7 @@ describe('parsing a flow chart', function () {
}); });
it('should handle a trailing whitespaces after statements', function () { it('should handle a trailing whitespaces after statements', function () {
const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;'); const res = flow.parser.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;'));
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();

View File

@@ -32,6 +32,7 @@ let links = {};
let sections = []; let sections = [];
let tasks = []; let tasks = [];
let currentSection = ''; let currentSection = '';
let displayMode = '';
const tags = ['active', 'done', 'crit', 'milestone']; const tags = ['active', 'done', 'crit', 'milestone'];
let funs = []; let funs = [];
let inclusiveEndDates = false; let inclusiveEndDates = false;
@@ -55,6 +56,7 @@ export const clear = function () {
rawTasks = []; rawTasks = [];
dateFormat = ''; dateFormat = '';
axisFormat = ''; axisFormat = '';
displayMode = '';
tickInterval = undefined; tickInterval = undefined;
todayMarker = ''; todayMarker = '';
includes = []; includes = [];
@@ -110,6 +112,14 @@ export const topAxisEnabled = function () {
return topAxis; return topAxis;
}; };
export const setDisplayMode = function (txt) {
displayMode = txt;
};
export const getDisplayMode = function () {
return displayMode;
};
export const getDateFormat = function () { export const getDateFormat = function () {
return dateFormat; return dateFormat;
}; };
@@ -143,11 +153,11 @@ export const getSections = function () {
}; };
export const getTasks = function () { export const getTasks = function () {
let allItemsPricessed = compileTasks(); let allItemsProcessed = compileTasks();
const maxDepth = 10; const maxDepth = 10;
let iterationCount = 0; let iterationCount = 0;
while (!allItemsPricessed && iterationCount < maxDepth) { while (!allItemsProcessed && iterationCount < maxDepth) {
allItemsPricessed = compileTasks(); allItemsProcessed = compileTasks();
iterationCount++; iterationCount++;
} }
@@ -719,6 +729,8 @@ export default {
getAccTitle, getAccTitle,
setDiagramTitle, setDiagramTitle,
getDiagramTitle, getDiagramTitle,
setDisplayMode,
getDisplayMode,
setAccDescription, setAccDescription,
getAccDescription, getAccDescription,
addSection, addSection,

View File

@@ -34,6 +34,7 @@ describe('when using the ganttDb', function () {
beforeEach(function () { beforeEach(function () {
ganttDb.setDateFormat('YYYY-MM-DD'); ganttDb.setDateFormat('YYYY-MM-DD');
ganttDb.enableInclusiveEndDates(); ganttDb.enableInclusiveEndDates();
ganttDb.setDisplayMode('compact');
ganttDb.setTodayMarker('off'); ganttDb.setTodayMarker('off');
ganttDb.setExcludes('weekends 2019-02-06,friday'); ganttDb.setExcludes('weekends 2019-02-06,friday');
ganttDb.addSection('weekends skip test'); ganttDb.addSection('weekends skip test');
@@ -53,6 +54,7 @@ describe('when using the ganttDb', function () {
${'getExcludes'} | ${[]} ${'getExcludes'} | ${[]}
${'getSections'} | ${[]} ${'getSections'} | ${[]}
${'endDatesAreInclusive'} | ${false} ${'endDatesAreInclusive'} | ${false}
${'getDisplayMode'} | ${''}
`)('should clear $fn', ({ fn, expected }) => { `)('should clear $fn', ({ fn, expected }) => {
expect(ganttDb[fn]()).toEqual(expected); expect(ganttDb[fn]()).toEqual(expected);
}); });

View File

@@ -24,12 +24,43 @@ export const setConf = function () {
log.debug('Something is calling, setConf, remove the call'); log.debug('Something is calling, setConf, remove the call');
}; };
/**
* For this issue:
* https://github.com/mermaid-js/mermaid/issues/1618
*
* Finds the number of intersections between tasks that happen at any point in time.
* Used to figure out how many rows are needed to display the tasks when the display
* mode is set to 'compact'.
*
* @param tasks
* @param orderOffset
*/
const getMaxIntersections = (tasks, orderOffset) => {
let timeline = [...tasks].map(() => -Infinity);
let sorted = [...tasks].sort((a, b) => a.startTime - b.startTime || a.order - b.order);
let maxIntersections = 0;
for (const element of sorted) {
for (let j = 0; j < timeline.length; j++) {
if (element.startTime >= timeline[j]) {
timeline[j] = element.endTime;
element.order = j + orderOffset;
if (j > maxIntersections) {
maxIntersections = j;
}
break;
}
}
}
return maxIntersections;
};
let w; let w;
export const draw = function (text, id, version, diagObj) { export const draw = function (text, id, version, diagObj) {
const conf = getConfig().gantt; const conf = getConfig().gantt;
// diagObj.db.clear(); // diagObj.db.clear();
// parser.parse(text); // parser.parse(text);
const securityLevel = getConfig().securityLevel; const securityLevel = getConfig().securityLevel;
// Handle root and Document for when rendering in sandbox mode // Handle root and Document for when rendering in sandbox mode
let sandboxElement; let sandboxElement;
@@ -56,7 +87,40 @@ export const draw = function (text, id, version, diagObj) {
const taskArray = diagObj.db.getTasks(); const taskArray = diagObj.db.getTasks();
// Set height based on number of tasks // Set height based on number of tasks
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding;
let categories = [];
for (const element of taskArray) {
categories.push(element.type);
}
categories = checkUnique(categories);
const categoryHeights = {};
let h = 2 * conf.topPadding;
if (diagObj.db.getDisplayMode() === 'compact' || conf.displayMode === 'compact') {
const categoryElements = {};
for (const element of taskArray) {
if (categoryElements[element.section] === undefined) {
categoryElements[element.section] = [element];
} else {
categoryElements[element.section].push(element);
}
}
let intersections = 0;
for (const category of Object.keys(categoryElements)) {
const categoryHeight = getMaxIntersections(categoryElements[category], intersections) + 1;
intersections += categoryHeight;
h += categoryHeight * (conf.barHeight + conf.barGap);
categoryHeights[category] = categoryHeight;
}
} else {
h += taskArray.length * (conf.barHeight + conf.barGap);
for (const category of categories) {
categoryHeights[category] = taskArray.filter((task) => task.type === category).length;
}
}
// Set viewBox // Set viewBox
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h); elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
@@ -74,16 +138,6 @@ export const draw = function (text, id, version, diagObj) {
]) ])
.rangeRound([0, w - conf.leftPadding - conf.rightPadding]); .rangeRound([0, w - conf.leftPadding - conf.rightPadding]);
let categories = [];
for (const element of taskArray) {
categories.push(element.type);
}
const catsUnfiltered = categories; // for vert labels
categories = checkUnique(categories);
/** /**
* @param a * @param a
* @param b * @param b
@@ -157,11 +211,15 @@ export const draw = function (text, id, version, diagObj) {
* @param w * @param w
*/ */
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w) { function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w) {
// Get unique task orders. Required to draw the background rects when display mode is compact.
const uniqueTaskOrderIds = [...new Set(theArray.map((item) => item.order))];
const uniqueTasks = uniqueTaskOrderIds.map((id) => theArray.find((item) => item.order === id));
// Draw background rects covering the entire width of the graph, these form the section rows. // Draw background rects covering the entire width of the graph, these form the section rows.
svg svg
.append('g') .append('g')
.selectAll('rect') .selectAll('rect')
.data(theArray) .data(uniqueTasks)
.enter() .enter()
.append('rect') .append('rect')
.attr('x', 0) .attr('x', 0)
@@ -582,12 +640,9 @@ export const draw = function (text, id, version, diagObj) {
* @param theTopPad * @param theTopPad
*/ */
function vertLabels(theGap, theTopPad) { function vertLabels(theGap, theTopPad) {
const numOccurances = [];
let prevGap = 0; let prevGap = 0;
for (const [i, category] of categories.entries()) { const numOccurances = Object.keys(categoryHeights).map((d) => [d, categoryHeights[d]]);
numOccurances[i] = [category, getCount(category, catsUnfiltered)];
}
svg svg
.append('g') // without doing this, impossible to put grid lines behind text .append('g') // without doing this, impossible to put grid lines behind text
@@ -625,7 +680,6 @@ export const draw = function (text, id, version, diagObj) {
} }
}) })
.attr('font-size', conf.sectionFontSize) .attr('font-size', conf.sectionFontSize)
.attr('font-size', conf.sectionFontSize)
.attr('class', function (d) { .attr('class', function (d) {
for (const [i, category] of categories.entries()) { for (const [i, category] of categories.entries()) {
if (d[0] === category) { if (d[0] === category) {
@@ -682,31 +736,6 @@ export const draw = function (text, id, version, diagObj) {
} }
return result; return result;
} }
/**
* From this stack exchange question:
* http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
*
* @param arr
*/
function getCounts(arr) {
let i = arr.length; // const to loop over
const obj = {}; // obj to store results
while (i) {
obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
}
return obj;
}
/**
* Get specific from everything
*
* @param word
* @param arr
*/
function getCount(word, arr) {
return getCounts(arr)[word] || 0;
}
}; };
export default { export default {

View File

@@ -133,7 +133,8 @@ statement
| title {yy.setDiagramTitle($1.substr(6));$$=$1.substr(6);} | title {yy.setDiagramTitle($1.substr(6));$$=$1.substr(6);}
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } | acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } | acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);} | acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
| section { yy.addSection($1.substr(8));$$=$1.substr(8); }
| clickStatement | clickStatement
| taskTxt taskData {yy.addTask($1,$2);$$='task';} | taskTxt taskData {yy.addTask($1,$2);$$='task';}
| directive | directive

View File

@@ -122,9 +122,7 @@ flowchart LR
### A hexagon node ### A hexagon node
Code: ```mermaid-example
```mmd
flowchart LR flowchart LR
id1{{This is the text in the box}} id1{{This is the text in the box}}
``` ```

View File

@@ -189,9 +189,27 @@ The pattern is:
More info in: [https://github.com/d3/d3-time#interval_every](https://github.com/d3/d3-time#interval_every) More info in: [https://github.com/d3/d3-time#interval_every](https://github.com/d3/d3-time#interval_every)
## Output in compact mode
The compact mode allows you to display multiple tasks in the same row. Compact mode can be enabled for a gantt chart by setting the display mode of the graph via preceeding YAML settings.
```mmd
---
displayMode: compact
---
gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :a2, 2014-01-20, 25d
Another one :a3, 2014-02-10, 20d
```
## Comments ## Comments
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax.
```mmd ```mmd
gantt gantt

View File

@@ -2,7 +2,7 @@
> Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part. > Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
"A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia "A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia
### An example of a timeline. ### An example of a timeline.
@@ -139,7 +139,7 @@ However, if there is no section defined, then we have two possibilities:
``` ```
Note that this is no, section defined, and each time period and its corresponding events will have its own color scheme. Note that there are no sections defined, and each time period and its corresponding events will have its own color scheme.
2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme. 2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme.
@@ -172,7 +172,7 @@ let us look at same example, where we have disabled the multiColor option.
### Customizing Color scheme ### Customizing Color scheme
You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on. You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12 sections, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on.
In case you have more than 12 sections, the color scheme will start to repeat. In case you have more than 12 sections, the color scheme will start to repeat.
NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values. NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values.

View File

@@ -392,7 +392,7 @@ const render = (id: string, text: string, container?: Element): Promise<RenderRe
}); });
}; };
const mermaid: { export interface Mermaid {
startOnLoad: boolean; startOnLoad: boolean;
parseError?: ParseErrorFunction; parseError?: ParseErrorFunction;
mermaidAPI: typeof mermaidAPI; mermaidAPI: typeof mermaidAPI;
@@ -405,7 +405,9 @@ const mermaid: {
contentLoaded: typeof contentLoaded; contentLoaded: typeof contentLoaded;
setParseErrorHandler: typeof setParseErrorHandler; setParseErrorHandler: typeof setParseErrorHandler;
detectType: typeof detectType; detectType: typeof detectType;
} = { }
const mermaid: Mermaid = {
startOnLoad: true, startOnLoad: true,
mermaidAPI, mermaidAPI,
parse, parse,

View File

@@ -531,6 +531,10 @@ const render = async function (
attachFunctions(); attachFunctions();
if (parseEncounteredException) {
throw parseEncounteredException;
}
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
// Remove the temporary HTML element if appropriate // Remove the temporary HTML element if appropriate
const tmpElementSelector = isSandboxed ? iFrameID_selector : enclosingDivID_selector; const tmpElementSelector = isSandboxed ? iFrameID_selector : enclosingDivID_selector;
@@ -539,10 +543,6 @@ const render = async function (
node.remove(); node.remove();
} }
if (parseEncounteredException) {
throw parseEncounteredException;
}
return { return {
svg: svgCode, svg: svgCode,
bindFunctions: diag.db.bindFunctions, bindFunctions: diag.db.bindFunctions,
@@ -650,6 +650,7 @@ function addA11yInfo(
* numberSectionStyles: 4, * numberSectionStyles: 4,
* axisFormat: '%Y-%m-%d', * axisFormat: '%Y-%m-%d',
* topAxis: false, * topAxis: false,
* displayMode: '',
* }, * },
* }; * };
* mermaid.initialize(config); * mermaid.initialize(config);