diff --git a/.changeset/bright-ads-exist.md b/.changeset/bright-ads-exist.md new file mode 100644 index 000000000..ef2f76f4c --- /dev/null +++ b/.changeset/bright-ads-exist.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +Fixes for consistent edge id creation & handling edge cases for animate edge feature diff --git a/.changeset/chatty-elephants-warn.md b/.changeset/chatty-elephants-warn.md new file mode 100644 index 000000000..225047ece --- /dev/null +++ b/.changeset/chatty-elephants-warn.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +Fix for issue #6195 - allowing @ signs inside node labels diff --git a/.changeset/stupid-dots-do.md b/.changeset/stupid-dots-do.md new file mode 100644 index 000000000..594fa9536 --- /dev/null +++ b/.changeset/stupid-dots-do.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Gantt, Sankey and User Journey diagram are now able to pick font-family from mermaid config. diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index dfcd8aa4f..90ecd20ea 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -78,9 +78,11 @@ font-family: monospace; font-size: 72px; } + pre { width: 100%; } + /* tspan { font-size: 6px !important; } */ @@ -103,16 +105,20 @@
-+flowchart LR - A --> B + AB["apa@apa@"] --> B(("`apa@apa`")) +++ flowchart + D(("for D"))flowchart LR A e1@==> B e1@{ animate: true}-+flowchart LR A e1@--> B classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round @@ -126,7 +132,7 @@ flowchart LR class e1 animateMermaid - edge-animation-slow
-+flowchart LR A e1@--> B e1@{ animation: fast} @@ -428,7 +434,10 @@ kanban window.callback = function () { alert('A callback was triggered'); }; - mermaid.initialize({ + function callback() { + alert('It worked'); + } + await mermaid.initialize({ // theme: 'base', // theme: 'default', // theme: 'forest', @@ -440,9 +449,11 @@ kanban // layout: 'fixed', // htmlLabels: false, flowchart: { titleTopMargin: 10 }, + // fontFamily: 'Caveat', // fontFamily: 'Kalam', // fontFamily: 'courier', + fontFamily: 'arial', sequence: { actorFontFamily: 'courier', noteFontFamily: 'courier', @@ -454,10 +465,9 @@ kanban fontSize: 12, logLevel: 0, securityLevel: 'loose', + callback, }); - function callback() { - alert('It worked'); - } + mermaid.parseError = function (err, hash) { console.error('In parse error:'); console.error(err); diff --git a/docs/config/setup/interfaces/mermaid.LayoutData.md b/docs/config/setup/interfaces/mermaid.LayoutData.md index 552a16a8d..46c9134e8 100644 --- a/docs/config/setup/interfaces/mermaid.LayoutData.md +++ b/docs/config/setup/interfaces/mermaid.LayoutData.md @@ -20,7 +20,7 @@ #### Defined in -[packages/mermaid/src/rendering-util/types.ts:147](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L147) +[packages/mermaid/src/rendering-util/types.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L148) --- @@ -30,7 +30,7 @@ #### Defined in -[packages/mermaid/src/rendering-util/types.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L146) +[packages/mermaid/src/rendering-util/types.ts:147](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L147) --- @@ -40,4 +40,4 @@ #### Defined in -[packages/mermaid/src/rendering-util/types.ts:145](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L145) +[packages/mermaid/src/rendering-util/types.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L146) diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts index e96384110..7074464dc 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -133,10 +133,10 @@ export class FlowDB implements DiagramDB { const edge = this.edges.find((e) => e.id === id); if (edge) { const edgeDoc = doc as EdgeMetaData; - if (edgeDoc?.animate) { + if (edgeDoc?.animate !== undefined) { edge.animate = edgeDoc.animate; } - if (edgeDoc?.animation) { + if (edgeDoc?.animation !== undefined) { edge.animation = edgeDoc.animation; } return; @@ -251,6 +251,7 @@ export class FlowDB implements DiagramDB { text: '', labelType: 'text', classes: [], + isUserDefinedId: false, }; log.info('abc78 Got edge...', edge); const linkTextObj = type.text; @@ -270,8 +271,19 @@ export class FlowDB implements DiagramDB { edge.stroke = type.stroke; edge.length = type.length > 10 ? 10 : type.length; } - if (id) { + if (id && !this.edges.some((e) => e.id === id)) { edge.id = id; + edge.isUserDefinedId = true; + } else { + const existingLinks = this.edges.filter((e) => e.start === edge.start && e.end === edge.end); + if (existingLinks.length === 0) { + edge.id = getEdgeId(edge.start, edge.end, { counter: 0, prefix: 'L' }); + } else { + edge.id = getEdgeId(edge.start, edge.end, { + counter: existingLinks.length + 1, + prefix: 'L', + }); + } } if (this.edges.length < (this.config.maxEdges ?? 500)) { @@ -302,9 +314,18 @@ You have to call mermaid.initialize.` log.info('addLink', _start, _end, id); + // for a group syntax like A e1@--> B & C, only the first edge should have an the userDefined id + // the rest of the edges should have auto generated ids for (const start of _start) { for (const end of _end) { - this.addSingleLink(start, end, linkData, id); + //use the id only for last node in _start and and first node in _end + const isLastStart = start === _start[_start.length - 1]; + const isFirstEnd = end === _end[0]; + if (isLastStart && isFirstEnd) { + this.addSingleLink(start, end, linkData, id); + } else { + this.addSingleLink(start, end, linkData, undefined); + } } } } @@ -1077,6 +1098,7 @@ You have to call mermaid.initialize.` } const edge: Edge = { id: getEdgeId(rawEdge.start, rawEdge.end, { counter: index, prefix: 'L' }, rawEdge.id), + isUserDefinedId: rawEdge.isUserDefinedId, start: rawEdge.start, end: rawEdge.end, type: rawEdge.type ?? 'normal', diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js index 284f1b225..60620d8b2 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js @@ -251,7 +251,7 @@ describe('when parsing directions', function () { expect(data4Layout.nodes[0].shape).toEqual('squareRect'); expect(data4Layout.nodes[0].label).toEqual('This is a
multiline string'); }); - it(' should be possible to use } in strings', function () { + it('should be possible to use } in strings', function () { const res = flow.parser.parse(`flowchart TB A@{ label: "This is a string with }" @@ -264,7 +264,7 @@ describe('when parsing directions', function () { expect(data4Layout.nodes[0].shape).toEqual('squareRect'); expect(data4Layout.nodes[0].label).toEqual('This is a string with }'); }); - it(' should be possible to use @ in strings', function () { + it('should be possible to use @ in strings', function () { const res = flow.parser.parse(`flowchart TB A@{ label: "This is a string with @" @@ -277,7 +277,7 @@ describe('when parsing directions', function () { expect(data4Layout.nodes[0].shape).toEqual('squareRect'); expect(data4Layout.nodes[0].label).toEqual('This is a string with @'); }); - it(' should be possible to use @ in strings', function () { + it('should be possible to use @ in strings', function () { const res = flow.parser.parse(`flowchart TB A@{ label: "This is a string with}" @@ -291,7 +291,7 @@ describe('when parsing directions', function () { expect(data4Layout.nodes[0].label).toEqual('This is a string with}'); }); - it(' should be possible to use @ syntax to add labels on multi nodes', function () { + it('should be possible to use @ syntax to add labels on multi nodes', function () { const res = flow.parser.parse(`flowchart TB n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"} `); @@ -317,7 +317,90 @@ describe('when parsing directions', function () { expect(data4Layout.nodes[3].label).toEqual('for E'); expect(data4Layout.nodes[4].label).toEqual('for D'); }); - it.skip(' should be possible to use @ syntax to add labels with trail spaces', function () { + + it('should be possible to use @ syntax in labels', function () { + const res = flow.parser.parse(`flowchart TD + A["@A@"] --> B["@for@ B@"] & C@{ label: "@for@ c@"} & E{"\`@for@ E@\`"} & D(("@for@ D@")) + H1{{"@for@ H@"}} + H2{{"\`@for@ H@\`"}} + Q1{"@for@ Q@"} + Q2{"\`@for@ Q@\`"} + AS1>"@for@ AS@"] + AS2>"\`@for@ AS@\`"] + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(11); + expect(data4Layout.nodes[0].label).toEqual('@A@'); + expect(data4Layout.nodes[1].label).toEqual('@for@ B@'); + expect(data4Layout.nodes[2].label).toEqual('@for@ c@'); + expect(data4Layout.nodes[3].label).toEqual('@for@ E@'); + expect(data4Layout.nodes[4].label).toEqual('@for@ D@'); + expect(data4Layout.nodes[5].label).toEqual('@for@ H@'); + expect(data4Layout.nodes[6].label).toEqual('@for@ H@'); + expect(data4Layout.nodes[7].label).toEqual('@for@ Q@'); + expect(data4Layout.nodes[8].label).toEqual('@for@ Q@'); + expect(data4Layout.nodes[9].label).toEqual('@for@ AS@'); + expect(data4Layout.nodes[10].label).toEqual('@for@ AS@'); + }); + + it('should handle unique edge creation with using @ and &', function () { + const res = flow.parser.parse(`flowchart TD + A & B e1@--> C & D + A1 e2@--> C1 & D1 + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(7); + expect(data4Layout.edges.length).toBe(6); + expect(data4Layout.edges[0].id).toEqual('L_A_C_0'); + expect(data4Layout.edges[1].id).toEqual('L_A_D_0'); + expect(data4Layout.edges[2].id).toEqual('e1'); + expect(data4Layout.edges[3].id).toEqual('L_B_D_0'); + expect(data4Layout.edges[4].id).toEqual('e2'); + expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0'); + }); + + it('should handle redefine same edge ids again', function () { + const res = flow.parser.parse(`flowchart TD + A & B e1@--> C & D + A1 e1@--> C1 & D1 + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(7); + expect(data4Layout.edges.length).toBe(6); + expect(data4Layout.edges[0].id).toEqual('L_A_C_0'); + expect(data4Layout.edges[1].id).toEqual('L_A_D_0'); + expect(data4Layout.edges[2].id).toEqual('e1'); + expect(data4Layout.edges[3].id).toEqual('L_B_D_0'); + expect(data4Layout.edges[4].id).toEqual('L_A1_C1_0'); + expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0'); + }); + + it('should handle overriding edge animate again', function () { + const res = flow.parser.parse(`flowchart TD + A e1@--> B + C e2@--> D + E e3@--> F + e1@{ animate: true } + e2@{ animate: false } + e3@{ animate: true } + e3@{ animate: false } + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(6); + expect(data4Layout.edges.length).toBe(3); + expect(data4Layout.edges[0].id).toEqual('e1'); + expect(data4Layout.edges[0].animate).toEqual(true); + expect(data4Layout.edges[1].id).toEqual('e2'); + expect(data4Layout.edges[1].animate).toEqual(false); + expect(data4Layout.edges[2].id).toEqual('e3'); + expect(data4Layout.edges[2].animate).toEqual(false); + }); + + it.skip('should be possible to use @ syntax to add labels with trail spaces', function () { const res = flow.parser.parse( `flowchart TB n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"} ` diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index c3ac2b04b..7b2f4386a 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -141,7 +141,7 @@ that id. .*direction\s+RL[^\n]* return 'direction_rl'; .*direction\s+LR[^\n]* return 'direction_lr'; -[^\s]+\@(?=[^\{]) { return 'LINK_ID'; } +[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; } [0-9]+ return 'NUM'; \# return 'BRKT'; ":::" return 'STYLE_SEPARATOR'; diff --git a/packages/mermaid/src/diagrams/flowchart/types.ts b/packages/mermaid/src/diagrams/flowchart/types.ts index 00acb6751..54156091b 100644 --- a/packages/mermaid/src/diagrams/flowchart/types.ts +++ b/packages/mermaid/src/diagrams/flowchart/types.ts @@ -53,6 +53,7 @@ export interface FlowText { } export interface FlowEdge { + isUserDefinedId: boolean; start: string; end: string; interpolate?: string; diff --git a/packages/mermaid/src/diagrams/gantt/styles.js b/packages/mermaid/src/diagrams/gantt/styles.js index 626ed4e0f..5b53a1b07 100644 --- a/packages/mermaid/src/diagrams/gantt/styles.js +++ b/packages/mermaid/src/diagrams/gantt/styles.js @@ -1,7 +1,7 @@ const getStyles = (options) => ` .mermaid-main-font { - font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + font-family: ${options.fontFamily}; } .exclude-range { @@ -45,7 +45,7 @@ const getStyles = (options) => .sectionTitle { text-anchor: start; - font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + font-family: ${options.fontFamily}; } @@ -86,13 +86,13 @@ const getStyles = (options) => .taskText { text-anchor: middle; - font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + font-family: ${options.fontFamily}; } .taskTextOutsideRight { fill: ${options.taskTextDarkColor}; text-anchor: start; - font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + font-family: ${options.fontFamily}; } .taskTextOutsideLeft { @@ -248,7 +248,7 @@ const getStyles = (options) => text-anchor: middle; font-size: 18px; fill: ${options.titleColor || options.textColor}; - font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + font-family: ${options.fontFamily}; } `; diff --git a/packages/mermaid/src/diagrams/sankey/sankeyDiagram.ts b/packages/mermaid/src/diagrams/sankey/sankeyDiagram.ts index 6fed435ac..54aa40a78 100644 --- a/packages/mermaid/src/diagrams/sankey/sankeyDiagram.ts +++ b/packages/mermaid/src/diagrams/sankey/sankeyDiagram.ts @@ -4,11 +4,13 @@ import parser from './parser/sankey.jison'; import db from './sankeyDB.js'; import renderer from './sankeyRenderer.js'; import { prepareTextForParsing } from './sankeyUtils.js'; +import sankeyStyles from './styles.js'; const originalParse = parser.parse.bind(parser); parser.parse = (text: string) => originalParse(prepareTextForParsing(text)); export const diagram: DiagramDefinition = { + styles: sankeyStyles, parser, db, renderer, diff --git a/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts b/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts index a981a346e..5e3f04173 100644 --- a/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts +++ b/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts @@ -136,7 +136,6 @@ export const draw = function (text: string, id: string, _version: string, diagOb svg .append('g') .attr('class', 'node-labels') - .attr('font-family', 'sans-serif') .attr('font-size', 14) .selectAll('text') .data(graph.nodes) diff --git a/packages/mermaid/src/diagrams/sankey/styles.js b/packages/mermaid/src/diagrams/sankey/styles.js new file mode 100644 index 000000000..eafb62b05 --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/styles.js @@ -0,0 +1,6 @@ +const getStyles = (options) => + `.label { + font-family: ${options.fontFamily}; + }`; + +export default getStyles; diff --git a/packages/mermaid/src/diagrams/user-journey/styles.js b/packages/mermaid/src/diagrams/user-journey/styles.js index 998a73894..9cdbcd12e 100644 --- a/packages/mermaid/src/diagrams/user-journey/styles.js +++ b/packages/mermaid/src/diagrams/user-journey/styles.js @@ -1,7 +1,6 @@ const getStyles = (options) => `.label { - font-family: 'trebuchet ms', verdana, arial, sans-serif; - font-family: var(--mermaid-font-family); + font-family: ${options.fontFamily}; color: ${options.textColor}; } .mouth { @@ -79,8 +78,7 @@ const getStyles = (options) => text-align: center; max-width: 200px; padding: 2px; - font-family: 'trebuchet ms', verdana, arial, sans-serif; - font-family: var(--mermaid-font-family); + font-family: ${options.fontFamily}; font-size: 12px; background: ${options.tertiaryColor}; border: 1px solid ${options.border2}; diff --git a/packages/mermaid/src/rendering-util/types.ts b/packages/mermaid/src/rendering-util/types.ts index 1f84c66c3..b11d2f314 100644 --- a/packages/mermaid/src/rendering-util/types.ts +++ b/packages/mermaid/src/rendering-util/types.ts @@ -125,6 +125,7 @@ export interface Edge { pattern?: string; thickness?: 'normal' | 'thick' | 'invisible' | 'dotted'; look?: string; + isUserDefinedId?: boolean; } export interface RectOptions {