From 260a045da0124cef12da9a7e77b1199abe71680f Mon Sep 17 00:00:00 2001 From: darshanr0107 Date: Mon, 14 Jul 2025 20:00:13 +0530 Subject: [PATCH 1/4] migrate mindmap to class based approach --- .../diagrams/mindmap/mindmap-definition.ts | 6 +- .../src/diagrams/mindmap/mindmap.spec.ts | 4 +- .../mermaid/src/diagrams/mindmap/mindmapDb.ts | 285 +++++++++--------- .../src/diagrams/mindmap/mindmapRenderer.ts | 4 +- .../src/diagrams/mindmap/mindmapTypes.ts | 2 - .../mermaid/src/diagrams/mindmap/svgDraw.ts | 3 +- 6 files changed, 154 insertions(+), 150 deletions(-) diff --git a/packages/mermaid/src/diagrams/mindmap/mindmap-definition.ts b/packages/mermaid/src/diagrams/mindmap/mindmap-definition.ts index 66b44b4f9..c02898954 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmap-definition.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmap-definition.ts @@ -1,12 +1,14 @@ // @ts-ignore: JISON doesn't support types import parser from './parser/mindmap.jison'; -import db from './mindmapDb.js'; +import { MindmapDB } from './mindmapDb.js'; import renderer from './mindmapRenderer.js'; import styles from './styles.js'; import type { DiagramDefinition } from '../../diagram-api/types.js'; export const diagram: DiagramDefinition = { - db, + get db() { + return new MindmapDB(); + }, renderer, parser, styles, diff --git a/packages/mermaid/src/diagrams/mindmap/mindmap.spec.ts b/packages/mermaid/src/diagrams/mindmap/mindmap.spec.ts index d4f2d316e..b912e1b8c 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmap.spec.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmap.spec.ts @@ -1,12 +1,12 @@ // @ts-expect-error No types available for JISON import { parser as mindmap } from './parser/mindmap.jison'; -import mindmapDB from './mindmapDb.js'; +import { MindmapDB } from './mindmapDb.js'; // Todo fix utils functions for tests import { setLogLevel } from '../../diagram-api/diagramAPI.js'; describe('when parsing a mindmap ', function () { beforeEach(function () { - mindmap.yy = mindmapDB; + mindmap.yy = new MindmapDB(); mindmap.yy.clear(); setLogLevel('trace'); }); diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts b/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts index e7041e9d6..7a151d9c0 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts @@ -5,70 +5,6 @@ import { log } from '../../logger.js'; import type { MindmapNode } from './mindmapTypes.js'; import defaultConfig from '../../defaultConfig.js'; -let nodes: MindmapNode[] = []; -let cnt = 0; -let elements: Record = {}; - -const clear = () => { - nodes = []; - cnt = 0; - elements = {}; -}; - -const getParent = function (level: number) { - for (let i = nodes.length - 1; i >= 0; i--) { - if (nodes[i].level < level) { - return nodes[i]; - } - } - // No parent found - return null; -}; - -const getMindmap = () => { - return nodes.length > 0 ? nodes[0] : null; -}; - -const addNode = (level: number, id: string, descr: string, type: number) => { - log.info('addNode', level, id, descr, type); - const conf = getConfig(); - let padding: number = conf.mindmap?.padding ?? defaultConfig.mindmap.padding; - switch (type) { - case nodeType.ROUNDED_RECT: - case nodeType.RECT: - case nodeType.HEXAGON: - padding *= 2; - } - - const node = { - id: cnt++, - nodeId: sanitizeText(id, conf), - level, - descr: sanitizeText(descr, conf), - type, - children: [], - width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth, - padding, - } satisfies MindmapNode; - - const parent = getParent(level); - if (parent) { - parent.children.push(node); - // Keep all nodes in the list - nodes.push(node); - } else { - if (nodes.length === 0) { - // First node, the root - nodes.push(node); - } else { - // Syntax error ... there can only bee one root - throw new Error( - 'There can be only one root. No parent could be found for ("' + node.descr + '")' - ); - } - } -}; - const nodeType = { DEFAULT: 0, NO_BORDER: 0, @@ -78,82 +14,149 @@ const nodeType = { CLOUD: 4, BANG: 5, HEXAGON: 6, -}; - -const getType = (startStr: string, endStr: string): number => { - log.debug('In get type', startStr, endStr); - switch (startStr) { - case '[': - return nodeType.RECT; - case '(': - return endStr === ')' ? nodeType.ROUNDED_RECT : nodeType.CLOUD; - case '((': - return nodeType.CIRCLE; - case ')': - return nodeType.CLOUD; - case '))': - return nodeType.BANG; - case '{{': - return nodeType.HEXAGON; - default: - return nodeType.DEFAULT; - } -}; - -const setElementForId = (id: number, element: D3Element) => { - elements[id] = element; -}; - -const decorateNode = (decoration?: { class?: string; icon?: string }) => { - if (!decoration) { - return; - } - const config = getConfig(); - const node = nodes[nodes.length - 1]; - if (decoration.icon) { - node.icon = sanitizeText(decoration.icon, config); - } - if (decoration.class) { - node.class = sanitizeText(decoration.class, config); - } -}; - -const type2Str = (type: number) => { - switch (type) { - case nodeType.DEFAULT: - return 'no-border'; - case nodeType.RECT: - return 'rect'; - case nodeType.ROUNDED_RECT: - return 'rounded-rect'; - case nodeType.CIRCLE: - return 'circle'; - case nodeType.CLOUD: - return 'cloud'; - case nodeType.BANG: - return 'bang'; - case nodeType.HEXAGON: - return 'hexgon'; // cspell: disable-line - default: - return 'no-border'; - } -}; - -// Expose logger to grammar -const getLogger = () => log; -const getElementById = (id: number) => elements[id]; - -const db = { - clear, - addNode, - getMindmap, - nodeType, - getType, - setElementForId, - decorateNode, - type2Str, - getLogger, - getElementById, } as const; -export default db; +export class MindmapDB { + private nodes: MindmapNode[] = []; + private cnt = 0; + private elements: Record = {}; + public readonly nodeType: typeof nodeType; + + constructor() { + this.getLogger = this.getLogger.bind(this); + this.nodeType = nodeType; + this.clear(); + this.getType = this.getType.bind(this); + this.getMindmap = this.getMindmap.bind(this); + this.getElementById = this.getElementById.bind(this); + this.getParent = this.getParent.bind(this); + this.getMindmap = this.getMindmap.bind(this); + this.addNode = this.addNode.bind(this); + this.decorateNode = this.decorateNode.bind(this); + } + public clear() { + this.nodes = []; + this.cnt = 0; + this.elements = {}; + } + + public getParent(level: number): MindmapNode | null { + for (let i = this.nodes.length - 1; i >= 0; i--) { + if (this.nodes[i].level < level) { + return this.nodes[i]; + } + } + return null; + } + + public getMindmap(): MindmapNode | null { + return this.nodes.length > 0 ? this.nodes[0] : null; + } + + public addNode(level: number, id: string, descr: string, type: number): void { + log.info('addNode', level, id, descr, type); + + const conf = getConfig(); + let padding = conf.mindmap?.padding ?? defaultConfig.mindmap.padding; + + switch (type) { + case this.nodeType.ROUNDED_RECT: + case this.nodeType.RECT: + case this.nodeType.HEXAGON: + padding *= 2; + break; + } + + const node: MindmapNode = { + id: this.cnt++, + nodeId: sanitizeText(id, conf), + level, + descr: sanitizeText(descr, conf), + type, + children: [], + width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth, + padding, + }; + + const parent = this.getParent(level); + if (parent) { + parent.children.push(node); + this.nodes.push(node); + } else { + if (this.nodes.length === 0) { + this.nodes.push(node); + } else { + throw new Error( + `There can be only one root. No parent could be found for ("${node.descr}")` + ); + } + } + } + + public getType(startStr: string, endStr: string) { + log.debug('In get type', startStr, endStr); + switch (startStr) { + case '[': + return this.nodeType.RECT; + case '(': + return endStr === ')' ? this.nodeType.ROUNDED_RECT : this.nodeType.CLOUD; + case '((': + return this.nodeType.CIRCLE; + case ')': + return this.nodeType.CLOUD; + case '))': + return this.nodeType.BANG; + case '{{': + return this.nodeType.HEXAGON; + default: + return this.nodeType.DEFAULT; + } + } + + public setElementForId(id: number, element: D3Element): void { + this.elements[id] = element; + } + public getElementById(id: number) { + return this.elements[id]; + } + + public decorateNode(decoration?: { class?: string; icon?: string }): void { + if (!decoration) { + return; + } + + const config = getConfig(); + const node = this.nodes[this.nodes.length - 1]; + if (decoration.icon) { + node.icon = sanitizeText(decoration.icon, config); + } + if (decoration.class) { + node.class = sanitizeText(decoration.class, config); + } + } + + type2Str(type: number): string { + switch (type) { + case this.nodeType.DEFAULT: + return 'no-border'; + case this.nodeType.RECT: + return 'rect'; + case this.nodeType.ROUNDED_RECT: + return 'rounded-rect'; + case this.nodeType.CIRCLE: + return 'circle'; + case this.nodeType.CLOUD: + return 'cloud'; + case this.nodeType.BANG: + return 'bang'; + case this.nodeType.HEXAGON: + return 'hexgon'; // cspell: disable-line + default: + return 'no-border'; + } + } + + public getLogger() { + return log; + } +} diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts index 708b3cc28..ef9be0565 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts @@ -9,10 +9,10 @@ import { log } from '../../logger.js'; import type { D3Element } from '../../types.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { setupGraphViewbox } from '../../setupGraphViewbox.js'; -import type { FilledMindMapNode, MindmapDB, MindmapNode } from './mindmapTypes.js'; +import type { FilledMindMapNode, MindmapNode } from './mindmapTypes.js'; import { drawNode, positionNode } from './svgDraw.js'; import defaultConfig from '../../defaultConfig.js'; - +import type { MindmapDB } from './mindmapDb.js'; // Inject the layout algorithm into cytoscape cytoscape.use(coseBilkent); diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts b/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts index e8350477a..be8effab1 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts @@ -1,5 +1,4 @@ import type { RequiredDeep } from 'type-fest'; -import type mindmapDb from './mindmapDb.js'; export interface MindmapNode { id: number; @@ -19,4 +18,3 @@ export interface MindmapNode { } export type FilledMindMapNode = RequiredDeep; -export type MindmapDB = typeof mindmapDb; diff --git a/packages/mermaid/src/diagrams/mindmap/svgDraw.ts b/packages/mermaid/src/diagrams/mindmap/svgDraw.ts index 209a6a0e1..8aee82e30 100644 --- a/packages/mermaid/src/diagrams/mindmap/svgDraw.ts +++ b/packages/mermaid/src/diagrams/mindmap/svgDraw.ts @@ -1,8 +1,9 @@ import { createText } from '../../rendering-util/createText.js'; -import type { FilledMindMapNode, MindmapDB } from './mindmapTypes.js'; +import type { FilledMindMapNode } from './mindmapTypes.js'; import type { Point, D3Element } from '../../types.js'; import { parseFontSize } from '../../utils.js'; import type { MermaidConfig } from '../../config.type.js'; +import type { MindmapDB } from './mindmapDb.js'; const MAX_SECTIONS = 12; From d3e2be35be066adeb7fd502b4a24c223c3b53947 Mon Sep 17 00:00:00 2001 From: darshanr0107 Date: Mon, 14 Jul 2025 20:12:31 +0530 Subject: [PATCH 2/4] added changeset --- .changeset/weak-files-stare.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/weak-files-stare.md diff --git a/.changeset/weak-files-stare.md b/.changeset/weak-files-stare.md new file mode 100644 index 000000000..cbce03b8e --- /dev/null +++ b/.changeset/weak-files-stare.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +patch: updated mindmapdb to use class based approach From 96c21c7e54942ffc93c9b13d9f5f359f0f4c6664 Mon Sep 17 00:00:00 2001 From: darshanr0107 Date: Tue, 15 Jul 2025 12:19:02 +0530 Subject: [PATCH 3/4] resolve PR comments and update changeset --- .changeset/weak-files-stare.md | 2 +- packages/mermaid/src/diagrams/mindmap/mindmapDb.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/weak-files-stare.md b/.changeset/weak-files-stare.md index cbce03b8e..80440fe05 100644 --- a/.changeset/weak-files-stare.md +++ b/.changeset/weak-files-stare.md @@ -2,4 +2,4 @@ 'mermaid': patch --- -patch: updated mindmapdb to use class based approach +patch: Update MindmapDB to use class based approach diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts b/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts index 7a151d9c0..703ba8434 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts @@ -18,7 +18,7 @@ const nodeType = { export class MindmapDB { private nodes: MindmapNode[] = []; - private cnt = 0; + private count = 0; private elements: Record = {}; public readonly nodeType: typeof nodeType; @@ -36,7 +36,7 @@ export class MindmapDB { } public clear() { this.nodes = []; - this.cnt = 0; + this.count = 0; this.elements = {}; } @@ -68,7 +68,7 @@ export class MindmapDB { } const node: MindmapNode = { - id: this.cnt++, + id: this.count++, nodeId: sanitizeText(id, conf), level, descr: sanitizeText(descr, conf), From 1e5e835c41e9c11c5eb8b7e00060fc8c5392b0a3 Mon Sep 17 00:00:00 2001 From: darshanr0107 Date: Tue, 15 Jul 2025 16:50:15 +0530 Subject: [PATCH 4/4] updated changeset on-behalf-of: @Mermaid-Chart hello@mermaidchart.com --- .changeset/weak-files-stare.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/weak-files-stare.md b/.changeset/weak-files-stare.md index 80440fe05..c1a8d8f3a 100644 --- a/.changeset/weak-files-stare.md +++ b/.changeset/weak-files-stare.md @@ -2,4 +2,4 @@ 'mermaid': patch --- -patch: Update MindmapDB to use class based approach +chore: Update MindmapDB to use class based approach