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 { splitLineToFitWidth } from './splitText.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) {
|
||||
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 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';
|
||||
div.html(
|
||||
`<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'
|
||||
// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row'
|
||||
export const createText = (
|
||||
export const createText = async (
|
||||
el,
|
||||
text = '',
|
||||
{
|
||||
@ -218,7 +225,7 @@ export const createText = (
|
||||
label: decodedReplacedText,
|
||||
labelStyle: style.replace('fill:', 'color:'),
|
||||
};
|
||||
const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
|
||||
const vertexNode = await addHtmlSpan(el, node, width, classes, addSvgBackground);
|
||||
return vertexNode;
|
||||
} else {
|
||||
const structuredText = markdownToLines(text, config);
|
||||
|
@ -120,19 +120,35 @@ 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
|
||||
// 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
|
||||
graph.edges().forEach(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)));
|
||||
const processEdges = async () => {
|
||||
const edgePromises = graph.edges().map(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]);
|
||||
insertEdgeLabel(edgeLabels, edge);
|
||||
});
|
||||
// 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);
|
||||
});
|
||||
|
||||
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) {
|
||||
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 { 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);
|
||||
const siteConfig = getConfig();
|
||||
const { themeVariables, handdrawnSeed } = siteConfig;
|
||||
@ -29,8 +29,8 @@ const rect = (parent, node) => {
|
||||
// .appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
||||
const text =
|
||||
node.labelType === 'markdown'
|
||||
? createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels })
|
||||
: labelEl.node().appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
||||
? await createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels })
|
||||
: labelEl.node().appendChild(await createLabel(node.label, node.labelStyle, undefined, true));
|
||||
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
@ -154,7 +154,7 @@ const noteGroup = (parent, node) => {
|
||||
|
||||
return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } };
|
||||
};
|
||||
const roundedWithTitle = (parent, node) => {
|
||||
const roundedWithTitle = async (parent, node) => {
|
||||
const siteConfig = getConfig();
|
||||
|
||||
const { themeVariables, handdrawnSeed } = siteConfig;
|
||||
@ -177,7 +177,9 @@ const roundedWithTitle = (parent, node) => {
|
||||
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
||||
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
|
||||
let bbox = text.getBBox();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { select } from 'd3';
|
||||
import { log } from '$root/logger.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';
|
||||
|
||||
/**
|
||||
@ -18,11 +18,14 @@ function applyStyle(dom, styleFn) {
|
||||
* @param {any} node
|
||||
* @returns {SVGForeignObjectElement} Node
|
||||
*/
|
||||
function addHtmlLabel(node) {
|
||||
async function addHtmlLabel(node) {
|
||||
const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'));
|
||||
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';
|
||||
div.html(
|
||||
'<span class="' +
|
||||
@ -49,11 +52,12 @@ function addHtmlLabel(node) {
|
||||
* @param isNode
|
||||
* @deprecated svg-util/createText instead
|
||||
*/
|
||||
const createLabel = (_vertexText, style, isTitle, isNode) => {
|
||||
const createLabel = async (_vertexText, style, isTitle, isNode) => {
|
||||
let vertexText = _vertexText || '';
|
||||
if (typeof vertexText === 'object') {
|
||||
vertexText = vertexText[0];
|
||||
}
|
||||
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
|
||||
@ -66,7 +70,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
|
||||
),
|
||||
labelStyle: style ? style.replace('fill:', 'color:') : style,
|
||||
};
|
||||
let vertexNode = addHtmlLabel(node);
|
||||
let vertexNode = await addHtmlLabel(node);
|
||||
// vertexNode.parentNode.removeChild(vertexNode);
|
||||
return vertexNode;
|
||||
} else {
|
||||
|
@ -19,17 +19,17 @@ export const clear = () => {
|
||||
terminalLabels = {};
|
||||
};
|
||||
|
||||
export const insertEdgeLabel = (elem, edge) => {
|
||||
export const insertEdgeLabel = async (elem, edge) => {
|
||||
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
||||
// Create the actual text element
|
||||
const labelElement =
|
||||
edge.labelType === 'markdown'
|
||||
? createText(elem, edge.label, {
|
||||
? await createText(elem, edge.label, {
|
||||
style: edge.labelStyle,
|
||||
useHtmlLabels,
|
||||
addSvgBackground: true,
|
||||
})
|
||||
: createLabel(edge.label, edge.labelStyle);
|
||||
: await createLabel(edge.label, edge.labelStyle);
|
||||
log.info('abc82', edge, edge.labelType);
|
||||
|
||||
// Create outer g, edgeLabel, this will be positioned after graph layout
|
||||
@ -60,7 +60,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
||||
let fo;
|
||||
if (edge.startLabelLeft) {
|
||||
// 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 inner = startEdgeLabelLeft.insert('g').attr('class', 'inner');
|
||||
fo = inner.node().appendChild(startLabelElement);
|
||||
@ -74,7 +74,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
||||
}
|
||||
if (edge.startLabelRight) {
|
||||
// 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 inner = startEdgeLabelRight.insert('g').attr('class', 'inner');
|
||||
fo = startEdgeLabelRight.node().appendChild(startLabelElement);
|
||||
@ -90,7 +90,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
||||
}
|
||||
if (edge.endLabelLeft) {
|
||||
// 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 inner = endEdgeLabelLeft.insert('g').attr('class', 'inner');
|
||||
fo = inner.node().appendChild(endLabelElement);
|
||||
@ -107,7 +107,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
||||
}
|
||||
if (edge.endLabelRight) {
|
||||
// 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 inner = endEdgeLabelRight.insert('g').attr('class', 'inner');
|
||||
|
||||
|
@ -34,7 +34,7 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => {
|
||||
|
||||
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 };
|
||||
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
@ -49,7 +49,12 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => {
|
||||
const descr = label
|
||||
.node()
|
||||
.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)) {
|
||||
|
@ -31,7 +31,7 @@ export const labelHelper = async (parent, node, _classes) => {
|
||||
}
|
||||
|
||||
let text;
|
||||
text = createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
||||
text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
||||
useHtmlLabels,
|
||||
width: node.width || getConfig().flowchart.wrappingWidth,
|
||||
cssClasses: 'markdown-node-label',
|
||||
|
Loading…
x
Reference in New Issue
Block a user