Convert svgDraw.ts

This commit is contained in:
Sidharth Vinod 2024-01-29 12:16:21 +05:30
parent 75ec719257
commit b51d8ff7ba
No known key found for this signature in database
GPG Key ID: FB5CCD378D3907CD
4 changed files with 51 additions and 43 deletions

View File

@ -1,12 +1,12 @@
import { getConfig } from '../../diagram-api/diagramAPI.js'; import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { D3Element } from '../../mermaidAPI.js';
import { sanitizeText } from '../../diagrams/common/common.js'; import { sanitizeText } from '../../diagrams/common/common.js';
import { log } from '../../logger.js'; import { log } from '../../logger.js';
import type { D3Element } from '../../mermaidAPI.js'; import type { MindmapNode } from './mindmapTypes.js';
import type { MindMapNode } from './mindmapTypes.js';
let nodes: MindMapNode[] = []; let nodes: MindmapNode[] = [];
let cnt = 0; let cnt = 0;
let elements: Record<string, D3Element> = {}; let elements: Record<number, D3Element> = {};
const clear = () => { const clear = () => {
nodes = []; nodes = [];
@ -14,7 +14,7 @@ const clear = () => {
elements = {}; elements = {};
}; };
const getParent = function (level: number) { const getParent = function(level: number) {
for (let i = nodes.length - 1; i >= 0; i--) { for (let i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].level < level) { if (nodes[i].level < level) {
return nodes[i]; return nodes[i];
@ -48,7 +48,7 @@ const addNode = (level: number, id: string, descr: string, type: number) => {
children: [], children: [],
width: conf.mindmap?.maxNodeWidth ?? 200, width: conf.mindmap?.maxNodeWidth ?? 200,
padding, padding,
} satisfies MindMapNode; } satisfies MindmapNode;
const parent = getParent(level); const parent = getParent(level);
if (parent) { if (parent) {
@ -100,7 +100,7 @@ const getType = (startStr: string, endStr: string): number => {
} }
}; };
const setElementForId = (id: string, element: D3Element) => { const setElementForId = (id: number, element: D3Element) => {
elements[id] = element; elements[id] = element;
}; };
@ -141,7 +141,7 @@ const type2Str = (type: number) => {
// Expose logger to grammar // Expose logger to grammar
const getLogger = () => log; const getLogger = () => log;
const getElementById = (id: string) => elements[id]; const getElementById = (id: number) => elements[id];
const db = { const db = {
clear, clear,

View File

@ -6,7 +6,7 @@ import { drawNode, positionNode } from './svgDraw.js';
import cytoscape from 'cytoscape'; import cytoscape from 'cytoscape';
// @ts-expect-error No types available // @ts-expect-error No types available
import coseBilkent from 'cytoscape-cose-bilkent'; import coseBilkent from 'cytoscape-cose-bilkent';
import type { MindMapNode, MindmapDB } from './mindmapTypes.js'; import type { MindmapNode, MindmapDB, FilledMindMapNode } from './mindmapTypes.js';
import type { MermaidConfig } from '../../config.type.js'; import type { MermaidConfig } from '../../config.type.js';
import type { Diagram } from '../../Diagram.js'; import type { Diagram } from '../../Diagram.js';
import type { D3Element } from '../../mermaidAPI.js'; import type { D3Element } from '../../mermaidAPI.js';
@ -18,9 +18,9 @@ cytoscape.use(coseBilkent);
function drawNodes( function drawNodes(
db: MindmapDB, db: MindmapDB,
svg: D3Element, svg: D3Element,
mindmap: MindMapNode, mindmap: FilledMindMapNode,
section: number, section: number,
conf: MermaidConfig conf: MermaidConfigWithDefaults
) { ) {
drawNode(db, svg, mindmap, section, conf); drawNode(db, svg, mindmap, section, conf);
if (mindmap.children) { if (mindmap.children) {
@ -63,7 +63,7 @@ function drawEdges(edgesEl: D3Element, cy: cytoscape.Core) {
}); });
} }
function addNodes(mindmap: MindMapNode, cy: cytoscape.Core, conf: MermaidConfig, level: number) { function addNodes(mindmap: MindmapNode, cy: cytoscape.Core, conf: MermaidConfig, level: number) {
cy.add({ cy.add({
group: 'nodes', group: 'nodes',
data: { data: {
@ -99,7 +99,7 @@ function addNodes(mindmap: MindMapNode, cy: cytoscape.Core, conf: MermaidConfig,
} }
function layoutMindmap( function layoutMindmap(
node: MindMapNode, node: MindmapNode,
conf: MermaidConfigWithDefaults conf: MermaidConfigWithDefaults
): Promise<cytoscape.Core> { ): Promise<cytoscape.Core> {
return new Promise((resolve) => { return new Promise((resolve) => {
@ -193,7 +193,7 @@ export const draw = async (text: string, id: string, version: string, diagObj: D
edgesElem.attr('class', 'mindmap-edges'); edgesElem.attr('class', 'mindmap-edges');
const nodesElem = svg.append('g'); const nodesElem = svg.append('g');
nodesElem.attr('class', 'mindmap-nodes'); nodesElem.attr('class', 'mindmap-nodes');
drawNodes(db, nodesElem, mm, -1, conf); drawNodes(db, nodesElem, mm as FilledMindMapNode, -1, conf);
// Next step is to layout the mindmap, giving each node a position // Next step is to layout the mindmap, giving each node a position

View File

@ -1,12 +1,13 @@
import { RequiredDeep } from 'type-fest';
import type mindmapDb from './mindmapDb.js'; import type mindmapDb from './mindmapDb.js';
export interface MindMapNode { export interface MindmapNode {
id: number; id: number;
nodeId: string; nodeId: string;
level: number; level: number;
descr: string; descr: string;
type: number; type: number;
children: MindMapNode[]; children: MindmapNode[];
width: number; width: number;
padding: number; padding: number;
section?: number; section?: number;
@ -17,4 +18,5 @@ export interface MindMapNode {
y?: number; y?: number;
} }
export type FilledMindMapNode = RequiredDeep<MindmapNode>;
export type MindmapDB = typeof mindmapDb; export type MindmapDB = typeof mindmapDb;

View File

@ -1,7 +1,13 @@
import type { D3Element } from '../../mermaidAPI.js';
import { createText } from '../../rendering-util/createText.js'; import { createText } from '../../rendering-util/createText.js';
import type { FilledMindMapNode, MindmapDB } from './mindmapTypes.js';
import { MermaidConfigWithDefaults } from '../../config.js';
import { Point } from '../../types.js';
const MAX_SECTIONS = 12; const MAX_SECTIONS = 12;
const defaultBkg = function (db, elem, node, section) { type ShapeFunction = (db: MindmapDB, elem: D3Element, node: FilledMindMapNode, section?: number) => void;
const defaultBkg: ShapeFunction = function(db, elem, node, section) {
const rd = 5; const rd = 5;
elem elem
.append('path') .append('path')
@ -9,8 +15,7 @@ const defaultBkg = function (db, elem, node, section) {
.attr('class', 'node-bkg node-' + db.type2Str(node.type)) .attr('class', 'node-bkg node-' + db.type2Str(node.type))
.attr( .attr(
'd', 'd',
`M0 ${node.height - rd} v${-node.height + 2 * rd} q0,-5 5,-5 h${ `M0 ${node.height - rd} v${-node.height + 2 * rd} q0,-5 5,-5 h${node.width - 2 * rd
node.width - 2 * rd
} q5,0 5,5 v${node.height - rd} H0 Z` } q5,0 5,5 v${node.height - rd} H0 Z`
); );
@ -23,7 +28,7 @@ const defaultBkg = function (db, elem, node, section) {
.attr('y2', node.height); .attr('y2', node.height);
}; };
const rectBkg = function (db, elem, node) { const rectBkg: ShapeFunction = function(db, elem, node) {
elem elem
.append('rect') .append('rect')
.attr('id', 'node-' + node.id) .attr('id', 'node-' + node.id)
@ -32,7 +37,7 @@ const rectBkg = function (db, elem, node) {
.attr('width', node.width); .attr('width', node.width);
}; };
const cloudBkg = function (db, elem, node) { const cloudBkg: ShapeFunction = function(db, elem, node) {
const w = node.width; const w = node.width;
const h = node.height; const h = node.height;
const r1 = 0.15 * w; const r1 = 0.15 * w;
@ -63,7 +68,7 @@ const cloudBkg = function (db, elem, node) {
); );
}; };
const bangBkg = function (db, elem, node) { const bangBkg: ShapeFunction = function(db, elem, node) {
const w = node.width; const w = node.width;
const h = node.height; const h = node.height;
const r = 0.15 * w; const r = 0.15 * w;
@ -95,7 +100,7 @@ const bangBkg = function (db, elem, node) {
); );
}; };
const circleBkg = function (db, elem, node) { const circleBkg: ShapeFunction = function(db, elem, node) {
elem elem
.append('circle') .append('circle')
.attr('id', 'node-' + node.id) .attr('id', 'node-' + node.id)
@ -111,13 +116,13 @@ const circleBkg = function (db, elem, node) {
* @param points * @param points
* @param node * @param node
*/ */
function insertPolygonShape(parent, w, h, points, node) { function insertPolygonShape(parent: D3Element, w: number, h: number, points: Point[], node: FilledMindMapNode) {
return parent return parent
.insert('polygon', ':first-child') .insert('polygon', ':first-child')
.attr( .attr(
'points', 'points',
points points
.map(function (d) { .map(function(d) {
return d.x + ',' + d.y; return d.x + ',' + d.y;
}) })
.join(' ') .join(' ')
@ -125,12 +130,12 @@ function insertPolygonShape(parent, w, h, points, node) {
.attr('transform', 'translate(' + (node.width - w) / 2 + ', ' + h + ')'); .attr('transform', 'translate(' + (node.width - w) / 2 + ', ' + h + ')');
} }
const hexagonBkg = function (db, elem, node) { const hexagonBkg: ShapeFunction = function(_db: MindmapDB, elem: D3Element, node: FilledMindMapNode) {
const h = node.height; const h = node.height;
const f = 4; const f = 4;
const m = h / f; const m = h / f;
const w = node.width - node.padding + 2 * m; const w = node.width - node.padding + 2 * m;
const points = [ const points: Point[] = [
{ x: m, y: 0 }, { x: m, y: 0 },
{ x: w - m, y: 0 }, { x: w - m, y: 0 },
{ x: w, y: -h / 2 }, { x: w, y: -h / 2 },
@ -138,10 +143,10 @@ const hexagonBkg = function (db, elem, node) {
{ x: m, y: -h }, { x: m, y: -h },
{ x: 0, y: -h / 2 }, { x: 0, y: -h / 2 },
]; ];
const shapeSvg = insertPolygonShape(elem, w, h, points, node); insertPolygonShape(elem, w, h, points, node);
}; };
const roundedRectBkg = function (db, elem, node) { const roundedRectBkg: ShapeFunction = function(db, elem, node) {
elem elem
.append('rect') .append('rect')
.attr('id', 'node-' + node.id) .attr('id', 'node-' + node.id)
@ -153,14 +158,14 @@ const roundedRectBkg = function (db, elem, node) {
}; };
/** /**
* @param {import('./mindmapTypes.js').MindmapDB} db The database * @param db The database
* @param {object} elem The D3 dom element in which the node is to be added * @param elem The D3 dom element in which the node is to be added
* @param {object} node The node to be added * @param node The node to be added
* @param fullSection * @param fullSection
* @param {object} conf The configuration object * @param conf The configuration object
* @returns {number} The height nodes dom element * @returns The height nodes dom element
*/ */
export const drawNode = function (db, elem, node, fullSection, conf) { export const drawNode = function(db: MindmapDB, elem: D3Element, node: FilledMindMapNode, fullSection: number, conf: MermaidConfigWithDefaults): number {
const htmlLabels = conf.htmlLabels; const htmlLabels = conf.htmlLabels;
const section = fullSection % (MAX_SECTIONS - 1); const section = fullSection % (MAX_SECTIONS - 1);
const nodeElem = elem.append('g'); const nodeElem = elem.append('g');
@ -190,6 +195,7 @@ export const drawNode = function (db, elem, node, fullSection, conf) {
} }
// .call(wrap, node.width); // .call(wrap, node.width);
const bbox = textElem.node().getBBox(); const bbox = textElem.node().getBBox();
// @ts-expect-error TODO: Check if fontSize can be string?
const fontSize = conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize; const fontSize = conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding; node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
node.width = bbox.width + 2 * node.padding; node.width = bbox.width + 2 * node.padding;
@ -247,26 +253,26 @@ export const drawNode = function (db, elem, node, fullSection, conf) {
switch (node.type) { switch (node.type) {
case db.nodeType.DEFAULT: case db.nodeType.DEFAULT:
defaultBkg(db, bkgElem, node, section, conf); defaultBkg(db, bkgElem, node, section);
break; break;
case db.nodeType.ROUNDED_RECT: case db.nodeType.ROUNDED_RECT:
roundedRectBkg(db, bkgElem, node, section, conf); roundedRectBkg(db, bkgElem, node, section);
break; break;
case db.nodeType.RECT: case db.nodeType.RECT:
rectBkg(db, bkgElem, node, section, conf); rectBkg(db, bkgElem, node, section);
break; break;
case db.nodeType.CIRCLE: case db.nodeType.CIRCLE:
bkgElem.attr('transform', 'translate(' + node.width / 2 + ', ' + +node.height / 2 + ')'); bkgElem.attr('transform', 'translate(' + node.width / 2 + ', ' + +node.height / 2 + ')');
circleBkg(db, bkgElem, node, section, conf); circleBkg(db, bkgElem, node, section);
break; break;
case db.nodeType.CLOUD: case db.nodeType.CLOUD:
cloudBkg(db, bkgElem, node, section, conf); cloudBkg(db, bkgElem, node, section);
break; break;
case db.nodeType.BANG: case db.nodeType.BANG:
bangBkg(db, bkgElem, node, section, conf); bangBkg(db, bkgElem, node, section);
break; break;
case db.nodeType.HEXAGON: case db.nodeType.HEXAGON:
hexagonBkg(db, bkgElem, node, section, conf); hexagonBkg(db, bkgElem, node, section);
break; break;
} }
@ -274,7 +280,7 @@ export const drawNode = function (db, elem, node, fullSection, conf) {
return node.height; return node.height;
}; };
export const positionNode = function (db, node) { export const positionNode = function(db: MindmapDB, node: FilledMindMapNode) {
const nodeElem = db.getElementById(node.id); const nodeElem = db.getElementById(node.id);
const x = node.x || 0; const x = node.x || 0;