diff --git a/.changeset/cold-sites-accept.md b/.changeset/cold-sites-accept.md new file mode 100644 index 000000000..cba7ae414 --- /dev/null +++ b/.changeset/cold-sites-accept.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Position the edge label in state diagram correctly relative to the edge diff --git a/cypress/integration/rendering/stateDiagram-v2.spec.js b/cypress/integration/rendering/stateDiagram-v2.spec.js index 83190dbc7..e5a0cedd0 100644 --- a/cypress/integration/rendering/stateDiagram-v2.spec.js +++ b/cypress/integration/rendering/stateDiagram-v2.spec.js @@ -602,6 +602,231 @@ State1 --> [*] -- 55 } +`, + {} + ); + }); + it('should render edge labels correctly', () => { + imgSnapshotTest( + `--- +title: On The Way To Something Something DarkSide +config: + look: default + theme: default +--- + +stateDiagram-v2 + + state State1_____________ + { + c0 + } + + state State2_____________ + { + c1 + } + + state State3_____________ + { + c7 + } + + state State4_____________ + { + c2 + } + + state State5_____________ + { + c3 + } + + state State6_____________ + { + c4 + } + + state State7_____________ + { + c5 + } + + state State8_____________ + { + c6 + } + + +[*] --> State1_____________ +State1_____________ --> State2_____________ : Transition1_____ +State2_____________ --> State4_____________ : Transition2_____ +State2_____________ --> State3_____________ : Transition3_____ +State3_____________ --> State2_____________ +State4_____________ --> State2_____________ : Transition5_____ +State4_____________ --> State5_____________ : Transition6_____ +State5_____________ --> State6_____________ : Transition7_____ +State6_____________ --> State4_____________ : Transition8_____ +State2_____________ --> State7_____________ : Transition4_____ +State4_____________ --> State7_____________ : Transition4_____ +State5_____________ --> State7_____________ : Transition4_____ +State6_____________ --> State7_____________ : Transition4_____ +State7_____________ --> State1_____________ : Transition9_____ +State5_____________ --> State8_____________ : Transition10____ +State8_____________ --> State5_____________ : Transition11____ +`, + {} + ); + }); + it('should render edge labels correctly with multiple transitions', () => { + imgSnapshotTest( + `--- +title: Multiple Transitions +config: + look: default + theme: default +--- + +stateDiagram-v2 + + state State1_____________ + { + c0 + } + + state State2_____________ + { + c1 + } + + state State3_____________ + { + c7 + } + + state State4_____________ + { + c2 + } + + state State5_____________ + { + c3 + } + + state State6_____________ + { + c4 + } + + state State7_____________ + { + c5 + } + + state State8_____________ + { + c6 + } + + state State9_____________ + { + c9 + } + +[*] --> State1_____________ +State1_____________ --> State2_____________ : Transition1_____ +State2_____________ --> State4_____________ : Transition2_____ +State2_____________ --> State3_____________ : Transition3_____ +State3_____________ --> State2_____________ +State4_____________ --> State2_____________ : Transition5_____ +State4_____________ --> State5_____________ : Transition6_____ +State5_____________ --> State6_____________ : Transition7_____ +State6_____________ --> State4_____________ : Transition8_____ +State2_____________ --> State7_____________ : Transition4_____ +State4_____________ --> State7_____________ : Transition4_____ +State5_____________ --> State7_____________ : Transition4_____ +State6_____________ --> State7_____________ : Transition4_____ +State7_____________ --> State1_____________ : Transition9_____ +State5_____________ --> State8_____________ : Transition10____ +State8_____________ --> State5_____________ : Transition11____ +State9_____________ --> State8_____________ : Transition12____ +`, + {} + ); + }); + + it('should render edge labels correctly with multiple states', () => { + imgSnapshotTest( + `--- +title: Multiple States +config: + look: default + theme: default +--- + +stateDiagram-v2 + + state State1_____________ + { + c0 + } + + state State2_____________ + { + c1 + } + + state State3_____________ + { + c7 + } + + state State4_____________ + { + c2 + } + + state State5_____________ + { + c3 + } + + state State6_____________ + { + c4 + } + + state State7_____________ + { + c5 + } + + state State8_____________ + { + c6 + } + + state State9_____________ + { + c9 + } + + state State10_____________ + { + c10 + } + +[*] --> State1_____________ +State1_____________ --> State2_____________ : Transition1_____ +State2_____________ --> State3_____________ : Transition2_____ +State3_____________ --> State4_____________ : Transition3_____ +State4_____________ --> State5_____________ : Transition4_____ +State5_____________ --> State6_____________ : Transition5_____ +State6_____________ --> State7_____________ : Transition6_____ +State7_____________ --> State8_____________ : Transition7_____ +State8_____________ --> State9_____________ : Transition8_____ +State9_____________ --> State10_____________ : Transition9_____ `, {} ); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js index a97668d5f..db48e313c 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js @@ -637,6 +637,11 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod log.info('arrowTypeEnd', edge.arrowTypeEnd); addEdgeMarkers(svgPath, edge, url, id, diagramType, strokeColor); + const midIndex = Math.floor(points.length / 2); + const point = points[midIndex]; + if (!utils.isLabelCoordinateInPath(point, svgPath.attr('d'))) { + pointsHasChanged = true; + } let paths = {}; if (pointsHasChanged) { diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index 6ed935cf6..0f1bfbecf 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -884,6 +884,7 @@ export default { runFunc, entityDecode, insertTitle, + isLabelCoordinateInPath, parseFontSize, InitIDGenerator, }; @@ -960,3 +961,23 @@ export function handleUndefinedAttr( ) { return attrValue ?? null; } + +/** + * Checks if the x or y coordinate of the edge label + * appears in the given SVG path data string. + * + * @param point - The Point object with x and y properties to check. + * @param dAttr - SVG path data string (the 'd' attribute of an SVG path element). + * @returns - True if the rounded x or y coordinate of the edge label is found + * in the sanitized path data string; otherwise, false. + */ +export function isLabelCoordinateInPath(point: Point, dAttr: string) { + const roundedX = Math.round(point.x); + const roundedY = Math.round(point.y); + + const sanitizedD = dAttr.replace(/(\d+\.\d+)/g, (match) => + Math.round(parseFloat(match)).toString() + ); + + return sanitizedD.includes(roundedX.toString()) || sanitizedD.includes(roundedY.toString()); +}