mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
Add katex support
This commit is contained in:
parent
1096b185ee
commit
e07fdfedb6
@ -8,6 +8,8 @@ import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdo
|
|||||||
import { decodeEntities } from '../utils.js';
|
import { decodeEntities } from '../utils.js';
|
||||||
import { splitLineToFitWidth } from './splitText.js';
|
import { splitLineToFitWidth } from './splitText.js';
|
||||||
import type { MarkdownLine, MarkdownWord } from './types.js';
|
import type { MarkdownLine, MarkdownWord } from './types.js';
|
||||||
|
import common, { renderKatex } from '$root/diagrams/common/common.js';
|
||||||
|
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
function applyStyle(dom, styleFn) {
|
function applyStyle(dom, styleFn) {
|
||||||
if (styleFn) {
|
if (styleFn) {
|
||||||
@ -15,11 +17,16 @@ function applyStyle(dom, styleFn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addHtmlSpan(element, node, width, classes, addBackground = false) {
|
async function addHtmlSpan(element, node, width, classes, addBackground = false) {
|
||||||
const fo = element.append('foreignObject');
|
const fo = element.append('foreignObject');
|
||||||
const div = fo.append('xhtml:div');
|
const div = fo.append('xhtml:div');
|
||||||
|
|
||||||
const label = node.label;
|
// const label = node.label;
|
||||||
|
let label = '';
|
||||||
|
|
||||||
|
if (node.label) {
|
||||||
|
label = await renderKatex(node.label.replace(common.lineBreakRegex, '\n'), getConfig());
|
||||||
|
}
|
||||||
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
||||||
div.html(
|
div.html(
|
||||||
`<span class="${labelClass} ${classes}" ` +
|
`<span class="${labelClass} ${classes}" ` +
|
||||||
@ -184,7 +191,7 @@ export function replaceIconSubstring(text: string) {
|
|||||||
|
|
||||||
// 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'
|
// 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'
|
||||||
// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row'
|
// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row'
|
||||||
export const createText = (
|
export const createText = async (
|
||||||
el,
|
el,
|
||||||
text = '',
|
text = '',
|
||||||
{
|
{
|
||||||
@ -218,7 +225,7 @@ export const createText = (
|
|||||||
label: decodedReplacedText,
|
label: decodedReplacedText,
|
||||||
labelStyle: style.replace('fill:', 'color:'),
|
labelStyle: style.replace('fill:', 'color:'),
|
||||||
};
|
};
|
||||||
const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
|
const vertexNode = await addHtmlSpan(el, node, width, classes, addSvgBackground);
|
||||||
return vertexNode;
|
return vertexNode;
|
||||||
} else {
|
} else {
|
||||||
const structuredText = markdownToLines(text, config);
|
const structuredText = markdownToLines(text, config);
|
||||||
|
@ -120,20 +120,36 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Insert labels, this will insert them into the dom so that the width can be calculated
|
const processEdges = async () => {
|
||||||
// Also figure out which edges point to/from clusters and adjust them accordingly
|
const edgePromises = graph.edges().map(async function (e) {
|
||||||
// Edges from/to clusters really points to the first child in the cluster.
|
|
||||||
// TODO: pick optimal child in the cluster to us as link anchor
|
|
||||||
graph.edges().forEach(function (e) {
|
|
||||||
const edge = graph.edge(e.v, e.w, e.name);
|
const edge = graph.edge(e.v, e.w, e.name);
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
||||||
|
|
||||||
// Check if link is either from or to a cluster
|
// Check if link is either from or to a cluster
|
||||||
log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
|
log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
|
||||||
insertEdgeLabel(edgeLabels, edge);
|
await insertEdgeLabel(edgeLabels, edge);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await Promise.all(edgePromises);
|
||||||
|
};
|
||||||
|
|
||||||
|
await processEdges();
|
||||||
|
|
||||||
|
// // Insert labels, this will insert them into the dom so that the width can be calculated
|
||||||
|
// // Also figure out which edges point to/from clusters and adjust them accordingly
|
||||||
|
// // Edges from/to clusters really points to the first child in the cluster.
|
||||||
|
// // TODO: pick optimal child in the cluster to us as link anchor
|
||||||
|
// await graph.edges().forEach(async function (e) {
|
||||||
|
// const edge = graph.edge(e.v, e.w, e.name);
|
||||||
|
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||||
|
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
||||||
|
|
||||||
|
// // Check if link is either from or to a cluster
|
||||||
|
// log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
|
||||||
|
// await insertEdgeLabel(edgeLabels, edge);
|
||||||
|
// });
|
||||||
|
|
||||||
graph.edges().forEach(function (e) {
|
graph.edges().forEach(function (e) {
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ import createLabel from './createLabel.js';
|
|||||||
import { createRoundedRectPathD } from './shapes/roundedRectPath.ts';
|
import { createRoundedRectPathD } from './shapes/roundedRectPath.ts';
|
||||||
import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
|
import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
|
||||||
|
|
||||||
const rect = (parent, node) => {
|
const rect = async (parent, node) => {
|
||||||
log.info('Creating subgraph rect for ', node.id, node);
|
log.info('Creating subgraph rect for ', node.id, node);
|
||||||
const siteConfig = getConfig();
|
const siteConfig = getConfig();
|
||||||
const { themeVariables, handdrawnSeed } = siteConfig;
|
const { themeVariables, handdrawnSeed } = siteConfig;
|
||||||
@ -29,8 +29,8 @@ const rect = (parent, node) => {
|
|||||||
// .appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
// .appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
||||||
const text =
|
const text =
|
||||||
node.labelType === 'markdown'
|
node.labelType === 'markdown'
|
||||||
? createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels })
|
? await createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels })
|
||||||
: labelEl.node().appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
: labelEl.node().appendChild(await createLabel(node.label, node.labelStyle, undefined, true));
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
@ -154,7 +154,7 @@ const noteGroup = (parent, node) => {
|
|||||||
|
|
||||||
return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } };
|
return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } };
|
||||||
};
|
};
|
||||||
const roundedWithTitle = (parent, node) => {
|
const roundedWithTitle = async (parent, node) => {
|
||||||
const siteConfig = getConfig();
|
const siteConfig = getConfig();
|
||||||
|
|
||||||
const { themeVariables, handdrawnSeed } = siteConfig;
|
const { themeVariables, handdrawnSeed } = siteConfig;
|
||||||
@ -177,7 +177,9 @@ const roundedWithTitle = (parent, node) => {
|
|||||||
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
||||||
let innerRect = shapeSvg.append('rect');
|
let innerRect = shapeSvg.append('rect');
|
||||||
|
|
||||||
const text = label.node().appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
const text = label
|
||||||
|
.node()
|
||||||
|
.appendChild(await createLabel(node.label, node.labelStyle, undefined, true));
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import { log } from '$root/logger.js';
|
import { log } from '$root/logger.js';
|
||||||
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
||||||
import { evaluate } from '$root/diagrams/common/common.js';
|
import common, { evaluate, renderKatex } from '$root/diagrams/common/common.js';
|
||||||
import { decodeEntities } from '$root/utils.js';
|
import { decodeEntities } from '$root/utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,11 +18,14 @@ function applyStyle(dom, styleFn) {
|
|||||||
* @param {any} node
|
* @param {any} node
|
||||||
* @returns {SVGForeignObjectElement} Node
|
* @returns {SVGForeignObjectElement} Node
|
||||||
*/
|
*/
|
||||||
function addHtmlLabel(node) {
|
async function addHtmlLabel(node) {
|
||||||
const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'));
|
const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'));
|
||||||
const div = fo.append('xhtml:div');
|
const div = fo.append('xhtml:div');
|
||||||
|
|
||||||
const label = node.label;
|
let label = node.label;
|
||||||
|
if (node.label) {
|
||||||
|
label = await renderKatex(node.label.replace(common.lineBreakRegex, '\n'), getConfig());
|
||||||
|
}
|
||||||
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
||||||
div.html(
|
div.html(
|
||||||
'<span class="' +
|
'<span class="' +
|
||||||
@ -49,11 +52,12 @@ function addHtmlLabel(node) {
|
|||||||
* @param isNode
|
* @param isNode
|
||||||
* @deprecated svg-util/createText instead
|
* @deprecated svg-util/createText instead
|
||||||
*/
|
*/
|
||||||
const createLabel = (_vertexText, style, isTitle, isNode) => {
|
const createLabel = async (_vertexText, style, isTitle, isNode) => {
|
||||||
let vertexText = _vertexText || '';
|
let vertexText = _vertexText || '';
|
||||||
if (typeof vertexText === 'object') {
|
if (typeof vertexText === 'object') {
|
||||||
vertexText = vertexText[0];
|
vertexText = vertexText[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
|
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
|
||||||
@ -66,7 +70,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
|
|||||||
),
|
),
|
||||||
labelStyle: style ? style.replace('fill:', 'color:') : style,
|
labelStyle: style ? style.replace('fill:', 'color:') : style,
|
||||||
};
|
};
|
||||||
let vertexNode = addHtmlLabel(node);
|
let vertexNode = await addHtmlLabel(node);
|
||||||
// vertexNode.parentNode.removeChild(vertexNode);
|
// vertexNode.parentNode.removeChild(vertexNode);
|
||||||
return vertexNode;
|
return vertexNode;
|
||||||
} else {
|
} else {
|
||||||
|
@ -19,17 +19,17 @@ export const clear = () => {
|
|||||||
terminalLabels = {};
|
terminalLabels = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const insertEdgeLabel = (elem, edge) => {
|
export const insertEdgeLabel = async (elem, edge) => {
|
||||||
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
||||||
// Create the actual text element
|
// Create the actual text element
|
||||||
const labelElement =
|
const labelElement =
|
||||||
edge.labelType === 'markdown'
|
edge.labelType === 'markdown'
|
||||||
? createText(elem, edge.label, {
|
? await createText(elem, edge.label, {
|
||||||
style: edge.labelStyle,
|
style: edge.labelStyle,
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
addSvgBackground: true,
|
addSvgBackground: true,
|
||||||
})
|
})
|
||||||
: createLabel(edge.label, edge.labelStyle);
|
: await createLabel(edge.label, edge.labelStyle);
|
||||||
log.info('abc82', edge, edge.labelType);
|
log.info('abc82', edge, edge.labelType);
|
||||||
|
|
||||||
// Create outer g, edgeLabel, this will be positioned after graph layout
|
// Create outer g, edgeLabel, this will be positioned after graph layout
|
||||||
@ -60,7 +60,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
|||||||
let fo;
|
let fo;
|
||||||
if (edge.startLabelLeft) {
|
if (edge.startLabelLeft) {
|
||||||
// Create the actual text element
|
// Create the actual text element
|
||||||
const startLabelElement = createLabel(edge.startLabelLeft, edge.labelStyle);
|
const startLabelElement = await createLabel(edge.startLabelLeft, edge.labelStyle);
|
||||||
const startEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
|
const startEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
|
||||||
const inner = startEdgeLabelLeft.insert('g').attr('class', 'inner');
|
const inner = startEdgeLabelLeft.insert('g').attr('class', 'inner');
|
||||||
fo = inner.node().appendChild(startLabelElement);
|
fo = inner.node().appendChild(startLabelElement);
|
||||||
@ -74,7 +74,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
|||||||
}
|
}
|
||||||
if (edge.startLabelRight) {
|
if (edge.startLabelRight) {
|
||||||
// Create the actual text element
|
// Create the actual text element
|
||||||
const startLabelElement = createLabel(edge.startLabelRight, edge.labelStyle);
|
const startLabelElement = await createLabel(edge.startLabelRight, edge.labelStyle);
|
||||||
const startEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
|
const startEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
|
||||||
const inner = startEdgeLabelRight.insert('g').attr('class', 'inner');
|
const inner = startEdgeLabelRight.insert('g').attr('class', 'inner');
|
||||||
fo = startEdgeLabelRight.node().appendChild(startLabelElement);
|
fo = startEdgeLabelRight.node().appendChild(startLabelElement);
|
||||||
@ -90,7 +90,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
|||||||
}
|
}
|
||||||
if (edge.endLabelLeft) {
|
if (edge.endLabelLeft) {
|
||||||
// Create the actual text element
|
// Create the actual text element
|
||||||
const endLabelElement = createLabel(edge.endLabelLeft, edge.labelStyle);
|
const endLabelElement = await createLabel(edge.endLabelLeft, edge.labelStyle);
|
||||||
const endEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
|
const endEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
|
||||||
const inner = endEdgeLabelLeft.insert('g').attr('class', 'inner');
|
const inner = endEdgeLabelLeft.insert('g').attr('class', 'inner');
|
||||||
fo = inner.node().appendChild(endLabelElement);
|
fo = inner.node().appendChild(endLabelElement);
|
||||||
@ -107,7 +107,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
|||||||
}
|
}
|
||||||
if (edge.endLabelRight) {
|
if (edge.endLabelRight) {
|
||||||
// Create the actual text element
|
// Create the actual text element
|
||||||
const endLabelElement = createLabel(edge.endLabelRight, edge.labelStyle);
|
const endLabelElement = await createLabel(edge.endLabelRight, edge.labelStyle);
|
||||||
const endEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
|
const endEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
|
||||||
const inner = endEdgeLabelRight.insert('g').attr('class', 'inner');
|
const inner = endEdgeLabelRight.insert('g').attr('class', 'inner');
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => {
|
|||||||
|
|
||||||
const title = node.label;
|
const title = node.label;
|
||||||
|
|
||||||
const text = label.node().appendChild(createLabel(title, node.labelStyle, true, true));
|
const text = label.node().appendChild(await createLabel(title, node.labelStyle, true, true));
|
||||||
let bbox = { width: 0, height: 0 };
|
let bbox = { width: 0, height: 0 };
|
||||||
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {
|
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {
|
||||||
const div = text.children[0];
|
const div = text.children[0];
|
||||||
@ -49,7 +49,12 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => {
|
|||||||
const descr = label
|
const descr = label
|
||||||
.node()
|
.node()
|
||||||
.appendChild(
|
.appendChild(
|
||||||
createLabel(textRows.join ? textRows.join('<br/>') : textRows, node.labelStyle, true, true)
|
await createLabel(
|
||||||
|
textRows.join ? textRows.join('<br/>') : textRows,
|
||||||
|
node.labelStyle,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {
|
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {
|
||||||
|
@ -31,7 +31,7 @@ export const labelHelper = async (parent, node, _classes) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let text;
|
let text;
|
||||||
text = createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
width: node.width || getConfig().flowchart.wrappingWidth,
|
width: node.width || getConfig().flowchart.wrappingWidth,
|
||||||
cssClasses: 'markdown-node-label',
|
cssClasses: 'markdown-node-label',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user