Merge branch 'develop' into origin/3258_Flowchart_nodeSpacing_Subgraph

This commit is contained in:
Sidharth Vinod 2024-03-23 17:09:50 +05:30 committed by GitHub
commit b78f16ef19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 134 additions and 45 deletions

View File

@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
NODE_OPTIONS=--max_old_space_size=8192 pnpm run pre-commit
NODE_OPTIONS="--max_old_space_size=8192" pnpm run pre-commit

View File

@ -926,6 +926,18 @@ end
);
});
});
it('should not auto wrap when markdownAutoWrap is false', () => {
imgSnapshotTest(
`flowchart TD
angular_velocity["\`**angular_velocity**
*angular_displacement / duration*
[rad/s, 1/s]
{vector}\`"]
frequency["frequency\n(1 / period_duration)\n[Hz, 1/s]"]`,
{ markdownAutoWrap: false }
);
});
});
describe('Subgraph title margins', () => {
it('Should render subgraphs with title margins set (LR)', () => {

View File

@ -50,7 +50,7 @@ Pushes in a directive to the configuration
| --------- | ------------------------- | ----------- | ------------------------------ |
| getConfig | Obtains the currentConfig | Get Request | Any Values from current Config |
**Notes**: Returns **any** the currentConfig
**Notes**: Avoid calling this function repeatedly. Instead, store the result in a variable and use it, and pass it down to function calls.
#### Returns

View File

@ -852,6 +852,16 @@ Formatting:
This feature is applicable to node labels, edge labels, and subgraph labels.
The auto wrapping can be disabled by using
```
---
config:
markdownAutoWrap: false
---
graph LR
```
## Interaction
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab.

View File

@ -124,7 +124,7 @@ export const setConfig = (conf: MermaidConfig): MermaidConfig => {
* | --------- | ------------------------- | ----------- | ------------------------------ |
* | getConfig | Obtains the currentConfig | Get Request | Any Values from current Config |
*
* **Notes**: Returns **any** the currentConfig
* **Notes**: Avoid calling this function repeatedly. Instead, store the result in a variable and use it, and pass it down to function calls.
*
* @returns The currentConfig
*/

View File

@ -159,6 +159,7 @@ export interface MermaidConfig {
dompurifyConfig?: DOMPurifyConfiguration;
wrap?: boolean;
fontSize?: number;
markdownAutoWrap?: boolean;
/**
* Suppresses inserting 'Syntax error' diagram in the DOM.
* This is useful when you want to control how to handle syntax errors in your application.

View File

@ -30,7 +30,7 @@ const rect = (parent, node) => {
// .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
const text =
node.labelType === 'markdown'
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels })
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels }, siteConfig)
: label.node().appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
// Get the size of the label

View File

@ -18,15 +18,21 @@ export const clear = () => {
};
export const insertEdgeLabel = (elem, edge) => {
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
const config = getConfig();
const useHtmlLabels = evaluate(config.flowchart.htmlLabels);
// Create the actual text element
const labelElement =
edge.labelType === 'markdown'
? createText(elem, edge.label, {
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
})
? createText(
elem,
edge.label,
{
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
},
config
)
: createLabel(edge.label, edge.labelStyle);
// Create outer g, edgeLabel, this will be positioned after graph layout

View File

@ -6,8 +6,9 @@ import { evaluate, sanitizeText } from '../../diagrams/common/common.js';
import { decodeEntities } from '../../utils.js';
export const labelHelper = async (parent, node, _classes, isNode) => {
const config = getConfig();
let classes;
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
const useHtmlLabels = node.useHtmlLabels || evaluate(config.flowchart.htmlLabels);
if (!_classes) {
classes = 'node default';
} else {
@ -35,26 +36,26 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
let text;
if (node.labelType === 'markdown') {
// text = textNode;
text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
useHtmlLabels,
width: node.width || getConfig().flowchart.wrappingWidth,
classes: 'markdown-node-label',
});
text = createText(
label,
sanitizeText(decodeEntities(labelText), config),
{
useHtmlLabels,
width: node.width || config.flowchart.wrappingWidth,
classes: 'markdown-node-label',
},
config
);
} else {
text = textNode.appendChild(
createLabel(
sanitizeText(decodeEntities(labelText), getConfig()),
node.labelStyle,
false,
isNode
)
createLabel(sanitizeText(decodeEntities(labelText), config), node.labelStyle, false, isNode)
);
}
// Get the size of the label
let bbox = text.getBBox();
const halfPadding = node.padding / 2;
if (evaluate(getConfig().flowchart.htmlLabels)) {
if (evaluate(config.flowchart.htmlLabels)) {
const div = text.children[0];
const dv = select(text);
@ -76,8 +77,8 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
if (noImgText) {
// default size if no text
const bodyFontSize = getConfig().fontSize
? getConfig().fontSize
const bodyFontSize = config.fontSize
? config.fontSize
: window.getComputedStyle(document.body).fontSize;
const enlargingFactor = 5;
const width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';

View File

@ -346,11 +346,9 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
.split(lineBreakRegex)
.map((line) =>
hasKatex(line)
? `
<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
${line}
</div>
`
</div>`
: `<div>${line}</div>`
)
.join('')

View File

@ -196,11 +196,16 @@ export const drawNode = function (
// Create the wrapped text element
const textElem = nodeElem.append('g');
const description = node.descr.replace(/(<br\/*>)/g, '\n');
const newEl = createText(textElem, description, {
useHtmlLabels: htmlLabels,
width: node.width,
classes: 'mindmap-node-label',
});
const newEl = createText(
textElem,
description,
{
useHtmlLabels: htmlLabels,
width: node.width,
classes: 'mindmap-node-label',
},
conf
);
if (!htmlLabels) {
textElem

View File

@ -537,6 +537,16 @@ Formatting:
This feature is applicable to node labels, edge labels, and subgraph labels.
The auto wrapping can be disabled by using
```
---
config:
markdownAutoWrap: false
---
graph LR
```
## Interaction
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab.

View File

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck TODO: Fix types
import type { MermaidConfig } from '../config.type.js';
import type { Group } from '../diagram-api/types.js';
import type { D3TSpanElement, D3TextElement } from '../diagrams/common/commonTypes.js';
import { log } from '../logger.js';
@ -21,8 +22,7 @@ function addHtmlSpan(element, node, width, classes, addBackground = false) {
const label = node.label;
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
div.html(
`
<span class="${labelClass} ${classes}" ` +
`<span class="${labelClass} ${classes}" ` +
(node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
'>' +
label +
@ -181,14 +181,14 @@ export const createText = (
isNode = true,
width = 200,
addSvgBackground = false,
} = {}
} = {},
config: MermaidConfig
) => {
log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground);
if (useHtmlLabels) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
// text = text.replace(/\\n|\n/g, '<br />');
const htmlText = markdownToHTML(text);
// log.info('markdownToHTML' + text, markdownToHTML(text));
const htmlText = markdownToHTML(text, config);
const node = {
isNode,
label: decodeEntities(htmlText).replace(
@ -200,7 +200,7 @@ export const createText = (
const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
return vertexNode;
} else {
const structuredText = markdownToLines(text);
const structuredText = markdownToLines(text, config);
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
return svgLabel;
}

View File

@ -1,3 +1,4 @@
/* eslint-disable no-irregular-whitespace */
import { markdownToLines, markdownToHTML } from './handle-markdown-text.js';
import { test, expect } from 'vitest';
@ -203,6 +204,31 @@ Word!`;
expect(output).toEqual(expectedOutput);
});
test('markdownToLines - No auto wrapping', () => {
expect(
markdownToLines(
`Hello, how do
you do?`,
{ markdownAutoWrap: false }
)
).toMatchInlineSnapshot(`
[
[
{
"content": "Hello, how do",
"type": "normal",
},
],
[
{
"content": "you do?",
"type": "normal",
},
],
]
`);
});
test('markdownToHTML - Basic test', () => {
const input = `This is regular text
Here is a new line
@ -262,3 +288,13 @@ test('markdownToHTML - Unsupported formatting', () => {
- l3`)
).toMatchInlineSnapshot('"<p>Hello</p>Unsupported markdown: list"');
});
test('markdownToHTML - no auto wrapping', () => {
expect(
markdownToHTML(
`Hello, how do
you do?`,
{ markdownAutoWrap: false }
)
).toMatchInlineSnapshot('"<p>Hello,&nbsp;how&nbsp;do<br/>you&nbsp;do?</p>"');
});

View File

@ -2,24 +2,28 @@ import type { Content } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { dedent } from 'ts-dedent';
import type { MarkdownLine, MarkdownWordType } from './types.js';
import type { MermaidConfig } from '../config.type.js';
/**
* @param markdown - markdown to process
* @returns processed markdown
*/
function preprocessMarkdown(markdown: string): string {
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string {
// Replace multiple newlines with a single newline
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
// Remove extra spaces at the beginning of each line
const withoutExtraSpaces = dedent(withoutMultipleNewlines);
if (markdownAutoWrap === false) {
return withoutExtraSpaces.replace(/ /g, '&nbsp;');
}
return withoutExtraSpaces;
}
/**
* @param markdown - markdown to split into lines
*/
export function markdownToLines(markdown: string): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown);
export function markdownToLines(markdown: string, config: MermaidConfig = {}): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown, config);
const { children } = fromMarkdown(preprocessedMarkdown);
const lines: MarkdownLine[] = [[]];
let currentLine = 0;
@ -56,11 +60,14 @@ export function markdownToLines(markdown: string): MarkdownLine[] {
return lines;
}
export function markdownToHTML(markdown: string) {
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidConfig = {}) {
const { children } = fromMarkdown(markdown);
function output(node: Content): string {
if (node.type === 'text') {
if (markdownAutoWrap === false) {
return node.value.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;');
}
return node.value.replace(/\n/g, '<br/>');
} else if (node.type === 'strong') {
return `<strong>${node.children.map(output).join('')}</strong>`;

View File

@ -243,6 +243,9 @@ properties:
fontSize:
type: number
default: 16
markdownAutoWrap:
type: boolean
default: true
suppressErrorRendering:
type: boolean
default: false