diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts index b65e9755c..ead157a04 100644 --- a/packages/mermaid-layout-elk/src/render.ts +++ b/packages/mermaid-layout-elk/src/render.ts @@ -263,7 +263,7 @@ const calcOffset = function (src, dest, parentLookupDb) { /** * 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); const edges = dataForLayout.edges; const labelsEl = svg.insert('g').attr('class', 'edgeLabels'); @@ -272,152 +272,154 @@ export const addEdges = function (dataForLayout, graph, svg) { let defaultStyle; let defaultLabelStyle; - edges.forEach(function (edge) { - // Identify Link - const linkIdBase = edge.id; // 'L-' + edge.start + '-' + edge.end; - // count the links from+to the same node to give unique id - if (linkIdCnt[linkIdBase] === undefined) { - linkIdCnt[linkIdBase] = 0; - log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); - } else { - 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'; + await Promise.all( + edges.map(async function (edge) { + // Identify Link + const linkIdBase = edge.id; // 'L-' + edge.start + '-' + edge.end; + // count the links from+to the same node to give unique id + if (linkIdCnt[linkIdBase] === undefined) { + linkIdCnt[linkIdBase] = 0; + log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); + } else { + linkIdCnt[linkIdBase]++; + log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); } - } else { - edgeData.arrowheadStyle = 'fill: #333'; - edgeData.labelpos = 'c'; - } + 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; - edgeData.labelType = edge.labelType; - edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n'); + 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'; + } - if (edge.style === undefined) { - edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;'; - } + // Check of arrow types, placed here in order not to break old rendering + 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; - edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd; + let style = ''; + 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 - // 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.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 { + edgeData.arrowheadStyle = 'fill: #333'; + edgeData.labelpos = 'c'; + } + + edgeData.labelType = edge.labelType; + 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; }; @@ -503,7 +505,7 @@ export const render = async (data4Layout, svg, element, algorithm) => { const edgesEl = svg.insert('g').attr('class', 'edges edgePath'); // 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 const nodes = data4Layout.nodes; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js index 943ea3c06..6b06eb139 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js @@ -25,7 +25,8 @@ const rect = async (parent, node) => { const shapeSvg = parent .insert('g') .attr('class', 'cluster ' + node.cssClasses) - .attr('id', node.id); + .attr('id', node.id) + .attr('data-look', node.look); const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels); @@ -174,9 +175,8 @@ const roundedWithTitle = async (parent, node) => { .insert('g') .attr('class', node.cssClasses) .attr('id', node.id) - .attr('data-et', 'node') - .attr('data-node', 'true') - .attr('data-id', node.id); + .attr('data-id', node.id) + .attr('data-look', node.look); // add the rect 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 x = node.x - width / 2; const y = node.y - height / 2; - + node.width = width; const innerY = node.y - node.height / 2 - halfPadding + bbox.height + 2; const look = siteConfig.look; @@ -254,12 +254,7 @@ const roundedWithTitle = async (parent, node) => { innerRect = shapeSvg.insert(() => roughInnerNode); } else { rect = outerRectG.insert('rect', ':first-child'); - let outerRectClass = 'outer'; - if (look === 'neo') { - outerRectClass = 'outer state-shadow-neo'; - } else { - outerRectClass = 'outer'; - } + const outerRectClass = 'outer'; // center the rect around its coordinate rect @@ -267,7 +262,8 @@ const roundedWithTitle = async (parent, node) => { .attr('x', x) .attr('y', y) .attr('width', width) - .attr('height', height); + .attr('height', height) + .attr('data-look', node.look); innerRect .attr('class', 'inner') .attr('x', x) @@ -294,8 +290,86 @@ const roundedWithTitle = async (parent, node) => { 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(); // Add outer g element 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 } }; }; const squareRect = rect; -const shapes = { rect, squareRect, roundedWithTitle, noteGroup, divider }; +const shapes = { + rect, + squareRect, + roundedWithTitle, + noteGroup, + divider, +}; let clusterElems = {}; @@ -365,8 +445,20 @@ export const clear = () => { }; 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]; - - el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')'); + el.cluster.attr('transform', 'translate(' + node.x + ', ' + node.y + ')'); };