diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts index 694a061aa7..533a72ddb8 100644 --- a/packages/mermaid-layout-elk/src/render.ts +++ b/packages/mermaid-layout-elk/src/render.ts @@ -1,848 +1,847 @@ import { curveLinear } from 'd3'; import ELK from 'elkjs/lib/elk.bundled.js'; -import mermaid, { type LayoutData } from 'mermaid'; +import type { InternalHelpers, LayoutData } from 'mermaid'; import { type TreeData, findCommonAncestor } from './find-common-ancestor.js'; -const { - common, - getConfig, - insertCluster, - insertEdge, - insertEdgeLabel, - insertMarkers, - insertNode, - interpolateToCurve, - labelHelper, - log, - positionEdgeLabel, -} = mermaid.internalHelpers; - -const nodeDb: Record = {}; -const portPos: Record = {}; -const clusterDb: Record = {}; - -const addVertex = async (nodeEl: any, graph: { children: any[] }, nodeArr: any, node: any) => { - const labelData: any = { width: 0, height: 0 }; - // const ports = [ - // { - // id: node.id + '-west', - // layoutOptions: { - // 'port.side': 'WEST', - // }, - // }, - // { - // id: node.id + '-east', - // layoutOptions: { - // 'port.side': 'EAST', - // }, - // }, - // { - // id: node.id + '-south', - // layoutOptions: { - // 'port.side': 'SOUTH', - // }, - // }, - // { - // id: node.id + '-north', - // layoutOptions: { - // 'port.side': 'NORTH', - // }, - // }, - // ]; - - let boundingBox; - const child = { - ...node, - // ports: node.shape === 'diamond' ? ports : [], - }; - graph.children.push(child); - nodeDb[node.id] = child; - - // Add the element to the DOM - if (!node.isGroup) { - const childNodeEl = await insertNode(nodeEl, node, node.dir); - boundingBox = childNodeEl.node().getBBox(); - child.domId = childNodeEl; - child.width = boundingBox.width; - child.height = boundingBox.height; - } else { - // A subgraph - child.children = []; - await addVertices(nodeEl, nodeArr, child, node.id); - - if (node.label) { - // @ts-ignore TODO: fix this - const { shapeSvg, bbox } = await labelHelper(nodeEl, node, undefined, true); - labelData.width = bbox.width; - labelData.wrappingWidth = getConfig().flowchart!.wrappingWidth; - // Give some padding for elk - labelData.height = bbox.height - 2; - labelData.labelNode = shapeSvg.node(); - // We need the label hight to be able to size the subgraph; - shapeSvg.remove(); - } else { - // Subgraph without label - labelData.width = 0; - labelData.height = 0; - } - child.labelData = labelData; - child.domId = nodeEl; - } -}; - -const addVertices = async function ( - nodeEl: any, - nodeArr: any[], - graph: { - id: string; - layoutOptions: { - 'elk.hierarchyHandling': string; - 'elk.algorithm': any; - 'nodePlacement.strategy': any; - 'elk.layered.mergeEdges': any; - 'elk.direction': string; - 'spacing.baseValue': number; - }; - children: never[]; - edges: never[]; - }, - parentId?: undefined -) { - const siblings = nodeArr.filter((node: { parentId: any }) => node.parentId === parentId); - log.info('addVertices APA12', siblings, parentId); - // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition - await Promise.all( - siblings.map(async (node: any) => { - await addVertex(nodeEl, graph, nodeArr, node); - }) - ); - return graph; -}; - -const drawNodes = async ( - relX: number, - relY: number, - nodeArray: any[], - svg: any, - subgraphsEl: { +export const render = async ( + data4Layout: LayoutData, + svg: { insert: (arg0: string) => { (): any; new (): any; attr: { (arg0: string, arg1: string): any; new (): any }; }; }, - depth: number + element: any, + { + common, + getConfig, + insertCluster, + insertEdge, + insertEdgeLabel, + insertMarkers, + insertNode, + interpolateToCurve, + labelHelper, + log, + positionEdgeLabel, + }: InternalHelpers, + algorithm: any ) => { - await Promise.all( - nodeArray.map(async function (node: { - id: string | number; - x: any; - y: any; - width: number; - labels: { width: any }[]; - height: number; - isGroup: any; - labelData: any; - offset: { posX: number; posY: number }; - shape: any; - domId: { node: () => any; attr: (arg0: string, arg1: string) => void }; - }) { - if (node) { - nodeDb[node.id] = node; - nodeDb[node.id].offset = { - posX: node.x + relX, - posY: node.y + relY, - x: relX, - y: relY, - depth, - width: Math.max(node.width, node.labels ? node.labels[0]?.width || 0 : 0), - height: node.height, - }; - if (node.isGroup) { - log.debug('Id abc88 subgraph = ', node.id, node.x, node.y, node.labelData); - const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph'); - // TODO use faster way of cloning - const clusterNode = JSON.parse(JSON.stringify(node)); - clusterNode.x = node.offset.posX + node.width / 2; - clusterNode.y = node.offset.posY + node.height / 2; - await insertCluster(subgraphEl, clusterNode); - - log.debug('Id (UIO)= ', node.id, node.width, node.shape, node.labels); - } else { - log.info( - 'Id NODE = ', - node.id, - node.x, - node.y, - relX, - relY, - node.domId.node(), - `translate(${node.x + relX + node.width / 2}, ${node.y + relY + node.height / 2})` - ); - node.domId.attr( - 'transform', - `translate(${node.x + relX + node.width / 2}, ${node.y + relY + node.height / 2})` - ); - } - } - }) - ); + const nodeDb: Record = {}; + const portPos: Record = {}; + const clusterDb: Record = {}; + + const addVertex = async (nodeEl: any, graph: { children: any[] }, nodeArr: any, node: any) => { + const labelData: any = { width: 0, height: 0 }; + // const ports = [ + // { + // id: node.id + '-west', + // layoutOptions: { + // 'port.side': 'WEST', + // }, + // }, + // { + // id: node.id + '-east', + // layoutOptions: { + // 'port.side': 'EAST', + // }, + // }, + // { + // id: node.id + '-south', + // layoutOptions: { + // 'port.side': 'SOUTH', + // }, + // }, + // { + // id: node.id + '-north', + // layoutOptions: { + // 'port.side': 'NORTH', + // }, + // }, + // ]; + + let boundingBox; + const child = { + ...node, + // ports: node.shape === 'diamond' ? ports : [], + }; + graph.children.push(child); + nodeDb[node.id] = child; + + // Add the element to the DOM + if (!node.isGroup) { + const childNodeEl = await insertNode(nodeEl, node, node.dir); + boundingBox = childNodeEl.node().getBBox(); + child.domId = childNodeEl; + child.width = boundingBox.width; + child.height = boundingBox.height; + } else { + // A subgraph + child.children = []; + await addVertices(nodeEl, nodeArr, child, node.id); - await Promise.all( - nodeArray.map(async function (node: { isGroup: any; x: any; y: any; children: any }) { - if (node?.isGroup) { - await drawNodes(relX + node.x, relY + node.y, node.children, svg, subgraphsEl, depth + 1); + if (node.label) { + // @ts-ignore TODO: fix this + const { shapeSvg, bbox } = await labelHelper(nodeEl, node, undefined, true); + labelData.width = bbox.width; + labelData.wrappingWidth = getConfig().flowchart!.wrappingWidth; + // Give some padding for elk + labelData.height = bbox.height - 2; + labelData.labelNode = shapeSvg.node(); + // We need the label hight to be able to size the subgraph; + shapeSvg.remove(); + } else { + // Subgraph without label + labelData.width = 0; + labelData.height = 0; } - }) - ); -}; - -const getNextPort = (node: string | number, edgeDirection: string, graphDirection: any) => { - log.info('getNextPort abc88', { node, edgeDirection, graphDirection }); - if (!portPos[node]) { - switch (graphDirection) { - case 'TB': - case 'TD': - portPos[node] = { - inPosition: 'north', - outPosition: 'south', - }; - break; - case 'BT': - portPos[node] = { - inPosition: 'south', - outPosition: 'north', - }; - break; - case 'RL': - portPos[node] = { - inPosition: 'east', - outPosition: 'west', - }; - break; - case 'LR': - portPos[node] = { - inPosition: 'west', - outPosition: 'east', - }; - break; + child.labelData = labelData; + child.domId = nodeEl; } - } - const result = edgeDirection === 'in' ? portPos[node].inPosition : portPos[node].outPosition; - - if (edgeDirection === 'in') { - // @ts-ignore TODO: fix this - portPos[node].inPosition = getNextPosition( - portPos[node].inPosition, - edgeDirection, - graphDirection + }; + + const addVertices = async function ( + nodeEl: any, + nodeArr: any[], + graph: { + id: string; + layoutOptions: { + 'elk.hierarchyHandling': string; + 'elk.algorithm': any; + 'nodePlacement.strategy': any; + 'elk.layered.mergeEdges': any; + 'elk.direction': string; + 'spacing.baseValue': number; + }; + children: never[]; + edges: never[]; + }, + parentId?: undefined + ) { + const siblings = nodeArr.filter((node: { parentId: any }) => node.parentId === parentId); + log.info('addVertices APA12', siblings, parentId); + // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition + await Promise.all( + siblings.map(async (node: any) => { + await addVertex(nodeEl, graph, nodeArr, node); + }) ); - } else { - // @ts-ignore TODO: fix this - portPos[node].outPosition = getNextPosition( - portPos[node].outPosition, - edgeDirection, - graphDirection + return graph; + }; + + const drawNodes = async ( + relX: number, + relY: number, + nodeArray: any[], + svg: any, + subgraphsEl: { + insert: (arg0: string) => { + (): any; + new (): any; + attr: { (arg0: string, arg1: string): any; new (): any }; + }; + }, + depth: number + ) => { + await Promise.all( + nodeArray.map(async function (node: { + id: string | number; + x: any; + y: any; + width: number; + labels: { width: any }[]; + height: number; + isGroup: any; + labelData: any; + offset: { posX: number; posY: number }; + shape: any; + domId: { node: () => any; attr: (arg0: string, arg1: string) => void }; + }) { + if (node) { + nodeDb[node.id] = node; + nodeDb[node.id].offset = { + posX: node.x + relX, + posY: node.y + relY, + x: relX, + y: relY, + depth, + width: Math.max(node.width, node.labels ? node.labels[0]?.width || 0 : 0), + height: node.height, + }; + if (node.isGroup) { + log.debug('Id abc88 subgraph = ', node.id, node.x, node.y, node.labelData); + const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph'); + // TODO use faster way of cloning + const clusterNode = JSON.parse(JSON.stringify(node)); + clusterNode.x = node.offset.posX + node.width / 2; + clusterNode.y = node.offset.posY + node.height / 2; + await insertCluster(subgraphEl, clusterNode); + + log.debug('Id (UIO)= ', node.id, node.width, node.shape, node.labels); + } else { + log.info( + 'Id NODE = ', + node.id, + node.x, + node.y, + relX, + relY, + node.domId.node(), + `translate(${node.x + relX + node.width / 2}, ${node.y + relY + node.height / 2})` + ); + node.domId.attr( + 'transform', + `translate(${node.x + relX + node.width / 2}, ${node.y + relY + node.height / 2})` + ); + } + } + }) ); - } - return result; -}; -const addSubGraphs = (nodeArr: any[]): TreeData => { - const parentLookupDb: TreeData = { parentById: {}, childrenById: {} }; - const subgraphs = nodeArr.filter((node: { isGroup: any }) => node.isGroup); - log.info('Subgraphs - ', subgraphs); - subgraphs.forEach((subgraph: { id: string }) => { - const children = nodeArr.filter((node: { parentId: any }) => node.parentId === subgraph.id); - children.forEach((node: any) => { - parentLookupDb.parentById[node.id] = subgraph.id; - if (parentLookupDb.childrenById[subgraph.id] === undefined) { - parentLookupDb.childrenById[subgraph.id] = []; + await Promise.all( + nodeArray.map(async function (node: { isGroup: any; x: any; y: any; children: any }) { + if (node?.isGroup) { + await drawNodes(relX + node.x, relY + node.y, node.children, svg, subgraphsEl, depth + 1); + } + }) + ); + }; + + const getNextPort = (node: string | number, edgeDirection: string, graphDirection: any) => { + log.info('getNextPort abc88', { node, edgeDirection, graphDirection }); + if (!portPos[node]) { + switch (graphDirection) { + case 'TB': + case 'TD': + portPos[node] = { + inPosition: 'north', + outPosition: 'south', + }; + break; + case 'BT': + portPos[node] = { + inPosition: 'south', + outPosition: 'north', + }; + break; + case 'RL': + portPos[node] = { + inPosition: 'east', + outPosition: 'west', + }; + break; + case 'LR': + portPos[node] = { + inPosition: 'west', + outPosition: 'east', + }; + break; } - parentLookupDb.childrenById[subgraph.id].push(node); - }); - }); + } + const result = edgeDirection === 'in' ? portPos[node].inPosition : portPos[node].outPosition; - subgraphs.forEach(function (subgraph: { id: string | number }) { - const data: any = { id: subgraph.id }; - if (parentLookupDb.parentById[subgraph.id] !== undefined) { - data.parent = parentLookupDb.parentById[subgraph.id]; + if (edgeDirection === 'in') { + // @ts-ignore TODO: fix this + portPos[node].inPosition = getNextPosition( + portPos[node].inPosition, + edgeDirection, + graphDirection + ); + } else { + // @ts-ignore TODO: fix this + portPos[node].outPosition = getNextPosition( + portPos[node].outPosition, + edgeDirection, + graphDirection + ); } - }); - return parentLookupDb; -}; + return result; + }; -const getEdgeStartEndPoint = (edge: any, dir: any) => { - let source: any = edge.start; - let target: any = edge.end; + const addSubGraphs = (nodeArr: any[]): TreeData => { + const parentLookupDb: TreeData = { parentById: {}, childrenById: {} }; + const subgraphs = nodeArr.filter((node: { isGroup: any }) => node.isGroup); + log.info('Subgraphs - ', subgraphs); + subgraphs.forEach((subgraph: { id: string }) => { + const children = nodeArr.filter((node: { parentId: any }) => node.parentId === subgraph.id); + children.forEach((node: any) => { + parentLookupDb.parentById[node.id] = subgraph.id; + if (parentLookupDb.childrenById[subgraph.id] === undefined) { + parentLookupDb.childrenById[subgraph.id] = []; + } + parentLookupDb.childrenById[subgraph.id].push(node); + }); + }); - // Save the original source and target - const sourceId = source; - const targetId = target; + subgraphs.forEach(function (subgraph: { id: string | number }) { + const data: any = { id: subgraph.id }; + if (parentLookupDb.parentById[subgraph.id] !== undefined) { + data.parent = parentLookupDb.parentById[subgraph.id]; + } + }); + return parentLookupDb; + }; - const startNode = nodeDb[edge.start.id]; - const endNode = nodeDb[edge.end.id]; + const getEdgeStartEndPoint = (edge: any, dir: any) => { + let source: any = edge.start; + let target: any = edge.end; - if (!startNode || !endNode) { - return { source, target }; - } + // Save the original source and target + const sourceId = source; + const targetId = target; - if (startNode.shape === 'diamond') { - source = `${source}-${getNextPort(source, 'out', dir)}`; - } + const startNode = nodeDb[edge.start.id]; + const endNode = nodeDb[edge.end.id]; - if (endNode.shape === 'diamond') { - target = `${target}-${getNextPort(target, 'in', dir)}`; - } + if (!startNode || !endNode) { + return { source, target }; + } - // Add the edge to the graph - return { source, target, sourceId, targetId }; -}; + if (startNode.shape === 'diamond') { + source = `${source}-${getNextPort(source, 'out', dir)}`; + } -const calcOffset = function (src: string, dest: string, parentLookupDb: TreeData) { - const ancestor = findCommonAncestor(src, dest, parentLookupDb); - if (ancestor === undefined || ancestor === 'root') { - return { x: 0, y: 0 }; - } + if (endNode.shape === 'diamond') { + target = `${target}-${getNextPort(target, 'in', dir)}`; + } - const ancestorOffset = nodeDb[ancestor].offset; - return { x: ancestorOffset.posX, y: ancestorOffset.posY }; -}; + // Add the edge to the graph + return { source, target, sourceId, targetId }; + }; -/** - * Add edges to graph based on parsed graph definition - */ -const addEdges = async function ( - dataForLayout: { edges: any; direction: string }, - graph: { - id?: string; - layoutOptions?: { - 'elk.hierarchyHandling': string; - 'elk.algorithm': any; - 'nodePlacement.strategy': any; - 'elk.layered.mergeEdges': any; - 'elk.direction': string; - 'spacing.baseValue': number; - }; - children?: never[]; - edges: any; - }, - svg: { - insert: (arg0: string) => { - (): any; - new (): any; - attr: { (arg0: string, arg1: string): any; new (): any }; - }; - } -) { - log.info('abc78 DAGA edges = ', dataForLayout); - const edges = dataForLayout.edges; - const labelsEl = svg.insert('g').attr('class', 'edgeLabels'); - const linkIdCnt: any = {}; - const dir = dataForLayout.direction || 'DOWN'; - let defaultStyle: string | undefined; - let defaultLabelStyle: string | undefined; - - await Promise.all( - edges.map(async function (edge: { - id: string; - start: string; - end: string; - length: number; - text: undefined; - label: any; - type: string; - stroke: any; - interpolate: undefined; - style: undefined; - labelType: any; - }) { - // 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: any = { 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'; - } + const calcOffset = function (src: string, dest: string, parentLookupDb: TreeData) { + const ancestor = findCommonAncestor(src, dest, parentLookupDb); + if (ancestor === undefined || ancestor === 'root') { + return { x: 0, y: 0 }; + } - // Check of arrow types, placed here in order not to break old rendering - edgeData.arrowTypeStart = 'arrow_open'; - edgeData.arrowTypeEnd = 'arrow_open'; + const ancestorOffset = nodeDb[ancestor].offset; + return { x: ancestorOffset.posX, y: ancestorOffset.posY }; + }; - /* 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; - } + /** + * Add edges to graph based on parsed graph definition + */ + const addEdges = async function ( + dataForLayout: { edges: any; direction: string }, + graph: { + id?: string; + layoutOptions?: { + 'elk.hierarchyHandling': string; + 'elk.algorithm': any; + 'nodePlacement.strategy': any; + 'elk.layered.mergeEdges': any; + 'elk.direction': string; + 'spacing.baseValue': number; + }; + children?: never[]; + edges: any; + }, + svg: { + insert: (arg0: string) => { + (): any; + new (): any; + attr: { (arg0: string, arg1: string): any; new (): any }; + }; + } + ) { + log.info('abc78 DAGA edges = ', dataForLayout); + const edges = dataForLayout.edges; + const labelsEl = svg.insert('g').attr('class', 'edgeLabels'); + const linkIdCnt: any = {}; + const dir = dataForLayout.direction || 'DOWN'; + let defaultStyle: string | undefined; + let defaultLabelStyle: string | undefined; + + await Promise.all( + edges.map(async function (edge: { + id: string; + start: string; + end: string; + length: number; + text: undefined; + label: any; + type: string; + stroke: any; + interpolate: undefined; + style: undefined; + labelType: any; + }) { + // 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: any = { 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'; + } - let style = ''; - let labelStyle = ''; + // 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; + } - 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; - } + 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; + } - edgeData.style = edgeData.style += style; - edgeData.labelStyle = edgeData.labelStyle += 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 { - // @ts-ignore TODO: fix this - edgeData.curve = interpolateToCurve(conf.curve, curveLinear); - } + 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 { + // @ts-ignore TODO: fix this + edgeData.curve = interpolateToCurve(conf.curve, curveLinear); + } - if (edge.text === undefined) { - if (edge.style !== undefined) { + if (edge.text === undefined) { + if (edge.style !== undefined) { + edgeData.arrowheadStyle = 'fill: #333'; + } + } else { edgeData.arrowheadStyle = 'fill: #333'; + edgeData.labelpos = 'c'; } - } 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.labelType = edge.labelType; + edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n'); - edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:'); - - edgeData.id = linkId; - edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd; - - const labelEl = await insertEdgeLabel(labelsEl, edgeData); + if (edge.style === undefined) { + edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;'; + } - // 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({ - // @ts-ignore TODO: fix this - 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.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({ + // @ts-ignore TODO: fix this + 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, - }); - }) - ); - return graph; -}; + ], + edgeData, + }); + }) + ); + return graph; + }; -function dir2ElkDirection(dir: any) { - switch (dir) { - case 'LR': - return 'RIGHT'; - case 'RL': - return 'LEFT'; - case 'TB': - return 'DOWN'; - case 'BT': - return 'UP'; - default: - return 'DOWN'; + function dir2ElkDirection(dir: any) { + switch (dir) { + case 'LR': + return 'RIGHT'; + case 'RL': + return 'LEFT'; + case 'TB': + return 'DOWN'; + case 'BT': + return 'UP'; + default: + return 'DOWN'; + } } -} -function setIncludeChildrenPolicy(nodeId: string, ancestorId: string) { - const node = nodeDb[nodeId]; + function setIncludeChildrenPolicy(nodeId: string, ancestorId: string) { + const node = nodeDb[nodeId]; - if (!node) { - return; - } - if (node?.layoutOptions === undefined) { - node.layoutOptions = {}; - } - node.layoutOptions['elk.hierarchyHandling'] = 'INCLUDE_CHILDREN'; - if (node.id !== ancestorId) { - setIncludeChildrenPolicy(node.parentId, ancestorId); - } -} - -function intersectLine( - p1: { y: number; x: number }, - p2: { y: number; x: number }, - q1: { x: any; y: any }, - q2: { x: any; y: any } -) { - log.debug('UIO intersectLine', p1, p2, q1, q2); - // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, - // p7 and p473. - - // let a1, a2, b1, b2, c1, c2; - // let r1, r2, r3, r4; - // let denom, offset, num; - // let x, y; - - // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + - // b1 y + c1 = 0. - const a1 = p2.y - p1.y; - const b1 = p1.x - p2.x; - const c1 = p2.x * p1.y - p1.x * p2.y; - - // Compute r3 and r4. - const r3 = a1 * q1.x + b1 * q1.y + c1; - const r4 = a1 * q2.x + b1 * q2.y + c1; - - // Check signs of r3 and r4. If both point 3 and point 4 lie on - // same side of line 1, the line segments do not intersect. - if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { - return /*DON'T_INTERSECT*/; + if (!node) { + return; + } + if (node?.layoutOptions === undefined) { + node.layoutOptions = {}; + } + node.layoutOptions['elk.hierarchyHandling'] = 'INCLUDE_CHILDREN'; + if (node.id !== ancestorId) { + setIncludeChildrenPolicy(node.parentId, ancestorId); + } } - // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0 - const a2 = q2.y - q1.y; - const b2 = q1.x - q2.x; - const c2 = q2.x * q1.y - q1.x * q2.y; + function intersectLine( + p1: { y: number; x: number }, + p2: { y: number; x: number }, + q1: { x: any; y: any }, + q2: { x: any; y: any } + ) { + log.debug('UIO intersectLine', p1, p2, q1, q2); + // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, + // p7 and p473. + + // let a1, a2, b1, b2, c1, c2; + // let r1, r2, r3, r4; + // let denom, offset, num; + // let x, y; + + // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + + // b1 y + c1 = 0. + const a1 = p2.y - p1.y; + const b1 = p1.x - p2.x; + const c1 = p2.x * p1.y - p1.x * p2.y; + + // Compute r3 and r4. + const r3 = a1 * q1.x + b1 * q1.y + c1; + const r4 = a1 * q2.x + b1 * q2.y + c1; + + // Check signs of r3 and r4. If both point 3 and point 4 lie on + // same side of line 1, the line segments do not intersect. + if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { + return /*DON'T_INTERSECT*/; + } - // Compute r1 and r2 - const r1 = a2 * p1.x + b2 * p1.y + c2; - const r2 = a2 * p2.x + b2 * p2.y + c2; + // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0 + const a2 = q2.y - q1.y; + const b2 = q1.x - q2.x; + const c2 = q2.x * q1.y - q1.x * q2.y; - // Check signs of r1 and r2. If both point 1 and point 2 lie - // on same side of second line segment, the line segments do - // not intersect. - if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) { - return /*DON'T_INTERSECT*/; - } + // Compute r1 and r2 + const r1 = a2 * p1.x + b2 * p1.y + c2; + const r2 = a2 * p2.x + b2 * p2.y + c2; - // Line segments intersect: compute intersection point. - const denom = a1 * b2 - a2 * b1; - if (denom === 0) { - return /*COLLINEAR*/; - } + // Check signs of r1 and r2. If both point 1 and point 2 lie + // on same side of second line segment, the line segments do + // not intersect. + if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) { + return /*DON'T_INTERSECT*/; + } + + // Line segments intersect: compute intersection point. + const denom = a1 * b2 - a2 * b1; + if (denom === 0) { + return /*COLLINEAR*/; + } - const offset = Math.abs(denom / 2); + const offset = Math.abs(denom / 2); - // The denom/2 is to get rounding instead of truncating. It - // is added or subtracted to the numerator, depending upon the - // sign of the numerator. - let num = b1 * c2 - b2 * c1; - const x = num < 0 ? (num - offset) / denom : (num + offset) / denom; + // The denom/2 is to get rounding instead of truncating. It + // is added or subtracted to the numerator, depending upon the + // sign of the numerator. + let num = b1 * c2 - b2 * c1; + const x = num < 0 ? (num - offset) / denom : (num + offset) / denom; - num = a2 * c1 - a1 * c2; - const y = num < 0 ? (num - offset) / denom : (num + offset) / denom; + num = a2 * c1 - a1 * c2; + const y = num < 0 ? (num - offset) / denom : (num + offset) / denom; - return { x: x, y: y }; -} + return { x: x, y: y }; + } -function sameSign(r1: number, r2: number) { - return r1 * r2 > 0; -} -const diamondIntersection = ( - bounds: { x: any; y: any; width: any; height: any }, - outsidePoint: { x: number; y: number }, - insidePoint: any -) => { - const x1 = bounds.x; - const y1 = bounds.y; - - const w = bounds.width; //+ bounds.padding; - const h = bounds.height; // + bounds.padding; - - const polyPoints = [ - { x: x1, y: y1 - h / 2 }, - { x: x1 + w / 2, y: y1 }, - { x: x1, y: y1 + h / 2 }, - { x: x1 - w / 2, y: y1 }, - ]; - log.debug( - `UIO diamondIntersection calc abc89: + function sameSign(r1: number, r2: number) { + return r1 * r2 > 0; + } + const diamondIntersection = ( + bounds: { x: any; y: any; width: any; height: any }, + outsidePoint: { x: number; y: number }, + insidePoint: any + ) => { + const x1 = bounds.x; + const y1 = bounds.y; + + const w = bounds.width; //+ bounds.padding; + const h = bounds.height; // + bounds.padding; + + const polyPoints = [ + { x: x1, y: y1 - h / 2 }, + { x: x1 + w / 2, y: y1 }, + { x: x1, y: y1 + h / 2 }, + { x: x1 - w / 2, y: y1 }, + ]; + log.debug( + `UIO diamondIntersection calc abc89: outsidePoint: ${JSON.stringify(outsidePoint)} insidePoint : ${JSON.stringify(insidePoint)} node : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`, - polyPoints - ); + polyPoints + ); - const intersections = []; + const intersections = []; - let minX = Number.POSITIVE_INFINITY; - let minY = Number.POSITIVE_INFINITY; + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; - polyPoints.forEach(function (entry) { - minX = Math.min(minX, entry.x); - minY = Math.min(minY, entry.y); - }); + polyPoints.forEach(function (entry) { + minX = Math.min(minX, entry.x); + minY = Math.min(minY, entry.y); + }); - // const left = x1 - w / 2; - // const top = y1 + h / 2; - - for (let i = 0; i < polyPoints.length; i++) { - const p1 = polyPoints[i]; - const p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0]; - const intersect = intersectLine( - bounds, - outsidePoint, - { x: p1.x, y: p1.y }, - { x: p2.x, y: p2.y } - ); + // const left = x1 - w / 2; + // const top = y1 + h / 2; + + for (let i = 0; i < polyPoints.length; i++) { + const p1 = polyPoints[i]; + const p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0]; + const intersect = intersectLine( + bounds, + outsidePoint, + { x: p1.x, y: p1.y }, + { x: p2.x, y: p2.y } + ); - if (intersect) { - intersections.push(intersect); + if (intersect) { + intersections.push(intersect); + } } - } - if (!intersections.length) { - return bounds; - } + if (!intersections.length) { + return bounds; + } - log.debug('UIO intersections', intersections); + log.debug('UIO intersections', intersections); - if (intersections.length > 1) { - // More intersections, find the one nearest to edge end point - intersections.sort(function (p, q) { - const pdx = p.x - outsidePoint.x; - const pdy = p.y - outsidePoint.y; - const distp = Math.sqrt(pdx * pdx + pdy * pdy); + if (intersections.length > 1) { + // More intersections, find the one nearest to edge end point + intersections.sort(function (p, q) { + const pdx = p.x - outsidePoint.x; + const pdy = p.y - outsidePoint.y; + const distp = Math.sqrt(pdx * pdx + pdy * pdy); - const qdx = q.x - outsidePoint.x; - const qdy = q.y - outsidePoint.y; - const distq = Math.sqrt(qdx * qdx + qdy * qdy); + const qdx = q.x - outsidePoint.x; + const qdy = q.y - outsidePoint.y; + const distq = Math.sqrt(qdx * qdx + qdy * qdy); - return distp < distq ? -1 : distp === distq ? 0 : 1; - }); - } + return distp < distq ? -1 : distp === distq ? 0 : 1; + }); + } - return intersections[0]; -}; + return intersections[0]; + }; -const intersection = ( - node: { x: any; y: any; width: number; height: number }, - outsidePoint: { x: number; y: number }, - insidePoint: { x: number; y: number } -) => { - log.debug(`intersection calc abc89: + const intersection = ( + node: { x: any; y: any; width: number; height: number }, + outsidePoint: { x: number; y: number }, + insidePoint: { x: number; y: number } + ) => { + log.debug(`intersection calc abc89: outsidePoint: ${JSON.stringify(outsidePoint)} insidePoint : ${JSON.stringify(insidePoint)} node : x:${node.x} y:${node.y} w:${node.width} h:${node.height}`); - const x = node.x; - const y = node.y; - - const dx = Math.abs(x - insidePoint.x); - // const dy = Math.abs(y - insidePoint.y); - const w = node.width / 2; - let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; - const h = node.height / 2; - - const Q = Math.abs(outsidePoint.y - insidePoint.y); - const R = Math.abs(outsidePoint.x - insidePoint.x); - - if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { - // Intersection is top or bottom of rect. - const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y; - r = (R * q) / Q; - const res = { - x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r, - y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q, - }; + const x = node.x; + const y = node.y; + + const dx = Math.abs(x - insidePoint.x); + // const dy = Math.abs(y - insidePoint.y); + const w = node.width / 2; + let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; + const h = node.height / 2; + + const Q = Math.abs(outsidePoint.y - insidePoint.y); + const R = Math.abs(outsidePoint.x - insidePoint.x); + + if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { + // Intersection is top or bottom of rect. + const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y; + r = (R * q) / Q; + const res = { + x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r, + y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q, + }; - if (r === 0) { - res.x = outsidePoint.x; - res.y = outsidePoint.y; - } - if (R === 0) { - res.x = outsidePoint.x; - } - if (Q === 0) { - res.y = outsidePoint.y; - } + if (r === 0) { + res.x = outsidePoint.x; + res.y = outsidePoint.y; + } + if (R === 0) { + res.x = outsidePoint.x; + } + if (Q === 0) { + res.y = outsidePoint.y; + } - log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line + log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line - return res; - } else { - // Intersection onn sides of rect - if (insidePoint.x < outsidePoint.x) { - r = outsidePoint.x - w - x; + return res; } else { - // r = outsidePoint.x - w - x; - r = x - w - outsidePoint.x; - } - const q = (Q * r) / R; - // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w; - // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; - let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; - // let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; - let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; - log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y }); - if (r === 0) { - _x = outsidePoint.x; - _y = outsidePoint.y; - } - if (R === 0) { - _x = outsidePoint.x; + // Intersection onn sides of rect + if (insidePoint.x < outsidePoint.x) { + r = outsidePoint.x - w - x; + } else { + // r = outsidePoint.x - w - x; + r = x - w - outsidePoint.x; + } + const q = (Q * r) / R; + // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w; + // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; + let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; + // let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; + let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; + log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y }); + if (r === 0) { + _x = outsidePoint.x; + _y = outsidePoint.y; + } + if (R === 0) { + _x = outsidePoint.x; + } + if (Q === 0) { + _y = outsidePoint.y; + } + + return { x: _x, y: _y }; } - if (Q === 0) { - _y = outsidePoint.y; + }; + const outsideNode = ( + node: { x: any; y: any; width: number; height: number }, + point: { x: number; y: number } + ) => { + const x = node.x; + const y = node.y; + const dx = Math.abs(point.x - x); + const dy = Math.abs(point.y - y); + const w = node.width / 2; + const h = node.height / 2; + if (dx >= w || dy >= h) { + return true; } - - return { x: _x, y: _y }; - } -}; -const outsideNode = ( - node: { x: any; y: any; width: number; height: number }, - point: { x: number; y: number } -) => { - const x = node.x; - const y = node.y; - const dx = Math.abs(point.x - x); - const dy = Math.abs(point.y - y); - const w = node.width / 2; - const h = node.height / 2; - if (dx >= w || dy >= h) { - return true; - } - return false; -}; -/** - * This function will page a path and node where the last point(s) in the path is inside the node - * and return an update path ending by the border of the node. - */ -const cutPathAtIntersect = ( - _points: any[], - bounds: { x: any; y: any; width: any; height: any; padding: any }, - isDiamond: boolean -) => { - log.debug('UIO cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond); - const points: any[] = []; - let lastPointOutside = _points[0]; - let isInside = false; - _points.forEach((point: any) => { - // const node = clusterDb[edge.toCluster].node; - log.debug(' checking point', point, bounds); - - // check if point is inside the boundary rect - if (!outsideNode(bounds, point) && !isInside) { - // First point inside the rect found - // Calc the intersection coord between the point anf the last point outside the rect - let inter; - - if (isDiamond) { - const inter2 = diamondIntersection(bounds, lastPointOutside, point); - const distance = Math.sqrt( - (lastPointOutside.x - inter2.x) ** 2 + (lastPointOutside.y - inter2.y) ** 2 - ); - if (distance > 1) { - inter = inter2; + return false; + }; + /** + * This function will page a path and node where the last point(s) in the path is inside the node + * and return an update path ending by the border of the node. + */ + const cutPathAtIntersect = ( + _points: any[], + bounds: { x: any; y: any; width: any; height: any; padding: any }, + isDiamond: boolean + ) => { + log.debug('UIO cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond); + const points: any[] = []; + let lastPointOutside = _points[0]; + let isInside = false; + _points.forEach((point: any) => { + // const node = clusterDb[edge.toCluster].node; + log.debug(' checking point', point, bounds); + + // check if point is inside the boundary rect + if (!outsideNode(bounds, point) && !isInside) { + // First point inside the rect found + // Calc the intersection coord between the point anf the last point outside the rect + let inter; + + if (isDiamond) { + const inter2 = diamondIntersection(bounds, lastPointOutside, point); + const distance = Math.sqrt( + (lastPointOutside.x - inter2.x) ** 2 + (lastPointOutside.y - inter2.y) ** 2 + ); + if (distance > 1) { + inter = inter2; + } + } + if (!inter) { + inter = intersection(bounds, lastPointOutside, point); } - } - if (!inter) { - inter = intersection(bounds, lastPointOutside, point); - } - // Check case where the intersection is the same as the last point - let pointPresent = false; - points.forEach((p) => { - pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y); - }); - // if (!pointPresent) { - if (!points.some((e) => e.x === inter.x && e.y === inter.y)) { - points.push(inter); + // Check case where the intersection is the same as the last point + let pointPresent = false; + points.forEach((p) => { + pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y); + }); + // if (!pointPresent) { + if (!points.some((e) => e.x === inter.x && e.y === inter.y)) { + points.push(inter); + } else { + log.debug('abc88 no intersect', inter, points); + } + // points.push(inter); + isInside = true; } else { - log.debug('abc88 no intersect', inter, points); - } - // points.push(inter); - isInside = true; - } else { - // Outside - log.debug('abc88 outside', point, lastPointOutside, points); - lastPointOutside = point; - // points.push(point); - if (!isInside) { - points.push(point); + // Outside + log.debug('abc88 outside', point, lastPointOutside, points); + lastPointOutside = point; + // points.push(point); + if (!isInside) { + points.push(point); + } } - } - }); - log.debug('returning points', points); - return points; -}; + }); + log.debug('returning points', points); + return points; + }; -export const render = async ( - data4Layout: LayoutData, - svg: { - insert: (arg0: string) => { - (): any; - new (): any; - attr: { (arg0: string, arg1: string): any; new (): any }; - }; - }, - element: any, - algorithm: any -) => { // @ts-ignore - ELK is not typed const elk = new ELK(); diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index aa07375927..90b3b74b81 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -4,34 +4,35 @@ */ import { dedent } from 'ts-dedent'; import type { MermaidConfig } from './config.type.js'; -import { log } from './logger.js'; -import utils from './utils.js'; -import type { ParseOptions, ParseResult, RenderResult } from './types.js'; -import { mermaidAPI } from './mermaidAPI.js'; -import { registerLazyLoadedDiagrams, detectType } from './diagram-api/detectType.js'; +import { detectType, registerLazyLoadedDiagrams } from './diagram-api/detectType.js'; +import { addDiagrams } from './diagram-api/diagram-orchestration.js'; import { loadRegisteredDiagrams } from './diagram-api/loadDiagram.js'; -import type { ParseErrorFunction } from './Diagram.js'; -import { isDetailedError } from './utils.js'; -import type { DetailedError } from './utils.js'; import type { ExternalDiagramDefinition } from './diagram-api/types.js'; +import type { ParseErrorFunction } from './Diagram.js'; import type { UnknownDiagramError } from './errors.js'; -import { addDiagrams } from './diagram-api/diagram-orchestration.js'; -import { registerLayoutLoaders } from './rendering-util/render.js'; +import type { internalHelpers } from './internals.js'; +import { log } from './logger.js'; +import { mermaidAPI } from './mermaidAPI.js'; import type { LayoutLoaderDefinition } from './rendering-util/render.js'; -import { internalHelpers } from './internals.js'; +import { registerLayoutLoaders } from './rendering-util/render.js'; import type { LayoutData } from './rendering-util/types.js'; +import type { ParseOptions, ParseResult, RenderResult } from './types.js'; +import type { DetailedError } from './utils.js'; +import utils, { isDetailedError } from './utils.js'; +type InternalHelpers = typeof internalHelpers; export type { - MermaidConfig, DetailedError, ExternalDiagramDefinition, + InternalHelpers, + LayoutData, + LayoutLoaderDefinition, + MermaidConfig, ParseErrorFunction, - RenderResult, ParseOptions, ParseResult, + RenderResult, UnknownDiagramError, - LayoutLoaderDefinition, - LayoutData, }; export interface RunOptions { @@ -432,11 +433,6 @@ export interface Mermaid { contentLoaded: typeof contentLoaded; setParseErrorHandler: typeof setParseErrorHandler; detectType: typeof detectType; - /** - * Internal helpers for mermaid - * @deprecated - This should not be used by external packages, as the definitions will change without notice. - */ - internalHelpers: typeof internalHelpers; } const mermaid: Mermaid = { @@ -453,7 +449,6 @@ const mermaid: Mermaid = { contentLoaded, setParseErrorHandler, detectType, - internalHelpers, }; export default mermaid; diff --git a/packages/mermaid/src/rendering-util/render.ts b/packages/mermaid/src/rendering-util/render.ts index 4c08b5d27e..c2cbc994cf 100644 --- a/packages/mermaid/src/rendering-util/render.ts +++ b/packages/mermaid/src/rendering-util/render.ts @@ -1,7 +1,14 @@ +import { internalHelpers } from '$root/internals.js'; import { log } from '$root/logger.js'; export interface LayoutAlgorithm { - render(data4Layout: any, svg: any, element: any, algorithm?: string): any; + render( + data4Layout: any, + svg: any, + element: any, + helpers: typeof internalHelpers, + algorithm?: string + ): any; } export type LayoutLoader = () => Promise; @@ -38,7 +45,13 @@ export const render = async (data4Layout: any, svg: any, element: any) => { const layoutDefinition = layoutAlgorithms[data4Layout.layoutAlgorithm]; const layoutRenderer = await layoutDefinition.loader(); - return layoutRenderer.render(data4Layout, svg, element, layoutDefinition.algorithm); + return layoutRenderer.render( + data4Layout, + svg, + element, + internalHelpers, + layoutDefinition.algorithm + ); }; /**