mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-22 12:16:22 +01:00
Compare commits
5 Commits
fix/treema
...
fix/flowch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eb76c7c85 | ||
|
|
705a7aea50 | ||
|
|
86b1f8852c | ||
|
|
8eacf42fe4 | ||
|
|
373ebc8fd6 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Ensure treemap labels render correctly in large nested diagrams
|
||||
@@ -468,65 +468,4 @@ classDef highlight fill:#f39c12,color:#000,stroke:#e67e22,stroke-width:2px;
|
||||
);
|
||||
});
|
||||
*/
|
||||
|
||||
it('14: should render labels in complex treemap with many nested blocks', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"🔴 High Activity (Top 50%)":::redContainer
|
||||
"packages/app (1115)": 15:::redleaf
|
||||
"packages/app/src (844)": 12:::redleaf
|
||||
"packages/app/src/lib (707)": 11:::redleaf
|
||||
"packages/app/src/routes (353)": 7:::redleaf
|
||||
"packages/app/tests (277)": 6:::redleaf
|
||||
"packages/app/tests/e2e (245)": 6:::redleaf
|
||||
"packages/app/tests/common (48)": 4:::redleaf
|
||||
"packages/llm-prompts (29)": 3:::redleaf
|
||||
"packages/emails (26)": 3:::redleaf
|
||||
"packages/app/tests/api (23)": 3:::redleaf
|
||||
"packages/emails/src (18)": 3:::redleaf
|
||||
"packages/emails/src/emails (17)": 3:::redleaf
|
||||
"packages/app/static (15)": 3:::redleaf
|
||||
"packages/llm-prompts/diagram-chat (15)": 3:::redleaf
|
||||
"packages/app/prisma (11)": 3:::redleaf
|
||||
"packages/app/prisma/migrations (11)": 3:::redleaf
|
||||
"packages/llm-prompts/shared (9)": 3:::redleaf
|
||||
"packages/app/static/icons (8)": 3:::redleaf
|
||||
"packages/emails/src/components (8)": 3:::redleaf
|
||||
"packages/app-observability (8)": 3:::redleaf
|
||||
"packages/mermaid-pre-render-server (8)": 3:::redleaf
|
||||
"🟠 Medium Activity (35%)":::orangeContainer
|
||||
"packages/app/tests/mobile (6)": 3:::orangeleaf
|
||||
"packages/llm-prompts/diagram-chat/tests (6)": 3:::orangeleaf
|
||||
"packages/eslint-plugin (6)": 3:::orangeleaf
|
||||
"packages/eslint-plugin/src (5)": 3:::orangeleaf
|
||||
"packages/eslint-plugin/src/rules (5)": 3:::orangeleaf
|
||||
"packages/app/static/img (4)": 3:::orangeleaf
|
||||
"packages/llm-prompts/tests (4)": 3:::orangeleaf
|
||||
"packages/llm-prompts/common (3)": 3:::orangeleaf
|
||||
"packages/mermaid-pre-render-server/tests (3)": 3:::orangeleaf
|
||||
"packages/mermaid-pre-render-server/tests/e2e (3)": 3:::orangeleaf
|
||||
"packages/app/tests/seed (2)": 3:::orangeleaf
|
||||
"packages/llm-prompts/shared/patches (2)": 3:::orangeleaf
|
||||
"packages/mermaid-pre-render-server/src (2)": 3:::orangeleaf
|
||||
"packages/app/scripts (1)": 3:::orangeleaf
|
||||
"🟡 Low Activity (15%)":::yellowContainer
|
||||
"packages/llm-prompts/regenerate-diagram (1)": 3:::yellowleaf
|
||||
"packages/llm-prompts/repair-diagram (1)": 3:::yellowleaf
|
||||
"packages/app-buildship (1)": 3:::yellowleaf
|
||||
"packages/app-buildship/src (1)": 3:::yellowleaf
|
||||
"packages/app-buildship/src/cleanupAssets (1)": 3:::yellowleaf
|
||||
"packages/icons (1)": 3:::yellowleaf
|
||||
|
||||
classDef root fill:#F5BA71
|
||||
classDef redContainer fill:#F54927
|
||||
classDef orangeContainer fill:#DE8118
|
||||
classDef yellowContainer fill:#F5E514
|
||||
|
||||
classDef redleaf fill:#F54927,stroke:none,color:#FFFFFF
|
||||
classDef orangeleaf fill:#DE8118,stroke:none,color:#FFFFFF
|
||||
classDef yellowleaf fill:#F5E514,stroke:none,color:#333333
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: ParseOptions
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
|
||||
Defined in: [packages/mermaid/src/types.ts:90](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L90)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mer
|
||||
|
||||
> `optional` **suppressErrors**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L94)
|
||||
Defined in: [packages/mermaid/src/types.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L95)
|
||||
|
||||
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
|
||||
The `parseError` function will not be called.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: ParseResult
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L97)
|
||||
Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mer
|
||||
|
||||
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L105)
|
||||
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106)
|
||||
|
||||
The config passed as YAML frontmatter or directives
|
||||
|
||||
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
|
||||
|
||||
> **diagramType**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L101)
|
||||
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102)
|
||||
|
||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: RenderResult
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L115)
|
||||
Defined in: [packages/mermaid/src/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L116)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/me
|
||||
|
||||
> `optional` **bindFunctions**: (`element`) => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:133](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L133)
|
||||
Defined in: [packages/mermaid/src/types.ts:134](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L134)
|
||||
|
||||
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.
|
||||
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
||||
|
||||
> **diagramType**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L123)
|
||||
Defined in: [packages/mermaid/src/types.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L124)
|
||||
|
||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
||||
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
||||
> **svg**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:119](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L119)
|
||||
Defined in: [packages/mermaid/src/types.ts:120](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L120)
|
||||
|
||||
The svg code for the rendered graph.
|
||||
|
||||
@@ -85,6 +85,17 @@ export class FlowDB implements DiagramDB {
|
||||
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.
|
||||
*
|
||||
@@ -208,6 +219,7 @@ export class FlowDB implements DiagramDB {
|
||||
|
||||
if (doc?.label) {
|
||||
vertex.text = doc?.label;
|
||||
vertex.labelType = this.sanitizeNodeLabelType(doc?.labelType);
|
||||
}
|
||||
if (doc?.icon) {
|
||||
vertex.icon = doc?.icon;
|
||||
@@ -267,7 +279,7 @@ export class FlowDB implements DiagramDB {
|
||||
if (edge.text.startsWith('"') && edge.text.endsWith('"')) {
|
||||
edge.text = edge.text.substring(1, edge.text.length - 1);
|
||||
}
|
||||
edge.labelType = linkTextObj.type;
|
||||
edge.labelType = this.sanitizeNodeLabelType(linkTextObj.type);
|
||||
}
|
||||
|
||||
if (type !== undefined) {
|
||||
@@ -702,7 +714,7 @@ You have to call mermaid.initialize.`
|
||||
title: title.trim(),
|
||||
classes: [],
|
||||
dir,
|
||||
labelType: _title.type,
|
||||
labelType: this.sanitizeNodeLabelType(_title?.type),
|
||||
};
|
||||
|
||||
log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
|
||||
@@ -1012,6 +1024,7 @@ You have to call mermaid.initialize.`
|
||||
const baseNode = {
|
||||
id: vertex.id,
|
||||
label: vertex.text,
|
||||
labelType: vertex.labelType,
|
||||
labelStyle: '',
|
||||
parentId,
|
||||
padding: config.flowchart?.padding || 8,
|
||||
@@ -1088,6 +1101,7 @@ You have to call mermaid.initialize.`
|
||||
id: subGraph.id,
|
||||
label: subGraph.title,
|
||||
labelStyle: '',
|
||||
labelType: subGraph.labelType,
|
||||
parentId: parentDB.get(subGraph.id),
|
||||
padding: 8,
|
||||
cssCompiledStyles: this.getCompiledStyles(subGraph.classes),
|
||||
@@ -1119,6 +1133,7 @@ You have to call mermaid.initialize.`
|
||||
end: rawEdge.end,
|
||||
type: rawEdge.type ?? 'normal',
|
||||
label: rawEdge.text,
|
||||
labelType: rawEdge.labelType,
|
||||
labelpos: 'c',
|
||||
thickness: rawEdge.stroke,
|
||||
minlen: rawEdge.length,
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface FlowVertex {
|
||||
domId: string;
|
||||
haveCallback?: boolean;
|
||||
id: string;
|
||||
labelType: 'text';
|
||||
labelType: 'markdown' | 'string' | 'text';
|
||||
link?: string;
|
||||
linkTarget?: string;
|
||||
props?: any;
|
||||
@@ -62,7 +62,7 @@ export interface FlowEdge {
|
||||
style?: string[];
|
||||
length?: number;
|
||||
text: string;
|
||||
labelType: 'text';
|
||||
labelType: 'markdown' | 'string' | 'text';
|
||||
classes: string[];
|
||||
id?: string;
|
||||
animation?: 'fast' | 'slow';
|
||||
|
||||
@@ -224,7 +224,6 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.text((d) => (d.depth === 0 ? '' : d.data.name)) // Skip label for root section
|
||||
.attr('font-weight', 'bold')
|
||||
.attr('clip-path', (_d, i) => `url(#clip-section-${id}-${i})`) // Apply clip-path to prevent overflow
|
||||
.attr('style', (d) => {
|
||||
// Hide the label for the root section
|
||||
if (d.depth === 0) {
|
||||
@@ -310,17 +309,6 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
|
||||
// Draw the leaf nodes
|
||||
const leafNodes = treemapData.leaves();
|
||||
|
||||
const isComplexTreemap = leafNodes.length > 20;
|
||||
|
||||
const baseLabelFontSize = isComplexTreemap ? 16 : 38;
|
||||
const baseValueFontSize = isComplexTreemap ? 14 : 28;
|
||||
const minLabelFontSize = isComplexTreemap ? 4 : 8;
|
||||
const minValueFontSize = isComplexTreemap ? 4 : 6;
|
||||
const labelPadding = isComplexTreemap ? 2 : 4;
|
||||
const minDisplayThreshold = isComplexTreemap ? 8 : 10;
|
||||
const spacingBetweenLabelAndValue = isComplexTreemap ? 1 : 2;
|
||||
|
||||
const cell = g
|
||||
.selectAll('.treemapLeafGroup')
|
||||
.data(leafNodes)
|
||||
@@ -371,7 +359,7 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
// .style('fill', (d) => colorScaleLabel(d.data.name))
|
||||
.attr('style', (d) => {
|
||||
const labelStyles =
|
||||
`text-anchor: middle; dominant-baseline: middle; font-size: ${baseLabelFontSize}px;fill:` +
|
||||
'text-anchor: middle; dominant-baseline: middle; font-size: 38px;fill:' +
|
||||
colorScaleLabel(d.data.name) +
|
||||
';';
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
@@ -386,16 +374,21 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
const nodeHeight = d.y1 - d.y0;
|
||||
const textNode = self.node()!;
|
||||
|
||||
const availableWidth = nodeWidth - 2 * labelPadding;
|
||||
const availableHeight = nodeHeight - 2 * labelPadding;
|
||||
const padding = 4;
|
||||
const availableWidth = nodeWidth - 2 * padding;
|
||||
const availableHeight = nodeHeight - 2 * padding;
|
||||
|
||||
if (availableWidth < minDisplayThreshold || availableHeight < minDisplayThreshold) {
|
||||
if (availableWidth < 10 || availableHeight < 10) {
|
||||
self.style('display', 'none');
|
||||
return;
|
||||
}
|
||||
|
||||
let currentLabelFontSize = parseInt(self.style('font-size'), 10);
|
||||
const minLabelFontSize = 8;
|
||||
const originalValueRelFontSize = 28; // Original font size of value, for max cap
|
||||
const valueScaleFactor = 0.6; // Value font size as a factor of label font size
|
||||
const minValueFontSize = 6;
|
||||
const spacingBetweenLabelAndValue = 2;
|
||||
|
||||
// 1. Adjust label font size to fit width
|
||||
while (
|
||||
@@ -409,7 +402,7 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
// 2. Adjust both label and prospective value font size to fit combined height
|
||||
let prospectiveValueFontSize = Math.max(
|
||||
minValueFontSize,
|
||||
Math.min(baseValueFontSize, Math.round(currentLabelFontSize * valueScaleFactor))
|
||||
Math.min(originalValueRelFontSize, Math.round(currentLabelFontSize * valueScaleFactor))
|
||||
);
|
||||
let combinedHeight =
|
||||
currentLabelFontSize + spacingBetweenLabelAndValue + prospectiveValueFontSize;
|
||||
@@ -418,7 +411,7 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
currentLabelFontSize--;
|
||||
prospectiveValueFontSize = Math.max(
|
||||
minValueFontSize,
|
||||
Math.min(baseValueFontSize, Math.round(currentLabelFontSize * valueScaleFactor))
|
||||
Math.min(originalValueRelFontSize, Math.round(currentLabelFontSize * valueScaleFactor))
|
||||
);
|
||||
if (
|
||||
prospectiveValueFontSize < minValueFontSize &&
|
||||
@@ -439,18 +432,13 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
self.style('font-size', `${currentLabelFontSize}px`);
|
||||
|
||||
// 3. Final visibility check for the label
|
||||
if (isComplexTreemap) {
|
||||
if (currentLabelFontSize < minLabelFontSize || availableHeight < minLabelFontSize) {
|
||||
self.style('display', 'none');
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
textNode.getComputedTextLength() > availableWidth ||
|
||||
currentLabelFontSize < minLabelFontSize ||
|
||||
availableHeight < currentLabelFontSize
|
||||
) {
|
||||
self.style('display', 'none');
|
||||
}
|
||||
if (
|
||||
textNode.getComputedTextLength() > availableWidth ||
|
||||
currentLabelFontSize < minLabelFontSize ||
|
||||
availableHeight < currentLabelFontSize
|
||||
) {
|
||||
self.style('display', 'none');
|
||||
// If label is hidden, value will be hidden by its own .each() loop
|
||||
}
|
||||
});
|
||||
|
||||
@@ -466,7 +454,7 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
})
|
||||
.attr('style', (d) => {
|
||||
const labelStyles =
|
||||
`text-anchor: middle; dominant-baseline: hanging; font-size: ${baseValueFontSize}px;fill:` +
|
||||
'text-anchor: middle; dominant-baseline: hanging; font-size: 28px;fill:' +
|
||||
colorScaleLabel(d.data.name) +
|
||||
';';
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
@@ -493,11 +481,14 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
}
|
||||
|
||||
const finalLabelFontSize = parseFloat(labelElement.style('font-size'));
|
||||
const originalValueFontSize = 28; // From initial style setting
|
||||
const valueScaleFactor = 0.6;
|
||||
const minValueFontSize = 6;
|
||||
const spacingBetweenLabelAndValue = 2;
|
||||
|
||||
const actualValueFontSize = Math.max(
|
||||
minValueFontSize,
|
||||
Math.min(baseValueFontSize, Math.round(finalLabelFontSize * valueScaleFactor))
|
||||
Math.min(originalValueFontSize, Math.round(finalLabelFontSize * valueScaleFactor))
|
||||
);
|
||||
valueTextElement.style('font-size', `${actualValueFontSize}px`);
|
||||
|
||||
@@ -509,7 +500,7 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
const nodeTotalHeight = d.y1 - d.y0;
|
||||
const cellBottomPadding = 4;
|
||||
const maxValueBottomY = nodeTotalHeight - cellBottomPadding;
|
||||
const availableWidthForValue = nodeWidth - 2 * labelPadding;
|
||||
const availableWidthForValue = nodeWidth - 2 * 4; // padding for value text
|
||||
|
||||
if (
|
||||
valueTextElement.node()!.getComputedTextLength() > availableWidthForValue ||
|
||||
|
||||
@@ -13,7 +13,7 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
||||
_classes?: string
|
||||
) => {
|
||||
let cssClasses;
|
||||
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.htmlLabels);
|
||||
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels);
|
||||
if (!_classes) {
|
||||
cssClasses = 'node default';
|
||||
} else {
|
||||
@@ -48,6 +48,7 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
||||
style: node.labelStyle,
|
||||
addSvgBackground: !!node.icon || !!node.img,
|
||||
});
|
||||
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
const halfPadding = (node?.padding ?? 0) / 2;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export interface NodeMetaData {
|
||||
shape?: string;
|
||||
label?: string;
|
||||
labelType?: string;
|
||||
icon?: string;
|
||||
form?: string;
|
||||
pos?: 't' | 'b';
|
||||
|
||||
Reference in New Issue
Block a user