mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 06:19:24 +02:00
chore: Refactor nodes.js to remove global state
This commit is contained in:
@@ -10,12 +10,7 @@ import {
|
||||
findNonClusterChild,
|
||||
sortNodesByHierarchy,
|
||||
} from './mermaid-graphlib.js';
|
||||
import {
|
||||
insertNode,
|
||||
positionNode,
|
||||
clear as clearNodes,
|
||||
setNodeElem,
|
||||
} from '../../rendering-elements/nodes.js';
|
||||
import { Nodes } from '../../rendering-elements/nodes.js';
|
||||
import { insertCluster, clear as clearClusters } from '../../rendering-elements/clusters.js';
|
||||
import {
|
||||
insertEdgeLabel,
|
||||
@@ -27,8 +22,16 @@ import { log } from '../../../logger.js';
|
||||
import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js';
|
||||
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||
|
||||
const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => {
|
||||
log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster);
|
||||
const recursiveRender = async (
|
||||
_elem,
|
||||
graph,
|
||||
diagramType,
|
||||
id,
|
||||
parentCluster,
|
||||
siteConfig,
|
||||
nodeData
|
||||
) => {
|
||||
log.debug('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster);
|
||||
const dir = graph.graph().rankdir;
|
||||
log.trace('Dir in recursive render - dir:', dir);
|
||||
|
||||
@@ -89,7 +92,8 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
diagramType,
|
||||
id,
|
||||
graph.node(v),
|
||||
siteConfig
|
||||
siteConfig,
|
||||
nodeData
|
||||
);
|
||||
const newEl = o.elem;
|
||||
updateNodeBounds(node, newEl);
|
||||
@@ -106,7 +110,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
// node.x,
|
||||
// node.y
|
||||
);
|
||||
setNodeElem(newEl, node);
|
||||
nodeData.setNodeElem(newEl, node);
|
||||
} else {
|
||||
if (graph.children(v).length > 0) {
|
||||
// This is a cluster but not to be rendered recursively
|
||||
@@ -125,7 +129,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
// insertCluster(clusters, graph.node(v));
|
||||
} else {
|
||||
log.trace('Node - the non recursive path XAX', v, nodes, graph.node(v), dir);
|
||||
await insertNode(nodes, graph.node(v), { config: siteConfig, dir });
|
||||
await nodeData.insertNode(nodes, graph.node(v), { config: siteConfig, dir });
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -194,7 +198,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
graph.parent(v)
|
||||
);
|
||||
clusterDb.get(node.id).node = node;
|
||||
positionNode(node);
|
||||
nodeData.positionNode(node);
|
||||
} else {
|
||||
// A tainted cluster node
|
||||
if (graph.children(v).length > 0) {
|
||||
@@ -239,7 +243,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
node
|
||||
);
|
||||
|
||||
positionNode(node);
|
||||
nodeData.positionNode(node);
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -264,7 +268,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
diff = n.diff;
|
||||
}
|
||||
});
|
||||
log.warn('Returning from recursive render XAX', elem, diff);
|
||||
log.debug('Returning from recursive render XAX', elem, diff);
|
||||
return { elem, diff };
|
||||
};
|
||||
|
||||
@@ -291,7 +295,6 @@ export const render = async (data4Layout, svg) => {
|
||||
});
|
||||
const element = svg.select('g');
|
||||
insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId);
|
||||
clearNodes();
|
||||
clearEdges();
|
||||
clearClusters();
|
||||
clearGraphlib();
|
||||
@@ -362,16 +365,18 @@ export const render = async (data4Layout, svg) => {
|
||||
}
|
||||
});
|
||||
|
||||
log.warn('Graph at first:', JSON.stringify(graphlibJson.write(graph)));
|
||||
log.debug('Graph at first:', JSON.stringify(graphlibJson.write(graph)));
|
||||
adjustClustersAndEdges(graph);
|
||||
log.warn('Graph after XAX:', JSON.stringify(graphlibJson.write(graph)));
|
||||
log.debug('Graph after XAX:', JSON.stringify(graphlibJson.write(graph)));
|
||||
const siteConfig = getConfig();
|
||||
const nodeData = new Nodes();
|
||||
await recursiveRender(
|
||||
element,
|
||||
graph,
|
||||
data4Layout.type,
|
||||
data4Layout.diagramId,
|
||||
undefined,
|
||||
siteConfig
|
||||
siteConfig,
|
||||
nodeData
|
||||
);
|
||||
};
|
||||
|
@@ -1,82 +0,0 @@
|
||||
import { log } from '../../logger.js';
|
||||
import { shapes } from './shapes.js';
|
||||
|
||||
const nodeElems = new Map();
|
||||
|
||||
export const insertNode = async (elem, node, renderOptions) => {
|
||||
let newEl;
|
||||
let el;
|
||||
|
||||
//special check for rect shape (with or without rounded corners)
|
||||
if (node.shape === 'rect') {
|
||||
if (node.rx && node.ry) {
|
||||
node.shape = 'roundedRect';
|
||||
} else {
|
||||
node.shape = 'squareRect';
|
||||
}
|
||||
}
|
||||
|
||||
const shapeHandler = shapes[node.shape];
|
||||
|
||||
if (!shapeHandler) {
|
||||
throw new Error(`No such shape: ${node.shape}. Please check your syntax.`);
|
||||
}
|
||||
|
||||
if (node.link) {
|
||||
// Add link when appropriate
|
||||
let target;
|
||||
if (renderOptions.config.securityLevel === 'sandbox') {
|
||||
target = '_top';
|
||||
} else if (node.linkTarget) {
|
||||
target = node.linkTarget || '_blank';
|
||||
}
|
||||
newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target);
|
||||
el = await shapeHandler(newEl, node, renderOptions);
|
||||
} else {
|
||||
el = await shapeHandler(elem, node, renderOptions);
|
||||
newEl = el;
|
||||
}
|
||||
if (node.tooltip) {
|
||||
el.attr('title', node.tooltip);
|
||||
}
|
||||
|
||||
nodeElems.set(node.id, newEl);
|
||||
|
||||
if (node.haveCallback) {
|
||||
nodeElems.get(node.id).attr('class', nodeElems.get(node.id).attr('class') + ' clickable');
|
||||
}
|
||||
return newEl;
|
||||
};
|
||||
|
||||
export const setNodeElem = (elem, node) => {
|
||||
nodeElems.set(node.id, elem);
|
||||
};
|
||||
|
||||
export const clear = () => {
|
||||
nodeElems.clear();
|
||||
};
|
||||
|
||||
export const positionNode = (node) => {
|
||||
const el = nodeElems.get(node.id);
|
||||
log.trace(
|
||||
'Transforming node',
|
||||
node.diff,
|
||||
node,
|
||||
'translate(' + (node.x - node.width / 2 - 5) + ', ' + node.width / 2 + ')'
|
||||
);
|
||||
const padding = 8;
|
||||
const diff = node.diff || 0;
|
||||
if (node.clusterNode) {
|
||||
el.attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(node.x + diff - node.width / 2) +
|
||||
', ' +
|
||||
(node.y - node.height / 2 - padding) +
|
||||
')'
|
||||
);
|
||||
} else {
|
||||
el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
|
||||
}
|
||||
return diff;
|
||||
};
|
@@ -0,0 +1,83 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { log } from '../../logger.js';
|
||||
import type { SVG } from '../../mermaid.js';
|
||||
import type { Node, ShapeRenderOptions } from '../types.js';
|
||||
import { shapes } from './shapes.js';
|
||||
|
||||
// TODO: Need a better name for the class.
|
||||
export class Nodes {
|
||||
private readonly nodeElems = new Map();
|
||||
positionNode = (node: any) => {
|
||||
const el = this.nodeElems.get(node.id);
|
||||
log.trace(
|
||||
'Transforming node',
|
||||
node.diff,
|
||||
node,
|
||||
'translate(' + (node.x - node.width / 2 - 5) + ', ' + node.width / 2 + ')'
|
||||
);
|
||||
const padding = 8;
|
||||
const diff = node.diff || 0;
|
||||
if (node.clusterNode) {
|
||||
el.attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(node.x + diff - node.width / 2) +
|
||||
', ' +
|
||||
(node.y - node.height / 2 - padding) +
|
||||
')'
|
||||
);
|
||||
} else {
|
||||
el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
|
||||
}
|
||||
return diff;
|
||||
};
|
||||
setNodeElem = (elem: any, node: any) => {
|
||||
this.nodeElems.set(node.id, elem);
|
||||
};
|
||||
insertNode = async (elem: SVG, node: Node, renderOptions: ShapeRenderOptions) => {
|
||||
let newEl: any;
|
||||
let el: any;
|
||||
|
||||
//special check for rect shape (with or without rounded corners)
|
||||
if (node.shape === 'rect') {
|
||||
if (node.rx && node.ry) {
|
||||
node.shape = 'roundedRect';
|
||||
} else {
|
||||
node.shape = 'squareRect';
|
||||
}
|
||||
}
|
||||
|
||||
if (!node.shape) {
|
||||
throw new Error(`No shape defined for node ${node.id}. Please check your syntax.`);
|
||||
}
|
||||
|
||||
const shapeHandler = shapes[node.shape];
|
||||
|
||||
if (!shapeHandler) {
|
||||
throw new Error(`No such shape: ${node.shape}. Please check your syntax.`);
|
||||
}
|
||||
|
||||
if (node.link) {
|
||||
// Add link when appropriate
|
||||
const target =
|
||||
renderOptions.config.securityLevel === 'sandbox' ? '_top' : (node.linkTarget ?? '_blank');
|
||||
newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target);
|
||||
el = await shapeHandler(newEl, node, renderOptions);
|
||||
} else {
|
||||
el = await shapeHandler(elem, node, renderOptions);
|
||||
newEl = el;
|
||||
}
|
||||
if (node.tooltip) {
|
||||
el.attr('title', node.tooltip);
|
||||
}
|
||||
|
||||
this.nodeElems.set(node.id, newEl);
|
||||
|
||||
if (node.haveCallback) {
|
||||
this.nodeElems
|
||||
.get(node.id)
|
||||
.attr('class', this.nodeElems.get(node.id).attr('class') + ' clickable');
|
||||
}
|
||||
return newEl;
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user