• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mermaid-js / mermaid / 4608236643

pending completion
4608236643

push

github

Knut Sveidqvist
Merge branch 'release/10.1.0'

1643 of 1996 branches covered (82.31%)

Branch coverage included in aggregate %.

801 of 801 new or added lines in 37 files covered. (100.0%)

16190 of 33430 relevant lines covered (48.43%)

403.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

27.68
/packages/mermaid/src/rendering-util/createText.js
1
import { select } from 'd3';
1✔
2
import { log } from '../logger';
1✔
3
import { getConfig } from '../config';
1✔
4
import { evaluate } from '../diagrams/common/common';
1✔
5
import { decodeEntities } from '../mermaidAPI';
1✔
6
import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text';
1✔
7
/**
1✔
8
 * @param dom
1✔
9
 * @param styleFn
1✔
10
 */
1✔
11
function applyStyle(dom, styleFn) {
×
12
  if (styleFn) {
×
13
    dom.attr('style', styleFn);
×
14
  }
×
15
}
×
16

1✔
17
/**
1✔
18
 * @param element
1✔
19
 * @param {any} node
1✔
20
 * @param width
1✔
21
 * @param classes
1✔
22
 * @returns {SVGForeignObjectElement} Node
1✔
23
 */
1✔
24
function addHtmlSpan(element, node, width, classes) {
×
25
  const fo = element.append('foreignObject');
×
26
  // const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
×
27
  // const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
×
28
  const div = fo.append('xhtml:div');
×
29
  // const div = body.append('div');
×
30
  // const div = fo.append('div');
×
31

×
32
  const label = node.label;
×
33
  const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
×
34
  div.html(
×
35
    `<span class="${labelClass} ${classes}" ` +
×
36
      (node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
×
37
      '>' +
×
38
      label +
×
39
      '</span>'
×
40
  );
×
41

×
42
  applyStyle(div, node.labelStyle);
×
43
  div.style('display', 'table-cell');
×
44
  div.style('white-space', 'nowrap');
×
45
  div.style('max-width', width + 'px');
×
46
  div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
×
47

×
48
  let bbox = div.node().getBoundingClientRect();
×
49
  if (bbox.width === width) {
×
50
    div.style('display', 'table');
×
51
    div.style('white-space', 'break-spaces');
×
52
    div.style('width', width + 'px');
×
53
    bbox = div.node().getBoundingClientRect();
×
54
  }
×
55

×
56
  fo.style('width', bbox.width);
×
57
  fo.style('height', bbox.height);
×
58

×
59
  return fo.node();
×
60
}
×
61

1✔
62
/**
1✔
63
 * Creates a tspan element with the specified attributes for text positioning.
1✔
64
 *
1✔
65
 * @param {object} textElement - The parent text element to append the tspan element.
1✔
66
 * @param {number} lineIndex - The index of the current line in the structuredText array.
1✔
67
 * @param {number} lineHeight - The line height value for the text.
1✔
68
 * @returns {object} The created tspan element.
1✔
69
 */
1✔
70
function createTspan(textElement, lineIndex, lineHeight) {
×
71
  return textElement
×
72
    .append('tspan')
×
73
    .attr('class', 'text-outer-tspan')
×
74
    .attr('x', 0)
×
75
    .attr('y', lineIndex * lineHeight - 0.1 + 'em')
×
76
    .attr('dy', lineHeight + 'em');
×
77
}
×
78

1✔
79
/**
1✔
80
 * Creates a formatted text element by breaking lines and applying styles based on
1✔
81
 * the given structuredText.
1✔
82
 *
1✔
83
 * @param {number} width - The maximum allowed width of the text.
1✔
84
 * @param {object} g - The parent group element to append the formatted text.
1✔
85
 * @param {Array} structuredText - The structured text data to format.
1✔
86
 * @param addBackground
1✔
87
 */
1✔
88
function createFormattedText(width, g, structuredText, addBackground = false) {
×
89
  const lineHeight = 1.1;
×
90
  const labelGroup = g.append('g');
×
91
  let bkg = labelGroup.insert('rect').attr('class', 'background');
×
92
  const textElement = labelGroup.append('text').attr('y', '-10.1');
×
93
  // .attr('dominant-baseline', 'middle')
×
94
  // .attr('text-anchor', 'middle');
×
95
  // .attr('text-anchor', 'middle');
×
96
  let lineIndex = -1;
×
97
  structuredText.forEach((line) => {
×
98
    lineIndex++;
×
99
    let tspan = createTspan(textElement, lineIndex, lineHeight);
×
100

×
101
    let words = [...line].reverse();
×
102
    let currentWord;
×
103
    let wrappedLine = [];
×
104

×
105
    while (words.length) {
×
106
      currentWord = words.pop();
×
107
      wrappedLine.push(currentWord);
×
108

×
109
      updateTextContentAndStyles(tspan, wrappedLine);
×
110

×
111
      if (tspan.node().getComputedTextLength() > width) {
×
112
        wrappedLine.pop();
×
113
        words.push(currentWord);
×
114

×
115
        updateTextContentAndStyles(tspan, wrappedLine);
×
116

×
117
        wrappedLine = [];
×
118
        lineIndex++;
×
119
        tspan = createTspan(textElement, lineIndex, lineHeight);
×
120
      }
×
121
    }
×
122
  });
×
123
  if (addBackground) {
×
124
    const bbox = textElement.node().getBBox();
×
125
    const padding = 2;
×
126
    bkg
×
127
      .attr('x', -padding)
×
128
      .attr('y', -padding)
×
129
      .attr('width', bbox.width + 2 * padding)
×
130
      .attr('height', bbox.height + 2 * padding);
×
131
    // .style('fill', 'red');
×
132

×
133
    return labelGroup.node();
×
134
  } else {
×
135
    return textElement.node();
×
136
  }
×
137
}
×
138

1✔
139
/**
1✔
140
 * Updates the text content and styles of the given tspan element based on the
1✔
141
 * provided wrappedLine data.
1✔
142
 *
1✔
143
 * @param {object} tspan - The tspan element to update.
1✔
144
 * @param {Array} wrappedLine - The line data to apply to the tspan element.
1✔
145
 */
1✔
146
function updateTextContentAndStyles(tspan, wrappedLine) {
×
147
  tspan.text('');
×
148

×
149
  wrappedLine.forEach((word, index) => {
×
150
    const innerTspan = tspan
×
151
      .append('tspan')
×
152
      .attr('font-style', word.type === 'em' ? 'italic' : 'normal')
×
153
      .attr('class', 'text-inner-tspan')
×
154
      .attr('font-weight', word.type === 'strong' ? 'bold' : 'normal');
×
155
    const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
×
156
    if (index === 0) {
×
157
      innerTspan.text(word.content);
×
158
    } else {
×
159
      innerTspan.text(' ' + word.content);
×
160
    }
×
161
  });
×
162
}
×
163

1✔
164
/**
1✔
165
 *
1✔
166
 * @param el
1✔
167
 * @param {*} text
1✔
168
 * @param {*} param1
1✔
169
 * @param root0
1✔
170
 * @param root0.style
1✔
171
 * @param root0.isTitle
1✔
172
 * @param root0.classes
1✔
173
 * @param root0.useHtmlLabels
1✔
174
 * @param root0.isNode
1✔
175
 * @returns
1✔
176
 */
1✔
177
// Note when using from flowcharts converting the API isNode means classes should be set accordingly. When using htmlLabels => to sett classes to'nodeLabel' when isNode=true otherwise 'edgeLabel'
1✔
178
// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row'
1✔
179
export const createText = (
1✔
180
  el,
×
181
  text = '',
×
182
  {
×
183
    style = '',
×
184
    isTitle = false,
×
185
    classes = '',
×
186
    useHtmlLabels = true,
×
187
    isNode = true,
×
188
    width,
×
189
    addSvgBackground = false,
×
190
  } = {}
×
191
) => {
×
192
  log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground);
×
193
  if (useHtmlLabels) {
×
194
    // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
×
195
    // text = text.replace(/\\n|\n/g, '<br />');
×
196
    const htmlText = markdownToHTML(text);
×
197
    // log.info('markdo  wnToHTML' + text, markdownToHTML(text));
×
198
    const node = {
×
199
      isNode,
×
200
      label: decodeEntities(htmlText).replace(
×
201
        /fa[blrs]?:fa-[\w-]+/g,
×
202
        (s) => `<i class='${s.replace(':', ' ')}'></i>`
×
203
      ),
×
204
      labelStyle: style.replace('fill:', 'color:'),
×
205
    };
×
206
    let vertexNode = addHtmlSpan(el, node, width, classes);
×
207
    return vertexNode;
×
208
  } else {
×
209
    const structuredText = markdownToLines(text);
×
210
    const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
×
211
    let lastWord;
×
212
    structuredText.forEach((line) => {
×
213
      line.forEach((word) => {
×
214
        if (special.includes(word.content) && lastWord) {
×
215
          lastWord.content += word.content;
×
216
          word.content = '';
×
217
        }
×
218
        lastWord = word;
×
219
      });
×
220
    });
×
221
    const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
×
222
    return svgLabel;
×
223
  }
×
224
};
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc