Skip to content

Commit

Permalink
add the way to add notes to class diagram
Browse files Browse the repository at this point in the history
  • Loading branch information
DKurilo committed Oct 11, 2022
1 parent 5d4d7c5 commit f05f07e
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 11 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ Class01 <|-- AveryLongClass : Cool
Class09 --> C2 : Where am I?
Class09 --* C3
Class09 --|> Class07
note "I love this diagram!\nDo you love it?"
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Expand All @@ -172,6 +173,7 @@ class Class10 {
int id
size()
}
note for Class10 "Cool class\nI said it's very cool class!"
```

### State diagram [<a href="https://mermaid-js.github.io/mermaid/#/stateDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkEFvgzAMhf8K8nEqpYSNthx22Xbcqcexg0sCiZQQlDhIFeK_L8A6TfXp6fOz9ewJGssFVOAJSbwr7ByadGR1n8T6evpO0vQ1uZDSekOrXGFsPqJPO6q-2-imH8f_0TeHXm50lfelsAMjnEHFY6xpMdRAUhhRQxUlFy0GTTXU_RytYeAx-AdXZB1ULWovdoCB7OXWN1CRC-Ju-r3uz6UtchGHJqDbsPygU57iysb2reoWHpyOWBINvsqypb3vFMlw3TfWZF5xiY7keC6zkpUnZIUojwW-FAVvrvn51LLnvOXHQ84Q5nn-AVtLcwk">live editor</a>]
Expand Down
17 changes: 17 additions & 0 deletions packages/mermaid/src/diagrams/class/classDb.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const MERMAID_DOM_ID_PREFIX = 'classid-';

let relations = [];
let classes = {};
let notes = [];
let classCounter = 0;

let funs = [];
Expand Down Expand Up @@ -82,6 +83,7 @@ export const lookUpDomId = function (id) {
export const clear = function () {
relations = [];
classes = {};
notes = [];
funs = [];
funs.push(setupToolTips);
commonClear();
Expand All @@ -98,6 +100,10 @@ export const getRelations = function () {
return relations;
};

export const getNotes = function () {
return notes;
};

export const addRelation = function (relation) {
log.debug('Adding relation: ' + JSON.stringify(relation));
addClass(relation.id1);
Expand Down Expand Up @@ -168,6 +174,15 @@ export const addMembers = function (className, members) {
}
};

export const addNote = function (text, className) {
const note = {
id: `note${notes.length}`,
class: className,
text: text,
};
notes.push(note);
};

export const cleanupLabel = function (label) {
if (label.substring(0, 1) === ':') {
return common.sanitizeText(label.substr(1).trim(), configApi.getConfig());
Expand Down Expand Up @@ -369,7 +384,9 @@ export default {
clear,
getClass,
getClasses,
getNotes,
addAnnotation,
addNote,
getRelations,
addRelation,
getDirection,
Expand Down
10 changes: 10 additions & 0 deletions packages/mermaid/src/diagrams/class/classDiagram.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,16 @@ foo()

parser.parse(str);
});

it('should handle "note for"', function () {
const str = 'classDiagram\n' + 'Class11 <|.. Class12\n' + 'note for Class11 "test"\n';
parser.parse(str);
});

it('should handle "note"', function () {
const str = 'classDiagram\n' + 'note "test"\n';
parser.parse(str);
});
});

describe('when fetching data from a classDiagram graph it', function () {
Expand Down
95 changes: 95 additions & 0 deletions packages/mermaid/src/diagrams/class/classRenderer-v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,99 @@ export const addClasses = function (classes, g, _id, diagObj) {
});
};

/**
* Function that adds the additional vertices (notes) found during parsing to the graph to be rendered.
*
* @param {{text: string; class: string; placement: number}[]} notes
* Object containing the additional vertices (notes).
* @param {SVGGElement} g The graph that is to be drawn.
* @param {number} startEdgeId starting index for note edge
* @param classes
*/
export const addNotes = function (notes, g, startEdgeId, classes) {
log.info(notes);

// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
notes.forEach(function (note, i) {
const vertex = note;

/**
* Variable for storing the classes for the vertex
*
* @type {string}
*/
let cssNoteStr = '';

const styles = { labelStyle: '', style: '' };

// Use vertex id as text in the box if no text is provided by the graph definition
let vertexText = vertex.text;

let radious = 0;
let _shape = 'note';
// Add the node
g.setNode(vertex.id, {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: sanitizeText(vertexText),
noteData: vertex,
rx: radious,
ry: radious,
class: cssNoteStr,
style: styles.style,
id: vertex.id,
domId: vertex.id,
tooltip: '',
type: 'note',
padding: getConfig().flowchart.padding,
});

log.info('setNode', {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: vertexText,
rx: radious,
ry: radious,
style: styles.style,
id: vertex.id,
type: 'note',
padding: getConfig().flowchart.padding,
});

if (!vertex.class || !(vertex.class in classes)) {
return;
}
const edgeId = startEdgeId + i;
const edgeData = {};
//Set relationship style and line type
edgeData.classes = 'relation';
edgeData.pattern = 'dotted';

edgeData.id = `edgeNote${edgeId}`;
// Set link type for rendering
edgeData.arrowhead = 'none';

log.info(`Note edge: ${JSON.stringify(edgeData)}, ${JSON.stringify(vertex)}`);
//Set edge extra labels
edgeData.startLabelRight = '';
edgeData.endLabelLeft = '';

//Set relation arrow types
edgeData.arrowTypeStart = 'none';
edgeData.arrowTypeEnd = 'none';
let style = 'fill:none';
let labelStyle = '';

edgeData.style = style;
edgeData.labelStyle = labelStyle;

edgeData.curve = interpolateToCurve(conf.curve, curveLinear);

// Add the edge to the graph
g.setEdge(vertex.id, vertex.class, edgeData, edgeId);
});
};

/**
* Add edges to graph based on parsed graph definition
*
Expand Down Expand Up @@ -304,10 +397,12 @@ export const draw = function (text, id, _version, diagObj) {
// Fetch the vertices/nodes and edges/links from the parsed graph definition
const classes = diagObj.db.getClasses();
const relations = diagObj.db.getRelations();
const notes = diagObj.db.getNotes();

log.info(relations);
addClasses(classes, g, id, diagObj);
addRelations(relations, g);
addNotes(notes, g, relations.length + 1, classes);

// Add custom shapes
// flowChartShapes.addToRenderV2(addShape);
Expand Down
32 changes: 31 additions & 1 deletion packages/mermaid/src/diagrams/class/classRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,42 @@ export const draw = function (text, id, _version, diagObj) {
);
});

const notes = diagObj.db.getNotes();
notes.forEach(function (note) {
log.debug(`Adding note: ${JSON.stringify(note)}`);
const node = svgDraw.drawNote(diagram, note, conf, diagObj);
idCache[node.id] = node;

// Add nodes to the graph. The first argument is the node id. The second is
// metadata about the node. In this case we're going to add labels to each of
// our nodes.
g.setNode(node.id, node);
if (note.class && note.class in classes) {
g.setEdge(
note.id,
getGraphId(note.class),
{
relation: {
id1: note.id,
id2: note.class,
relation: {
type1: 'none',
type2: 'none',
lineType: 10,
},
},
},
'DEFAULT'
);
}
});

dagre.layout(g);
g.nodes().forEach(function (v) {
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
log.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)));
root
.select('#' + diagObj.db.lookUpDomId(v))
.select('#' + (diagObj.db.lookUpDomId(v) || v))
.attr(
'transform',
'translate(' +
Expand Down
10 changes: 10 additions & 0 deletions packages/mermaid/src/diagrams/class/parser/classDiagram.jison
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
"callback" return 'CALLBACK';
"link" return 'LINK';
"click" return 'CLICK';
"note for" return 'NOTE_FOR';
"note" return 'NOTE';
"<<" return 'ANNOTATION_START';
">>" return 'ANNOTATION_END';
[~] this.begin("generic");
Expand Down Expand Up @@ -263,6 +265,7 @@ statement
| annotationStatement
| clickStatement
| cssClassStatement
| noteStatement
| directive
| direction
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
Expand Down Expand Up @@ -300,6 +303,11 @@ relationStatement
| className STR relation STR className { $$ = {id1:$1, id2:$5, relation:$3, relationTitle1:$2, relationTitle2:$4} }
;

noteStatement
: NOTE_FOR className noteText { yy.addNote($3, $2); }
| NOTE noteText { yy.addNote($2); }
;

relation
: relationType lineType relationType { $$={type1:$1,type2:$3,lineType:$2}; }
| lineType relationType { $$={type1:'none',type2:$2,lineType:$1}; }
Expand Down Expand Up @@ -351,4 +359,6 @@ alphaNumToken : UNICODE_TEXT | NUM | ALPHA;

classLiteralName : BQUOTE_STR;

noteText : STR;

%%
4 changes: 4 additions & 0 deletions packages/mermaid/src/diagrams/class/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ g.classGroup line {
stroke-dasharray: 3;
}
.dotted-line{
stroke-dasharray: 1 2;
}
#compositionStart, .composition {
fill: ${options.lineColor} !important;
stroke: ${options.lineColor} !important;
Expand Down
75 changes: 71 additions & 4 deletions packages/mermaid/src/diagrams/class/svgDraw.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
switch (type) {
case diagObj.db.relationType.AGGREGATION:
return 'aggregation';
case diagObj.db.EXTENSION:
case diagObj.db.relationType.EXTENSION:
return 'extension';
case diagObj.db.COMPOSITION:
case diagObj.db.relationType.COMPOSITION:
return 'composition';
case diagObj.db.DEPENDENCY:
case diagObj.db.relationType.DEPENDENCY:
return 'dependency';
case diagObj.db.LOLLIPOP:
case diagObj.db.relationType.LOLLIPOP:
return 'lollipop';
}
};
Expand Down Expand Up @@ -55,6 +55,9 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
if (relation.relation.lineType == 1) {
svgPath.attr('class', 'relation dashed-line');
}
if (relation.relation.lineType == 10) {
svgPath.attr('class', 'relation dotted-line');
}
if (relation.relation.type1 !== 'none') {
svgPath.attr(
'marker-start',
Expand Down Expand Up @@ -284,6 +287,69 @@ export const drawClass = function (elem, classDef, conf, diagObj) {
return classInfo;
};

/**
* Renders a note diagram
*
* @param {SVGSVGElement} elem The element to draw it into
* @param {{id: string; text: string; class: string;}} note
* @param conf
* @param diagObj
* @todo Add more information in the JSDOC here
*/
export const drawNote = function (elem, note, conf, diagObj) {
log.debug('Rendering note ', note, conf);

const id = note.id;
const noteInfo = {
id: id,
text: note.text,
width: 0,
height: 0,
};

// add class group
const g = elem.append('g').attr('id', id).attr('class', 'classGroup');

// add text
let text = g
.append('text')
.attr('y', conf.textHeight + conf.padding)
.attr('x', 0);

const lines = JSON.parse(`"${note.text}"`).split('\n');

lines.forEach(function (line) {
log.debug(`Adding line: ${line}`);
text.append('tspan').text(line).attr('class', 'title').attr('dy', conf.textHeight);
});

const noteBox = g.node().getBBox();

const rect = g
.insert('rect', ':first-child')
.attr('x', 0)
.attr('y', 0)
.attr('width', noteBox.width + 2 * conf.padding)
.attr(
'height',
noteBox.height + lines.length * conf.textHeight + conf.padding + 0.5 * conf.dividerMargin
);

const rectWidth = rect.node().getBBox().width;

// Center title
// We subtract the width of each text element from the class box width and divide it by 2
text.node().childNodes.forEach(function (x) {
x.setAttribute('x', (rectWidth - x.getBBox().width) / 2);
});

noteInfo.width = rectWidth;
noteInfo.height =
noteBox.height + lines.length * conf.textHeight + conf.padding + 0.5 * conf.dividerMargin;

return noteInfo;
};

export const parseMember = function (text) {
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+) *(\*|\$)?$/;
const methodRegEx = /^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/;
Expand Down Expand Up @@ -435,5 +501,6 @@ const parseClassifier = function (classifier) {
export default {
drawClass,
drawEdge,
drawNote,
parseMember,
};
Loading

0 comments on commit f05f07e

Please sign in to comment.