mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 14:29:25 +02:00
feat: Inject internal helpers into render function
This commit is contained in:
@@ -1,9 +1,19 @@
|
|||||||
import { curveLinear } from 'd3';
|
import { curveLinear } from 'd3';
|
||||||
import ELK from 'elkjs/lib/elk.bundled.js';
|
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';
|
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
|
||||||
|
|
||||||
const {
|
export const render = async (
|
||||||
|
data4Layout: LayoutData,
|
||||||
|
svg: {
|
||||||
|
insert: (arg0: string) => {
|
||||||
|
(): any;
|
||||||
|
new (): any;
|
||||||
|
attr: { (arg0: string, arg1: string): any; new (): any };
|
||||||
|
};
|
||||||
|
},
|
||||||
|
element: any,
|
||||||
|
{
|
||||||
common,
|
common,
|
||||||
getConfig,
|
getConfig,
|
||||||
insertCluster,
|
insertCluster,
|
||||||
@@ -15,13 +25,14 @@ const {
|
|||||||
labelHelper,
|
labelHelper,
|
||||||
log,
|
log,
|
||||||
positionEdgeLabel,
|
positionEdgeLabel,
|
||||||
} = mermaid.internalHelpers;
|
}: InternalHelpers,
|
||||||
|
algorithm: any
|
||||||
|
) => {
|
||||||
|
const nodeDb: Record<string, any> = {};
|
||||||
|
const portPos: Record<string, any> = {};
|
||||||
|
const clusterDb: Record<string, any> = {};
|
||||||
|
|
||||||
const nodeDb: Record<string, any> = {};
|
const addVertex = async (nodeEl: any, graph: { children: any[] }, nodeArr: any, node: any) => {
|
||||||
const portPos: Record<string, any> = {};
|
|
||||||
const clusterDb: Record<string, any> = {};
|
|
||||||
|
|
||||||
const addVertex = async (nodeEl: any, graph: { children: any[] }, nodeArr: any, node: any) => {
|
|
||||||
const labelData: any = { width: 0, height: 0 };
|
const labelData: any = { width: 0, height: 0 };
|
||||||
// const ports = [
|
// const ports = [
|
||||||
// {
|
// {
|
||||||
@@ -88,9 +99,9 @@ const addVertex = async (nodeEl: any, graph: { children: any[] }, nodeArr: any,
|
|||||||
child.labelData = labelData;
|
child.labelData = labelData;
|
||||||
child.domId = nodeEl;
|
child.domId = nodeEl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addVertices = async function (
|
const addVertices = async function (
|
||||||
nodeEl: any,
|
nodeEl: any,
|
||||||
nodeArr: any[],
|
nodeArr: any[],
|
||||||
graph: {
|
graph: {
|
||||||
@@ -107,7 +118,7 @@ const addVertices = async function (
|
|||||||
edges: never[];
|
edges: never[];
|
||||||
},
|
},
|
||||||
parentId?: undefined
|
parentId?: undefined
|
||||||
) {
|
) {
|
||||||
const siblings = nodeArr.filter((node: { parentId: any }) => node.parentId === parentId);
|
const siblings = nodeArr.filter((node: { parentId: any }) => node.parentId === parentId);
|
||||||
log.info('addVertices APA12', siblings, parentId);
|
log.info('addVertices APA12', siblings, parentId);
|
||||||
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
||||||
@@ -117,9 +128,9 @@ const addVertices = async function (
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
return graph;
|
return graph;
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawNodes = async (
|
const drawNodes = async (
|
||||||
relX: number,
|
relX: number,
|
||||||
relY: number,
|
relY: number,
|
||||||
nodeArray: any[],
|
nodeArray: any[],
|
||||||
@@ -132,7 +143,7 @@ const drawNodes = async (
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
depth: number
|
depth: number
|
||||||
) => {
|
) => {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
nodeArray.map(async function (node: {
|
nodeArray.map(async function (node: {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
@@ -195,9 +206,9 @@ const drawNodes = async (
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNextPort = (node: string | number, edgeDirection: string, graphDirection: any) => {
|
const getNextPort = (node: string | number, edgeDirection: string, graphDirection: any) => {
|
||||||
log.info('getNextPort abc88', { node, edgeDirection, graphDirection });
|
log.info('getNextPort abc88', { node, edgeDirection, graphDirection });
|
||||||
if (!portPos[node]) {
|
if (!portPos[node]) {
|
||||||
switch (graphDirection) {
|
switch (graphDirection) {
|
||||||
@@ -246,9 +257,9 @@ const getNextPort = (node: string | number, edgeDirection: string, graphDirectio
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addSubGraphs = (nodeArr: any[]): TreeData => {
|
const addSubGraphs = (nodeArr: any[]): TreeData => {
|
||||||
const parentLookupDb: TreeData = { parentById: {}, childrenById: {} };
|
const parentLookupDb: TreeData = { parentById: {}, childrenById: {} };
|
||||||
const subgraphs = nodeArr.filter((node: { isGroup: any }) => node.isGroup);
|
const subgraphs = nodeArr.filter((node: { isGroup: any }) => node.isGroup);
|
||||||
log.info('Subgraphs - ', subgraphs);
|
log.info('Subgraphs - ', subgraphs);
|
||||||
@@ -270,9 +281,9 @@ const addSubGraphs = (nodeArr: any[]): TreeData => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return parentLookupDb;
|
return parentLookupDb;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getEdgeStartEndPoint = (edge: any, dir: any) => {
|
const getEdgeStartEndPoint = (edge: any, dir: any) => {
|
||||||
let source: any = edge.start;
|
let source: any = edge.start;
|
||||||
let target: any = edge.end;
|
let target: any = edge.end;
|
||||||
|
|
||||||
@@ -297,9 +308,9 @@ const getEdgeStartEndPoint = (edge: any, dir: any) => {
|
|||||||
|
|
||||||
// Add the edge to the graph
|
// Add the edge to the graph
|
||||||
return { source, target, sourceId, targetId };
|
return { source, target, sourceId, targetId };
|
||||||
};
|
};
|
||||||
|
|
||||||
const calcOffset = function (src: string, dest: string, parentLookupDb: TreeData) {
|
const calcOffset = function (src: string, dest: string, parentLookupDb: TreeData) {
|
||||||
const ancestor = findCommonAncestor(src, dest, parentLookupDb);
|
const ancestor = findCommonAncestor(src, dest, parentLookupDb);
|
||||||
if (ancestor === undefined || ancestor === 'root') {
|
if (ancestor === undefined || ancestor === 'root') {
|
||||||
return { x: 0, y: 0 };
|
return { x: 0, y: 0 };
|
||||||
@@ -307,12 +318,12 @@ const calcOffset = function (src: string, dest: string, parentLookupDb: TreeData
|
|||||||
|
|
||||||
const ancestorOffset = nodeDb[ancestor].offset;
|
const ancestorOffset = nodeDb[ancestor].offset;
|
||||||
return { x: ancestorOffset.posX, y: ancestorOffset.posY };
|
return { x: ancestorOffset.posX, y: ancestorOffset.posY };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add edges to graph based on parsed graph definition
|
* Add edges to graph based on parsed graph definition
|
||||||
*/
|
*/
|
||||||
const addEdges = async function (
|
const addEdges = async function (
|
||||||
dataForLayout: { edges: any; direction: string },
|
dataForLayout: { edges: any; direction: string },
|
||||||
graph: {
|
graph: {
|
||||||
id?: string;
|
id?: string;
|
||||||
@@ -334,7 +345,7 @@ const addEdges = async function (
|
|||||||
attr: { (arg0: string, arg1: string): any; new (): any };
|
attr: { (arg0: string, arg1: string): any; new (): any };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
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');
|
||||||
@@ -501,9 +512,9 @@ const addEdges = async function (
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
return graph;
|
return graph;
|
||||||
};
|
};
|
||||||
|
|
||||||
function dir2ElkDirection(dir: any) {
|
function dir2ElkDirection(dir: any) {
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case 'LR':
|
case 'LR':
|
||||||
return 'RIGHT';
|
return 'RIGHT';
|
||||||
@@ -516,9 +527,9 @@ function dir2ElkDirection(dir: any) {
|
|||||||
default:
|
default:
|
||||||
return 'DOWN';
|
return 'DOWN';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setIncludeChildrenPolicy(nodeId: string, ancestorId: string) {
|
function setIncludeChildrenPolicy(nodeId: string, ancestorId: string) {
|
||||||
const node = nodeDb[nodeId];
|
const node = nodeDb[nodeId];
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
@@ -531,14 +542,14 @@ function setIncludeChildrenPolicy(nodeId: string, ancestorId: string) {
|
|||||||
if (node.id !== ancestorId) {
|
if (node.id !== ancestorId) {
|
||||||
setIncludeChildrenPolicy(node.parentId, ancestorId);
|
setIncludeChildrenPolicy(node.parentId, ancestorId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function intersectLine(
|
function intersectLine(
|
||||||
p1: { y: number; x: number },
|
p1: { y: number; x: number },
|
||||||
p2: { y: number; x: number },
|
p2: { y: number; x: number },
|
||||||
q1: { x: any; y: any },
|
q1: { x: any; y: any },
|
||||||
q2: { x: any; y: any }
|
q2: { x: any; y: any }
|
||||||
) {
|
) {
|
||||||
log.debug('UIO intersectLine', p1, p2, q1, q2);
|
log.debug('UIO intersectLine', p1, p2, q1, q2);
|
||||||
// Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
|
// Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
|
||||||
// p7 and p473.
|
// p7 and p473.
|
||||||
@@ -598,16 +609,16 @@ function intersectLine(
|
|||||||
const y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
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) {
|
function sameSign(r1: number, r2: number) {
|
||||||
return r1 * r2 > 0;
|
return r1 * r2 > 0;
|
||||||
}
|
}
|
||||||
const diamondIntersection = (
|
const diamondIntersection = (
|
||||||
bounds: { x: any; y: any; width: any; height: any },
|
bounds: { x: any; y: any; width: any; height: any },
|
||||||
outsidePoint: { x: number; y: number },
|
outsidePoint: { x: number; y: number },
|
||||||
insidePoint: any
|
insidePoint: any
|
||||||
) => {
|
) => {
|
||||||
const x1 = bounds.x;
|
const x1 = bounds.x;
|
||||||
const y1 = bounds.y;
|
const y1 = bounds.y;
|
||||||
|
|
||||||
@@ -678,13 +689,13 @@ const diamondIntersection = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return intersections[0];
|
return intersections[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const intersection = (
|
const intersection = (
|
||||||
node: { x: any; y: any; width: number; height: number },
|
node: { x: any; y: any; width: number; height: number },
|
||||||
outsidePoint: { x: number; y: number },
|
outsidePoint: { x: number; y: number },
|
||||||
insidePoint: { x: number; y: number }
|
insidePoint: { x: number; y: number }
|
||||||
) => {
|
) => {
|
||||||
log.debug(`intersection calc abc89:
|
log.debug(`intersection calc abc89:
|
||||||
outsidePoint: ${JSON.stringify(outsidePoint)}
|
outsidePoint: ${JSON.stringify(outsidePoint)}
|
||||||
insidePoint : ${JSON.stringify(insidePoint)}
|
insidePoint : ${JSON.stringify(insidePoint)}
|
||||||
@@ -752,11 +763,11 @@ const intersection = (
|
|||||||
|
|
||||||
return { x: _x, y: _y };
|
return { x: _x, y: _y };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const outsideNode = (
|
const outsideNode = (
|
||||||
node: { x: any; y: any; width: number; height: number },
|
node: { x: any; y: any; width: number; height: number },
|
||||||
point: { x: number; y: number }
|
point: { x: number; y: number }
|
||||||
) => {
|
) => {
|
||||||
const x = node.x;
|
const x = node.x;
|
||||||
const y = node.y;
|
const y = node.y;
|
||||||
const dx = Math.abs(point.x - x);
|
const dx = Math.abs(point.x - x);
|
||||||
@@ -767,16 +778,16 @@ const outsideNode = (
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* This function will page a path and node where the last point(s) in the path is inside the node
|
* 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.
|
* and return an update path ending by the border of the node.
|
||||||
*/
|
*/
|
||||||
const cutPathAtIntersect = (
|
const cutPathAtIntersect = (
|
||||||
_points: any[],
|
_points: any[],
|
||||||
bounds: { x: any; y: any; width: any; height: any; padding: any },
|
bounds: { x: any; y: any; width: any; height: any; padding: any },
|
||||||
isDiamond: boolean
|
isDiamond: boolean
|
||||||
) => {
|
) => {
|
||||||
log.debug('UIO cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond);
|
log.debug('UIO cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond);
|
||||||
const points: any[] = [];
|
const points: any[] = [];
|
||||||
let lastPointOutside = _points[0];
|
let lastPointOutside = _points[0];
|
||||||
@@ -829,20 +840,8 @@ const cutPathAtIntersect = (
|
|||||||
});
|
});
|
||||||
log.debug('returning points', points);
|
log.debug('returning points', points);
|
||||||
return 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
|
// @ts-ignore - ELK is not typed
|
||||||
const elk = new ELK();
|
const elk = new ELK();
|
||||||
|
|
||||||
|
@@ -4,34 +4,35 @@
|
|||||||
*/
|
*/
|
||||||
import { dedent } from 'ts-dedent';
|
import { dedent } from 'ts-dedent';
|
||||||
import type { MermaidConfig } from './config.type.js';
|
import type { MermaidConfig } from './config.type.js';
|
||||||
import { log } from './logger.js';
|
import { detectType, registerLazyLoadedDiagrams } from './diagram-api/detectType.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 { 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 { UnknownDiagramError } from './errors.js';
|
|
||||||
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
||||||
import { registerLayoutLoaders } from './rendering-util/render.js';
|
import { loadRegisteredDiagrams } from './diagram-api/loadDiagram.js';
|
||||||
|
import type { ExternalDiagramDefinition } from './diagram-api/types.js';
|
||||||
|
import type { ParseErrorFunction } from './Diagram.js';
|
||||||
|
import type { UnknownDiagramError } from './errors.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 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 { 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 {
|
export type {
|
||||||
MermaidConfig,
|
|
||||||
DetailedError,
|
DetailedError,
|
||||||
ExternalDiagramDefinition,
|
ExternalDiagramDefinition,
|
||||||
|
InternalHelpers,
|
||||||
|
LayoutData,
|
||||||
|
LayoutLoaderDefinition,
|
||||||
|
MermaidConfig,
|
||||||
ParseErrorFunction,
|
ParseErrorFunction,
|
||||||
RenderResult,
|
|
||||||
ParseOptions,
|
ParseOptions,
|
||||||
ParseResult,
|
ParseResult,
|
||||||
|
RenderResult,
|
||||||
UnknownDiagramError,
|
UnknownDiagramError,
|
||||||
LayoutLoaderDefinition,
|
|
||||||
LayoutData,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface RunOptions {
|
export interface RunOptions {
|
||||||
@@ -432,11 +433,6 @@ export interface Mermaid {
|
|||||||
contentLoaded: typeof contentLoaded;
|
contentLoaded: typeof contentLoaded;
|
||||||
setParseErrorHandler: typeof setParseErrorHandler;
|
setParseErrorHandler: typeof setParseErrorHandler;
|
||||||
detectType: typeof detectType;
|
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 = {
|
const mermaid: Mermaid = {
|
||||||
@@ -453,7 +449,6 @@ const mermaid: Mermaid = {
|
|||||||
contentLoaded,
|
contentLoaded,
|
||||||
setParseErrorHandler,
|
setParseErrorHandler,
|
||||||
detectType,
|
detectType,
|
||||||
internalHelpers,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mermaid;
|
export default mermaid;
|
||||||
|
@@ -1,7 +1,14 @@
|
|||||||
|
import { internalHelpers } from '$root/internals.js';
|
||||||
import { log } from '$root/logger.js';
|
import { log } from '$root/logger.js';
|
||||||
|
|
||||||
export interface LayoutAlgorithm {
|
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<LayoutAlgorithm>;
|
export type LayoutLoader = () => Promise<LayoutAlgorithm>;
|
||||||
@@ -38,7 +45,13 @@ export const render = async (data4Layout: any, svg: any, element: any) => {
|
|||||||
|
|
||||||
const layoutDefinition = layoutAlgorithms[data4Layout.layoutAlgorithm];
|
const layoutDefinition = layoutAlgorithms[data4Layout.layoutAlgorithm];
|
||||||
const layoutRenderer = await layoutDefinition.loader();
|
const layoutRenderer = await layoutDefinition.loader();
|
||||||
return layoutRenderer.render(data4Layout, svg, element, layoutDefinition.algorithm);
|
return layoutRenderer.render(
|
||||||
|
data4Layout,
|
||||||
|
svg,
|
||||||
|
element,
|
||||||
|
internalHelpers,
|
||||||
|
layoutDefinition.algorithm
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user