chore: Refactor nodes.js to remove global state

This commit is contained in:
Sidharth Vinod
2024-10-04 00:22:56 +05:30
parent 3f21f59f55
commit e1a71296fe
3 changed files with 106 additions and 100 deletions

View File

@@ -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
);
};

View File

@@ -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;
};

View File

@@ -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;
};
}