mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 06:19:24 +02:00
Merge develop
into release/11.5.0
This commit is contained in:
5
.changeset/vast-nails-stay.md
Normal file
5
.changeset/vast-nails-stay.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
The arrowhead color should match the color of the edge. Creates a unique clone of the arrow marker with the appropriate color.
|
@@ -917,4 +917,21 @@ graph TD
|
||||
}
|
||||
);
|
||||
});
|
||||
it('#6369: edge color should affect arrow head', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
flowchart LR
|
||||
A --> B
|
||||
A --> C
|
||||
C --> D
|
||||
|
||||
linkStyle 0 stroke:#D50000
|
||||
linkStyle 2 stroke:#D50000
|
||||
`,
|
||||
{
|
||||
flowchart: { htmlLabels: true },
|
||||
securityLevel: 'loose',
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -15,32 +15,33 @@ export const addEdgeMarkers = (
|
||||
edge: Pick<EdgeData, 'arrowTypeStart' | 'arrowTypeEnd'>,
|
||||
url: string,
|
||||
id: string,
|
||||
diagramType: string
|
||||
diagramType: string,
|
||||
strokeColor?: string
|
||||
) => {
|
||||
if (edge.arrowTypeStart) {
|
||||
addEdgeMarker(svgPath, 'start', edge.arrowTypeStart, url, id, diagramType);
|
||||
addEdgeMarker(svgPath, 'start', edge.arrowTypeStart, url, id, diagramType, strokeColor);
|
||||
}
|
||||
if (edge.arrowTypeEnd) {
|
||||
addEdgeMarker(svgPath, 'end', edge.arrowTypeEnd, url, id, diagramType);
|
||||
addEdgeMarker(svgPath, 'end', edge.arrowTypeEnd, url, id, diagramType, strokeColor);
|
||||
}
|
||||
};
|
||||
|
||||
const arrowTypesMap = {
|
||||
arrow_cross: 'cross',
|
||||
arrow_point: 'point',
|
||||
arrow_barb: 'barb',
|
||||
arrow_circle: 'circle',
|
||||
aggregation: 'aggregation',
|
||||
extension: 'extension',
|
||||
composition: 'composition',
|
||||
dependency: 'dependency',
|
||||
lollipop: 'lollipop',
|
||||
only_one: 'onlyOne',
|
||||
zero_or_one: 'zeroOrOne',
|
||||
one_or_more: 'oneOrMore',
|
||||
zero_or_more: 'zeroOrMore',
|
||||
requirement_arrow: 'requirement_arrow',
|
||||
requirement_contains: 'requirement_contains',
|
||||
arrow_cross: { type: 'cross', fill: false },
|
||||
arrow_point: { type: 'point', fill: true },
|
||||
arrow_barb: { type: 'barb', fill: true },
|
||||
arrow_circle: { type: 'circle', fill: false },
|
||||
aggregation: { type: 'aggregation', fill: false },
|
||||
extension: { type: 'extension', fill: false },
|
||||
composition: { type: 'composition', fill: true },
|
||||
dependency: { type: 'dependency', fill: true },
|
||||
lollipop: { type: 'lollipop', fill: false },
|
||||
only_one: { type: 'onlyOne', fill: false },
|
||||
zero_or_one: { type: 'zeroOrOne', fill: false },
|
||||
one_or_more: { type: 'oneOrMore', fill: false },
|
||||
zero_or_more: { type: 'zeroOrMore', fill: false },
|
||||
requirement_arrow: { type: 'requirement_arrow', fill: false },
|
||||
requirement_contains: { type: 'requirement_contains', fill: false },
|
||||
} as const;
|
||||
|
||||
const addEdgeMarker = (
|
||||
@@ -49,15 +50,55 @@ const addEdgeMarker = (
|
||||
arrowType: string,
|
||||
url: string,
|
||||
id: string,
|
||||
diagramType: string
|
||||
diagramType: string,
|
||||
strokeColor?: string
|
||||
) => {
|
||||
const endMarkerType = arrowTypesMap[arrowType as keyof typeof arrowTypesMap];
|
||||
const arrowTypeInfo = arrowTypesMap[arrowType as keyof typeof arrowTypesMap];
|
||||
|
||||
if (!endMarkerType) {
|
||||
if (!arrowTypeInfo) {
|
||||
log.warn(`Unknown arrow type: ${arrowType}`);
|
||||
return; // unknown arrow type, ignore
|
||||
}
|
||||
|
||||
const endMarkerType = arrowTypeInfo.type;
|
||||
const suffix = position === 'start' ? 'Start' : 'End';
|
||||
svgPath.attr(`marker-${position}`, `url(${url}#${id}_${diagramType}-${endMarkerType}${suffix})`);
|
||||
const originalMarkerId = `${id}_${diagramType}-${endMarkerType}${suffix}`;
|
||||
|
||||
// If stroke color is specified and non-empty, create or use a colored variant of the marker
|
||||
if (strokeColor && strokeColor.trim() !== '') {
|
||||
// Create a sanitized color value for use in IDs
|
||||
const colorId = strokeColor.replace(/[^\dA-Za-z]/g, '_');
|
||||
const coloredMarkerId = `${originalMarkerId}_${colorId}`;
|
||||
|
||||
// Check if the colored marker already exists
|
||||
if (!document.getElementById(coloredMarkerId)) {
|
||||
// Get the original marker
|
||||
const originalMarker = document.getElementById(originalMarkerId);
|
||||
if (originalMarker) {
|
||||
// Clone the marker and create colored version
|
||||
const coloredMarker = originalMarker.cloneNode(true) as Element;
|
||||
coloredMarker.id = coloredMarkerId;
|
||||
|
||||
// Apply colors to the paths inside the marker
|
||||
const paths = coloredMarker.querySelectorAll('path, circle, line');
|
||||
paths.forEach((path) => {
|
||||
path.setAttribute('stroke', strokeColor);
|
||||
|
||||
// Apply fill only to markers that should be filled
|
||||
if (arrowTypeInfo.fill) {
|
||||
path.setAttribute('fill', strokeColor);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the new colored marker to the defs section
|
||||
originalMarker.parentNode?.appendChild(coloredMarker);
|
||||
}
|
||||
}
|
||||
|
||||
// Use the colored marker
|
||||
svgPath.attr(`marker-${position}`, `url(${url}#${coloredMarkerId})`);
|
||||
} else {
|
||||
// Always use the original marker for unstyled edges
|
||||
svgPath.attr(`marker-${position}`, `url(${url}#${originalMarkerId})`);
|
||||
}
|
||||
};
|
||||
|
@@ -521,6 +521,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
||||
let svgPath;
|
||||
let linePath = lineFunction(lineData);
|
||||
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
||||
let strokeColor = edgeStyles.find((style) => style.startsWith('stroke:'));
|
||||
|
||||
if (edge.look === 'handDrawn') {
|
||||
const rc = rough.svg(elem);
|
||||
@@ -551,18 +552,18 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
||||
if (edge.animation) {
|
||||
animationClass = ' edge-animation-' + edge.animation;
|
||||
}
|
||||
|
||||
const pathStyle = stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles;
|
||||
svgPath = elem
|
||||
.append('path')
|
||||
.attr('d', linePath)
|
||||
.attr('id', edge.id)
|
||||
.attr(
|
||||
'class',
|
||||
' ' +
|
||||
strokeClasses +
|
||||
(edge.classes ? ' ' + edge.classes : '') +
|
||||
(animationClass ? animationClass : '')
|
||||
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '')
|
||||
)
|
||||
.attr('style', stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles);
|
||||
.attr('style', pathStyle);
|
||||
strokeColor = pathStyle.match(/stroke:([^;]+)/)?.[1];
|
||||
}
|
||||
|
||||
// DEBUG code, DO NOT REMOVE
|
||||
@@ -599,7 +600,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
||||
log.info('arrowTypeStart', edge.arrowTypeStart);
|
||||
log.info('arrowTypeEnd', edge.arrowTypeEnd);
|
||||
|
||||
addEdgeMarkers(svgPath, edge, url, id, diagramType);
|
||||
addEdgeMarkers(svgPath, edge, url, id, diagramType, strokeColor);
|
||||
|
||||
let paths = {};
|
||||
if (pointsHasChanged) {
|
||||
|
Reference in New Issue
Block a user