5237 fix for async katex rendering when using elk

This commit is contained in:
Knut Sveidqvist 2024-06-29 21:19:09 +02:00
parent 35797f867f
commit 444de0f133
2 changed files with 251 additions and 157 deletions

View File

@ -263,7 +263,7 @@ const calcOffset = function (src, dest, parentLookupDb) {
/** /**
* Add edges to graph based on parsed graph definition * Add edges to graph based on parsed graph definition
*/ */
export const addEdges = function (dataForLayout, graph, svg) { export const addEdges = async function (dataForLayout, graph, svg) {
log.info('abc78 DAGA edges = ', dataForLayout); log.info('abc78 DAGA edges = ', dataForLayout);
const edges = dataForLayout.edges; const edges = dataForLayout.edges;
const labelsEl = svg.insert('g').attr('class', 'edgeLabels'); const labelsEl = svg.insert('g').attr('class', 'edgeLabels');
@ -272,152 +272,154 @@ export const addEdges = function (dataForLayout, graph, svg) {
let defaultStyle; let defaultStyle;
let defaultLabelStyle; let defaultLabelStyle;
edges.forEach(function (edge) { await Promise.all(
// Identify Link edges.map(async function (edge) {
const linkIdBase = edge.id; // 'L-' + edge.start + '-' + edge.end; // Identify Link
// count the links from+to the same node to give unique id const linkIdBase = edge.id; // 'L-' + edge.start + '-' + edge.end;
if (linkIdCnt[linkIdBase] === undefined) { // count the links from+to the same node to give unique id
linkIdCnt[linkIdBase] = 0; if (linkIdCnt[linkIdBase] === undefined) {
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); linkIdCnt[linkIdBase] = 0;
} else { log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
linkIdCnt[linkIdBase]++; } else {
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); linkIdCnt[linkIdBase]++;
} log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
const linkId = linkIdBase + '_' + linkIdCnt[linkIdBase];
edge.id = linkId;
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
const linkNameStart = 'LS_' + edge.start;
const linkNameEnd = 'LE_' + edge.end;
const edgeData = { style: '', labelStyle: '' };
edgeData.minlen = edge.length || 1;
edge.text = edge.label;
// Set link type for rendering
if (edge.type === 'arrow_open') {
edgeData.arrowhead = 'none';
} else {
edgeData.arrowhead = 'normal';
}
// Check of arrow types, placed here in order not to break old rendering
edgeData.arrowTypeStart = 'arrow_open';
edgeData.arrowTypeEnd = 'arrow_open';
/* eslint-disable no-fallthrough */
switch (edge.type) {
case 'double_arrow_cross':
edgeData.arrowTypeStart = 'arrow_cross';
case 'arrow_cross':
edgeData.arrowTypeEnd = 'arrow_cross';
break;
case 'double_arrow_point':
edgeData.arrowTypeStart = 'arrow_point';
case 'arrow_point':
edgeData.arrowTypeEnd = 'arrow_point';
break;
case 'double_arrow_circle':
edgeData.arrowTypeStart = 'arrow_circle';
case 'arrow_circle':
edgeData.arrowTypeEnd = 'arrow_circle';
break;
}
let style = '';
let labelStyle = '';
switch (edge.stroke) {
case 'normal':
style = 'fill:none;';
if (defaultStyle !== undefined) {
style = defaultStyle;
}
if (defaultLabelStyle !== undefined) {
labelStyle = defaultLabelStyle;
}
edgeData.thickness = 'normal';
edgeData.pattern = 'solid';
break;
case 'dotted':
edgeData.thickness = 'normal';
edgeData.pattern = 'dotted';
edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
break;
case 'thick':
edgeData.thickness = 'thick';
edgeData.pattern = 'solid';
edgeData.style = 'stroke-width: 3.5px;fill:none;';
break;
}
// if (edge.style !== undefined) {
// const styles = getStylesFromArray(edge.style);
// style = styles.style;
// labelStyle = styles.labelStyle;
// }
edgeData.style = edgeData.style += style;
edgeData.labelStyle = edgeData.labelStyle += labelStyle;
const conf = getConfig();
if (edge.interpolate !== undefined) {
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
} else if (edges.defaultInterpolate !== undefined) {
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
} else {
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
}
if (edge.text === undefined) {
if (edge.style !== undefined) {
edgeData.arrowheadStyle = 'fill: #333';
} }
} else { const linkId = linkIdBase + '_' + linkIdCnt[linkIdBase];
edgeData.arrowheadStyle = 'fill: #333'; edge.id = linkId;
edgeData.labelpos = 'c'; log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
} const linkNameStart = 'LS_' + edge.start;
const linkNameEnd = 'LE_' + edge.end;
edgeData.labelType = edge.labelType; const edgeData = { style: '', labelStyle: '' };
edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n'); edgeData.minlen = edge.length || 1;
edge.text = edge.label;
// Set link type for rendering
if (edge.type === 'arrow_open') {
edgeData.arrowhead = 'none';
} else {
edgeData.arrowhead = 'normal';
}
if (edge.style === undefined) { // Check of arrow types, placed here in order not to break old rendering
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;'; edgeData.arrowTypeStart = 'arrow_open';
} edgeData.arrowTypeEnd = 'arrow_open';
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:'); /* eslint-disable no-fallthrough */
switch (edge.type) {
case 'double_arrow_cross':
edgeData.arrowTypeStart = 'arrow_cross';
case 'arrow_cross':
edgeData.arrowTypeEnd = 'arrow_cross';
break;
case 'double_arrow_point':
edgeData.arrowTypeStart = 'arrow_point';
case 'arrow_point':
edgeData.arrowTypeEnd = 'arrow_point';
break;
case 'double_arrow_circle':
edgeData.arrowTypeStart = 'arrow_circle';
case 'arrow_circle':
edgeData.arrowTypeEnd = 'arrow_circle';
break;
}
edgeData.id = linkId; let style = '';
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd; let labelStyle = '';
const labelEl = insertEdgeLabel(labelsEl, edgeData); switch (edge.stroke) {
case 'normal':
style = 'fill:none;';
if (defaultStyle !== undefined) {
style = defaultStyle;
}
if (defaultLabelStyle !== undefined) {
labelStyle = defaultLabelStyle;
}
edgeData.thickness = 'normal';
edgeData.pattern = 'solid';
break;
case 'dotted':
edgeData.thickness = 'normal';
edgeData.pattern = 'dotted';
edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
break;
case 'thick':
edgeData.thickness = 'thick';
edgeData.pattern = 'solid';
edgeData.style = 'stroke-width: 3.5px;fill:none;';
break;
}
// if (edge.style !== undefined) {
// const styles = getStylesFromArray(edge.style);
// style = styles.style;
// labelStyle = styles.labelStyle;
// }
// calculate start and end points of the edge, note that the source and target edgeData.style = edgeData.style += style;
// can be modified for shapes that have ports edgeData.labelStyle = edgeData.labelStyle += labelStyle;
const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, dir);
log.debug('abc78 source and target', source, target); const conf = getConfig();
// Add the edge to the graph if (edge.interpolate !== undefined) {
graph.edges.push({ edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
id: 'e' + edge.start + edge.end, } else if (edges.defaultInterpolate !== undefined) {
...edge, edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
sources: [source], } else {
targets: [target], edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
sourceId, }
targetId,
labelEl: labelEl, if (edge.text === undefined) {
labels: [ if (edge.style !== undefined) {
{ edgeData.arrowheadStyle = 'fill: #333';
width: edgeData.width, }
height: edgeData.height, } else {
orgWidth: edgeData.width, edgeData.arrowheadStyle = 'fill: #333';
orgHeight: edgeData.height, edgeData.labelpos = 'c';
text: edgeData.label, }
layoutOptions: {
'edgeLabels.inline': 'true', edgeData.labelType = edge.labelType;
'edgeLabels.placement': 'CENTER', edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n');
if (edge.style === undefined) {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
}
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
edgeData.id = linkId;
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
const labelEl = await insertEdgeLabel(labelsEl, edgeData);
// calculate start and end points of the edge, note that the source and target
// can be modified for shapes that have ports
const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, dir);
log.debug('abc78 source and target', source, target);
// Add the edge to the graph
graph.edges.push({
id: 'e' + edge.start + edge.end,
...edge,
sources: [source],
targets: [target],
sourceId,
targetId,
labelEl: labelEl,
labels: [
{
width: edgeData.width,
height: edgeData.height,
orgWidth: edgeData.width,
orgHeight: edgeData.height,
text: edgeData.label,
layoutOptions: {
'edgeLabels.inline': 'true',
'edgeLabels.placement': 'CENTER',
},
}, },
}, ],
], edgeData,
edgeData, });
}); })
}); );
return graph; return graph;
}; };
@ -503,7 +505,7 @@ export const render = async (data4Layout, svg, element, algorithm) => {
const edgesEl = svg.insert('g').attr('class', 'edges edgePath'); const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
// Add the edges to the elk graph, this will entail creating the actual edges // Add the edges to the elk graph, this will entail creating the actual edges
elkGraph = addEdges(data4Layout, elkGraph, svg); elkGraph = await addEdges(data4Layout, elkGraph, svg);
// Iterate through all nodes and add the top level nodes to the graph // Iterate through all nodes and add the top level nodes to the graph
const nodes = data4Layout.nodes; const nodes = data4Layout.nodes;

View File

@ -25,7 +25,8 @@ const rect = async (parent, node) => {
const shapeSvg = parent const shapeSvg = parent
.insert('g') .insert('g')
.attr('class', 'cluster ' + node.cssClasses) .attr('class', 'cluster ' + node.cssClasses)
.attr('id', node.id); .attr('id', node.id)
.attr('data-look', node.look);
const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels); const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels);
@ -174,9 +175,8 @@ const roundedWithTitle = async (parent, node) => {
.insert('g') .insert('g')
.attr('class', node.cssClasses) .attr('class', node.cssClasses)
.attr('id', node.id) .attr('id', node.id)
.attr('data-et', 'node') .attr('data-id', node.id)
.attr('data-node', 'true') .attr('data-look', node.look);
.attr('data-id', node.id);
// add the rect // add the rect
const outerRectG = shapeSvg.insert('g', ':first-child'); const outerRectG = shapeSvg.insert('g', ':first-child');
@ -222,7 +222,7 @@ const roundedWithTitle = async (parent, node) => {
const innerHeight = node.height + padding - bbox.height - 6; const innerHeight = node.height + padding - bbox.height - 6;
const x = node.x - width / 2; const x = node.x - width / 2;
const y = node.y - height / 2; const y = node.y - height / 2;
node.width = width;
const innerY = node.y - node.height / 2 - halfPadding + bbox.height + 2; const innerY = node.y - node.height / 2 - halfPadding + bbox.height + 2;
const look = siteConfig.look; const look = siteConfig.look;
@ -254,12 +254,7 @@ const roundedWithTitle = async (parent, node) => {
innerRect = shapeSvg.insert(() => roughInnerNode); innerRect = shapeSvg.insert(() => roughInnerNode);
} else { } else {
rect = outerRectG.insert('rect', ':first-child'); rect = outerRectG.insert('rect', ':first-child');
let outerRectClass = 'outer'; const outerRectClass = 'outer';
if (look === 'neo') {
outerRectClass = 'outer state-shadow-neo';
} else {
outerRectClass = 'outer';
}
// center the rect around its coordinate // center the rect around its coordinate
rect rect
@ -267,7 +262,8 @@ const roundedWithTitle = async (parent, node) => {
.attr('x', x) .attr('x', x)
.attr('y', y) .attr('y', y)
.attr('width', width) .attr('width', width)
.attr('height', height); .attr('height', height)
.attr('data-look', node.look);
innerRect innerRect
.attr('class', 'inner') .attr('class', 'inner')
.attr('x', x) .attr('x', x)
@ -294,8 +290,86 @@ const roundedWithTitle = async (parent, node) => {
return { cluster: shapeSvg, labelBBox: bbox }; return { cluster: shapeSvg, labelBBox: bbox };
}; };
const divider = async (parent, node) => {
const siteConfig = getConfig();
const divider = (parent, node) => { const { themeVariables, handdrawnSeed } = siteConfig;
const { altBackground, compositeBackground, compositeTitleBackground, nodeBorder } =
themeVariables;
// Add outer g element
const shapeSvg = parent
.insert('g')
.attr('class', node.cssClasses)
.attr('id', node.id)
.attr('data-look', node.look);
// add the rect
const outerRectG = shapeSvg.insert('g', ':first-child');
// Create the label and insert it after the rect
let innerRect = shapeSvg.append('rect');
const padding = 0 * node.padding;
const halfPadding = padding / 2;
const width = node.width + padding;
node.diff = -node.padding;
const height = node.height + padding;
// const height = node.height + padding;
const x = node.x - width / 2;
const y = node.y - height / 2;
node.width = width;
const look = siteConfig.look;
// add the rect
let rect;
if (node.look === 'handdrawn') {
const isAlt = node.cssClasses.includes('statediagram-cluster-alt');
const rc = rough.svg(shapeSvg);
const roughOuterNode =
node.rx || node.ry
? rc.path(createRoundedRectPathD(x, y, width, height, 10), {
roughness: 0.7,
fill: compositeTitleBackground,
fillStyle: 'solid',
stroke: nodeBorder,
seed: handdrawnSeed,
})
: rc.rectangle(x, y, width, height, { seed: handdrawnSeed });
rect = shapeSvg.insert(() => roughOuterNode, ':first-child');
} else {
rect = outerRectG.insert('rect', ':first-child');
const outerRectClass = 'divider';
// center the rect around its coordinate
rect
.attr('class', outerRectClass)
.attr('x', x)
.attr('y', y)
.attr('width', width)
.attr('height', height)
.attr('data-look', node.look);
}
const rectBox = rect.node().getBBox();
node.height = rectBox.height;
node.offsetX = 0;
// Used by layout engine to position subgraph in parent
node.offsetY = 0;
node.intersect = function (point) {
return intersectRect(node, point);
};
return { cluster: shapeSvg, labelBBox: {} };
};
const dividerOrg = (parent, node) => {
console.log('Divider node IPI', node);
const { handdrawnSeed } = getConfig(); const { handdrawnSeed } = getConfig();
// Add outer g element // Add outer g element
const shapeSvg = parent.insert('g').attr('class', node.cssClasses).attr('id', node.id); const shapeSvg = parent.insert('g').attr('class', node.cssClasses).attr('id', node.id);
@ -342,7 +416,13 @@ const divider = (parent, node) => {
return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } }; return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } };
}; };
const squareRect = rect; const squareRect = rect;
const shapes = { rect, squareRect, roundedWithTitle, noteGroup, divider }; const shapes = {
rect,
squareRect,
roundedWithTitle,
noteGroup,
divider,
};
let clusterElems = {}; let clusterElems = {};
@ -365,8 +445,20 @@ export const clear = () => {
}; };
export const positionCluster = (node) => { export const positionCluster = (node) => {
log.debug('Position cluster (' + node.id + ', ' + node.x + ', ' + node.y + ')'); log.info(
'Position cluster (' +
node.id +
', ' +
node.x +
', ' +
node.y +
') (' +
node?.width +
', ' +
node?.height +
')',
clusterElems[node.id]
);
const el = clusterElems[node.id]; const el = clusterElems[node.id];
el.cluster.attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
}; };