diff --git a/cSpell.json b/cSpell.json index 6f93af103..ec2daed59 100644 --- a/cSpell.json +++ b/cSpell.json @@ -19,6 +19,7 @@ "brkt", "brolin", "brotli", + "Città", "classdef", "codedoc", "colour", @@ -109,7 +110,11 @@ "yash" ], "patterns": [ - { "name": "Markdown links", "pattern": "\\((.*)\\)", "description": "" }, + { + "name": "Markdown links", + "pattern": "\\((.*)\\)", + "description": "" + }, { "name": "Markdown code blocks", "pattern": "/^(\\s*`{3,}).*[\\s\\S]*?^\\1/gmx", @@ -120,14 +125,25 @@ "pattern": "\\`([^\\`\\r\\n]+?)\\`", "description": "https://stackoverflow.com/questions/41274241/how-to-capture-inline-markdown-code-but-not-a-markdown-code-fence-with-regex" }, - { "name": "Link contents", "pattern": "\\", "description": "" }, - { "name": "Snippet references", "pattern": "-- snippet:(.*)", "description": "" }, + { + "name": "Link contents", + "pattern": "\\", + "description": "" + }, + { + "name": "Snippet references", + "pattern": "-- snippet:(.*)", + "description": "" + }, { "name": "Snippet references 2", "pattern": "\\<\\[sample:(.*)", "description": "another kind of snippet reference" }, - { "name": "Multi-line code blocks", "pattern": "/^\\s*```[\\s\\S]*?^\\s*```/gm" }, + { + "name": "Multi-line code blocks", + "pattern": "/^\\s*```[\\s\\S]*?^\\s*```/gm" + }, { "name": "HTML Tags", "pattern": "<[^>]*>", diff --git a/cypress/helpers/util.js b/cypress/helpers/util.js index 533cca499..7ec960b97 100644 --- a/cypress/helpers/util.js +++ b/cypress/helpers/util.js @@ -22,7 +22,7 @@ export const mermaidUrl = (graphStr, options, api) => { return url; }; -export const imgSnapshotTest = (graphStr, _options, api = false, validation) => { +export const imgSnapshotTest = (graphStr, _options = {}, api = false, validation = undefined) => { cy.log(_options); const options = Object.assign(_options); if (!options.fontFamily) { diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js index 694ba074d..f684ad110 100644 --- a/packages/mermaid/src/dagre-wrapper/nodes.js +++ b/packages/mermaid/src/dagre-wrapper/nodes.js @@ -772,7 +772,7 @@ const class_box = (parent, node) => { maxWidth += interfaceBBox.width; } - let classTitleString = node.classData.id; + let classTitleString = node.classData.label; if (node.classData.type !== undefined && node.classData.type !== '') { if (getConfig().flowchart.htmlLabels) { diff --git a/packages/mermaid/src/diagrams/class/classDb.ts b/packages/mermaid/src/diagrams/class/classDb.ts index b4a67674e..451e66f0a 100644 --- a/packages/mermaid/src/diagrams/class/classDb.ts +++ b/packages/mermaid/src/diagrams/class/classDb.ts @@ -13,33 +13,12 @@ import { setDiagramTitle, getDiagramTitle, } from '../../commonDb'; +import { ClassRelation, ClassNode, ClassNote, ClassMap } from './classTypes'; -const MERMAID_DOM_ID_PREFIX = 'classid-'; - -interface ClassNode { - id: string; - type: string; - cssClasses: string[]; - methods: string[]; - members: string[]; - annotations: string[]; - domId: string; - link?: string; - linkTarget?: string; - haveCallback?: boolean; - tooltip?: string; -} - -interface ClassNote { - id: string; - class: string; - text: string; -} - -type ClassRelation = any; +const MERMAID_DOM_ID_PREFIX = 'classId-'; let relations: ClassRelation[] = []; -let classes: Record = {}; +let classes: ClassMap = {}; let notes: ClassNote[] = []; let classCounter = 0; @@ -58,9 +37,8 @@ const splitClassNameAndType = function (id: string) { if (id.indexOf('~') > 0) { const split = id.split('~'); - className = split[0]; - - genericType = common.sanitizeText(split[1], configApi.getConfig()); + className = sanitizeText(split[0]); + genericType = sanitizeText(split[1]); } return { className: className, type: genericType }; @@ -70,18 +48,24 @@ const splitClassNameAndType = function (id: string) { * Function called by parser when a node definition has been found. * * @param id - Id of the class to add + * @param label - Optional label of the class * @public */ -export const addClass = function (id: string) { +export const addClass = function (id: string, label?: string) { const classId = splitClassNameAndType(id); // Only add class if not exists if (classes[classId.className] !== undefined) { return; } + if (label) { + label = sanitizeText(label); + } + classes[classId.className] = { id: classId.className, type: classId.type, + label: label ?? classId.className, cssClasses: [], methods: [], members: [], @@ -121,7 +105,7 @@ export const getClasses = function () { return classes; }; -export const getRelations = function () { +export const getRelations = function (): ClassRelation[] { return relations; }; @@ -182,7 +166,6 @@ export const addMember = function (className: string, member: string) { if (memberString.startsWith('<<') && memberString.endsWith('>>')) { // Remove leading and trailing brackets - // theClass.annotations.push(memberString.substring(2, memberString.length - 2)); theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2))); } else if (memberString.indexOf(')') > 0) { theClass.methods.push(sanitizeText(memberString)); @@ -359,8 +342,7 @@ export const relationType = { }; const setupToolTips = function (element: Element) { - let tooltipElem: Selection = - select('.mermaidTooltip'); + let tooltipElem: Selection = select('.mermaidTooltip'); // @ts-ignore - _groups is a dynamic property if ((tooltipElem._groups || tooltipElem)[0][0] === null) { tooltipElem = select('body').append('div').attr('class', 'mermaidTooltip').style('opacity', 0); diff --git a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts index 0517b800c..12f76ec35 100644 --- a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts +++ b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts @@ -8,7 +8,7 @@ import { curveLinear } from 'd3'; import { interpolateToCurve, getStylesFromArray } from '../../utils'; import { setupGraphViewbox } from '../../setupGraphViewbox'; import common from '../common/common'; -import { ClassRelation, ClassNode, ClassNote, ClassMap, EdgeData } from './classTypes'; +import { ClassRelation, ClassNote, ClassMap, EdgeData } from './classTypes'; const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig()); diff --git a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison index 157e3d7d8..dec487b11 100644 --- a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison +++ b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison @@ -119,6 +119,8 @@ Function arguments are optional: 'call ()' simply executes 'callb "=" return 'EQUALS'; \= return 'EQUALS'; \w+ return 'ALPHA'; +"[" return 'SQS'; +"]" return 'SQE'; [!"#$%&'*+,-.`?\\/] return 'PUNCTUATION'; [0-9]+ return 'NUM'; [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| @@ -249,6 +251,10 @@ statements | statement NEWLINE statements ; +classLabel + : SQS STR SQE { $$=$2; } + ; + className : alphaNumToken { $$=$1; } | classLiteralName { $$=$1; } @@ -274,10 +280,15 @@ statement ; classStatement - : CLASS className {yy.addClass($2);} - | CLASS className STYLE_SEPARATOR alphaNumToken {yy.addClass($2);yy.setCssClass($2, $4);} - | CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addClass($2);yy.addMembers($2,$4);} - | CLASS className STYLE_SEPARATOR alphaNumToken STRUCT_START members STRUCT_STOP {yy.addClass($2);yy.setCssClass($2, $4);yy.addMembers($2,$6);} + : classIdentifier + | classIdentifier STYLE_SEPARATOR alphaNumToken {yy.setCssClass($1, $3);} + | classIdentifier STRUCT_START members STRUCT_STOP {yy.addMembers($1,$3);} + | classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START members STRUCT_STOP {yy.setCssClass($1, $3);yy.addMembers($1,$3);} + ; + +classIdentifier + : CLASS className {$$=$2; yy.addClass($2);} + | CLASS className classLabel {$$=$2; yy.addClass($2, $3);} ; annotationStatement