Fixes for erDiagram, pie charts and journey diagram

This commit is contained in:
Knut Sveidqvist 2022-07-04 18:11:58 +02:00
parent 5318ec6dbf
commit 682a1404ca
6 changed files with 81 additions and 67 deletions

View File

@ -30,15 +30,16 @@
<div class="mermaid2" style="width: 50%;"> <div class="mermaid" style="width: 50%;">
journey journey
title Adding journey diagram functionality to mermaid title My working day
accTitle: Adding acc journey diagram functionality to mermaid section Go to work
accDescr { Make tea: 5: Me
My multi-line description Go upstairs: 3: Me
of the diagram Do work: 1: Me, Cat
} section Go home
section Order from website Go downstairs: 5: Me
Sit down: 5: Mee
</div> </div>
<div class="mermaid2" style="width: 50%;"> <div class="mermaid2" style="width: 50%;">
pie pie
@ -135,7 +136,7 @@ requirementDiagram
test_req - contains -> test_req3 test_req - contains -> test_req3
test_req <- copies - test_entity2 test_req <- copies - test_entity2
</div> </div>
<div class="mermaid" style="width: 100%;"> <div class="mermaid2" style="width: 100%;">
gantt gantt
dateFormat YYYY-MM-DD dateFormat YYYY-MM-DD
title Adding GANTT diagram functionality to mermaid title Adding GANTT diagram functionality to mermaid
@ -242,6 +243,21 @@ class Class10 {
+run() +run()
} }
</div> </div>
<div class="mermaid2" style="width: 100%;">
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber
string make
string model
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string firstName
string lastName
int age
}
</div>
<script src="./mermaid.js"></script> <script src="./mermaid.js"></script>
<script> <script>

View File

@ -159,9 +159,11 @@ class Diagram {
break; break;
case 'journey': case 'journey':
log.debug('Journey'); log.debug('Journey');
journeyRenderer.setConf(cnf.journey);
this.parser = journeyParser; this.parser = journeyParser;
this.parser.parser.yy = journeyDb; this.parser.parser.yy = journeyDb;
this.db = journeyDb; this.db = journeyDb;
this.db.clear();
this.renderer = journeyRenderer; this.renderer = journeyRenderer;
break; break;
case 'requirement': case 'requirement':

View File

@ -1,7 +1,7 @@
import graphlib from 'graphlib'; import graphlib from 'graphlib';
import { line, curveBasis, select } from 'd3'; import { line, curveBasis, select } from 'd3';
import erDb from './erDb'; // import erDb from './erDb';
import erParser from './parser/erDiagram'; // import erParser from './parser/erDiagram';
import dagre from 'dagre'; import dagre from 'dagre';
import { getConfig } from '../../config'; import { getConfig } from '../../config';
import { log } from '../../logger'; import { log } from '../../logger';
@ -9,7 +9,7 @@ import erMarkers from './erMarkers';
import { configureSvgSize } from '../../utils'; import { configureSvgSize } from '../../utils';
import addSVGAccessibilityFields from '../../accessibility'; import addSVGAccessibilityFields from '../../accessibility';
const conf = {}; let conf = {};
/** /**
* Allows the top-level API module to inject config specific to this renderer, storing it in the * Allows the top-level API module to inject config specific to this renderer, storing it in the
@ -409,8 +409,9 @@ let relCnt = 0;
* @param g The graph containing the edge information * @param g The graph containing the edge information
* @param insert The insertion point in the svg DOM (because relationships have markers that need to * @param insert The insertion point in the svg DOM (because relationships have markers that need to
* sit 'behind' opaque entity boxes) * sit 'behind' opaque entity boxes)
* @param diagObj
*/ */
const drawRelationshipFromLayout = function (svg, rel, g, insert) { const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
relCnt++; relCnt++;
// Find the edge relating to this relationship // Find the edge relating to this relationship
@ -435,7 +436,7 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert) {
.attr('fill', 'none'); .attr('fill', 'none');
// ...and with dashes if necessary // ...and with dashes if necessary
if (rel.relSpec.relType === erDb.Identification.NON_IDENTIFYING) { if (rel.relSpec.relType === diagObj.db.Identification.NON_IDENTIFYING) {
svgPath.attr('stroke-dasharray', '8,8'); svgPath.attr('stroke-dasharray', '8,8');
} }
@ -457,40 +458,40 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert) {
// Note that the 'A' entity's marker is at the end of the relationship and the 'B' entity's marker is at the start // Note that the 'A' entity's marker is at the end of the relationship and the 'B' entity's marker is at the start
switch (rel.relSpec.cardA) { switch (rel.relSpec.cardA) {
case erDb.Cardinality.ZERO_OR_ONE: case diagObj.db.Cardinality.ZERO_OR_ONE:
svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_ONE_END + ')'); svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_ONE_END + ')');
break; break;
case erDb.Cardinality.ZERO_OR_MORE: case diagObj.db.Cardinality.ZERO_OR_MORE:
svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_MORE_END + ')'); svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_MORE_END + ')');
break; break;
case erDb.Cardinality.ONE_OR_MORE: case diagObj.db.Cardinality.ONE_OR_MORE:
svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ONE_OR_MORE_END + ')'); svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ONE_OR_MORE_END + ')');
break; break;
case erDb.Cardinality.ONLY_ONE: case diagObj.db.Cardinality.ONLY_ONE:
svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_END + ')'); svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_END + ')');
break; break;
} }
switch (rel.relSpec.cardB) { switch (rel.relSpec.cardB) {
case erDb.Cardinality.ZERO_OR_ONE: case diagObj.db.Cardinality.ZERO_OR_ONE:
svgPath.attr( svgPath.attr(
'marker-start', 'marker-start',
'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_ONE_START + ')' 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_ONE_START + ')'
); );
break; break;
case erDb.Cardinality.ZERO_OR_MORE: case diagObj.db.Cardinality.ZERO_OR_MORE:
svgPath.attr( svgPath.attr(
'marker-start', 'marker-start',
'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_MORE_START + ')' 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_MORE_START + ')'
); );
break; break;
case erDb.Cardinality.ONE_OR_MORE: case diagObj.db.Cardinality.ONE_OR_MORE:
svgPath.attr( svgPath.attr(
'marker-start', 'marker-start',
'url(' + url + '#' + erMarkers.ERMarkers.ONE_OR_MORE_START + ')' 'url(' + url + '#' + erMarkers.ERMarkers.ONE_OR_MORE_START + ')'
); );
break; break;
case erDb.Cardinality.ONLY_ONE: case diagObj.db.Cardinality.ONLY_ONE:
svgPath.attr('marker-start', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_START + ')'); svgPath.attr('marker-start', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_START + ')');
break; break;
} }
@ -540,12 +541,14 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert) {
* *
* @param text The text of the diagram * @param text The text of the diagram
* @param id The unique id of the DOM node that contains the diagram * @param id The unique id of the DOM node that contains the diagram
* @param _version
* @param diag
* @param diagObj
*/ */
export const draw = function (text, id) { export const draw = function (text, id, _version, diagObj) {
conf = getConfig().er;
log.info('Drawing ER diagram'); log.info('Drawing ER diagram');
erDb.clear(); // diag.db.clear();
const parser = erParser.parser;
parser.yy = erDb;
const securityLevel = getConfig().securityLevel; const securityLevel = getConfig().securityLevel;
// Handle root and Document for when rendering in sanbox mode // Handle root and Document for when rendering in sanbox mode
let sandboxElement; let sandboxElement;
@ -556,14 +559,14 @@ export const draw = function (text, id) {
securityLevel === 'sandbox' securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body) ? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body'); : select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document; // const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the text to populate erDb // Parse the text to populate erDb
try { // try {
parser.parse(text); // parser.parse(text);
} catch (err) { // } catch (err) {
log.debug('Parsing failed'); // log.debug('Parsing failed');
} // }
// Get a reference to the svg node that contains the text // Get a reference to the svg node that contains the text
const svg = root.select(`[id='${id}']`); const svg = root.select(`[id='${id}']`);
@ -612,12 +615,12 @@ export const draw = function (text, id) {
// Draw the entities (at 0,0), returning the first svg node that got // Draw the entities (at 0,0), returning the first svg node that got
// inserted - this represents the insertion point for relationship paths // inserted - this represents the insertion point for relationship paths
const firstEntity = drawEntities(svg, erDb.getEntities(), g); const firstEntity = drawEntities(svg, diagObj.db.getEntities(), g);
// TODO: externalise the addition of entities to the graph - it's a bit 'buried' in the above // TODO: externalise the addition of entities to the graph - it's a bit 'buried' in the above
// Add all the relationships to the graph // Add all the relationships to the graph
const relationships = addRelationships(erDb.getRelationships(), g); const relationships = addRelationships(diagObj.db.getRelationships(), g);
dagre.layout(g); // Node and edge positions will be updated dagre.layout(g); // Node and edge positions will be updated
@ -626,7 +629,7 @@ export const draw = function (text, id) {
// Draw the relationships // Draw the relationships
relationships.forEach(function (rel) { relationships.forEach(function (rel) {
drawRelationshipFromLayout(svg, rel, g, firstEntity); drawRelationshipFromLayout(svg, rel, g, firstEntity, diagObj);
}); });
const padding = conf.diagramPadding; const padding = conf.diagramPadding;
@ -639,7 +642,7 @@ export const draw = function (text, id) {
svg.attr('viewBox', `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`); svg.attr('viewBox', `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`);
addSVGAccessibilityFields(parser.yy, svg, id); addSVGAccessibilityFields(diagObj.db, svg, id);
}; // draw }; // draw
export default { export default {

View File

@ -1,7 +1,5 @@
/** Created by AshishJ on 11-09-2019. */ /** Created by AshishJ on 11-09-2019. */
import { select, scaleOrdinal, pie as d3pie, arc } from 'd3'; import { select, scaleOrdinal, pie as d3pie, arc } from 'd3';
import pieData from './pieDb';
import pieParser from './parser/pie';
import { log } from '../../logger'; import { log } from '../../logger';
import { configureSvgSize } from '../../utils'; import { configureSvgSize } from '../../utils';
import * as configApi from '../../config'; import * as configApi from '../../config';
@ -17,11 +15,9 @@ let conf = configApi.getConfig();
*/ */
let width; let width;
const height = 450; const height = 450;
export const draw = (txt, id) => { export const draw = (txt, id, _version, diagObj) => {
try { try {
conf = configApi.getConfig(); conf = configApi.getConfig();
const parser = pieParser.parser;
parser.yy = pieData;
log.debug('Rendering info diagram\n' + txt); log.debug('Rendering info diagram\n' + txt);
const securityLevel = configApi.getConfig().securityLevel; const securityLevel = configApi.getConfig().securityLevel;
@ -37,8 +33,8 @@ export const draw = (txt, id) => {
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document; const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the Pie Chart definition // Parse the Pie Chart definition
parser.yy.clear(); diagObj.db.clear();
parser.parse(txt); diagObj.parser.parse(txt);
log.debug('Parsed info diagram'); log.debug('Parsed info diagram');
const elem = doc.getElementById(id); const elem = doc.getElementById(id);
width = elem.parentElement.offsetWidth; width = elem.parentElement.offsetWidth;
@ -57,7 +53,7 @@ export const draw = (txt, id) => {
const diagram = root.select('#' + id); const diagram = root.select('#' + id);
configureSvgSize(diagram, height, width, conf.pie.useMaxWidth); configureSvgSize(diagram, height, width, conf.pie.useMaxWidth);
addSVGAccessibilityFields(parser.yy, diagram, id); addSVGAccessibilityFields(diagObj.db, diagram, id);
// Set viewBox // Set viewBox
elem.setAttribute('viewBox', '0 0 ' + width + ' ' + height); elem.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
@ -72,7 +68,7 @@ export const draw = (txt, id) => {
.append('g') .append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')'); .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var data = pieData.getSections(); var data = diagObj.db.getSections();
var sum = 0; var sum = 0;
Object.keys(data).forEach(function (key) { Object.keys(data).forEach(function (key) {
sum += data[key]; sum += data[key];
@ -136,7 +132,7 @@ export const draw = (txt, id) => {
svg svg
.append('text') .append('text')
.text(parser.yy.getDiagramTitle()) .text(diagObj.db.getDiagramTitle())
.attr('x', 0) .attr('x', 0)
.attr('y', -(height - 50) / 2) .attr('y', -(height - 50) / 2)
.attr('class', 'pieTitleText'); .attr('class', 'pieTitleText');
@ -169,7 +165,7 @@ export const draw = (txt, id) => {
.attr('x', legendRectSize + legendSpacing) .attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing) .attr('y', legendRectSize - legendSpacing)
.text(function (d) { .text(function (d) {
if (parser.yy.getShowData() || conf.showData || conf.pie.showData) { if (diagObj.db.getShowData() || conf.showData || conf.pie.showData) {
return d.data[0] + ' [' + d.data[1] + ']'; return d.data[0] + ' [' + d.data[1] + ']';
} else { } else {
return d.data[0]; return d.data[0];

View File

@ -1,13 +1,9 @@
import { select } from 'd3'; import { select } from 'd3';
import { parser } from './parser/journey';
import journeyDb from './journeyDb';
import svgDraw from './svgDraw'; import svgDraw from './svgDraw';
import { getConfig } from '../../config'; import { getConfig } from '../../config';
import { configureSvgSize } from '../../utils'; import { configureSvgSize } from '../../utils';
import addSVGAccessibilityFields from '../../accessibility'; import addSVGAccessibilityFields from '../../accessibility';
parser.yy = journeyDb;
export const setConf = function (cnf) { export const setConf = function (cnf) {
const keys = Object.keys(cnf); const keys = Object.keys(cnf);
@ -50,10 +46,10 @@ function drawActorLegend(diagram) {
} }
const conf = getConfig().journey; const conf = getConfig().journey;
const LEFT_MARGIN = getConfig().journey.leftMargin; const LEFT_MARGIN = getConfig().journey.leftMargin;
export const draw = function (text, id) { export const draw = function (text, id, version, diagObj) {
const conf = getConfig().journey; const conf = getConfig().journey;
parser.yy.clear(); diagObj.db.clear();
parser.parse(text + '\n'); diagObj.parser.parse(text + '\n');
const securityLevel = getConfig().securityLevel; const securityLevel = getConfig().securityLevel;
// Handle root and Document for when rendering in sanbox mode // Handle root and Document for when rendering in sanbox mode
@ -65,17 +61,18 @@ export const draw = function (text, id) {
securityLevel === 'sandbox' securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body) ? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body'); : select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document; // const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
bounds.init(); bounds.init();
const diagram = root.select('#' + id); const diagram = root.select('#' + id);
svgDraw.initGraphics(diagram); svgDraw.initGraphics(diagram);
const tasks = parser.yy.getTasks(); const tasks = diagObj.db.getTasks();
const title = parser.yy.getDiagramTitle(); console.log('text and tasks', text, tasks);
const title = diagObj.db.getDiagramTitle();
const actorNames = parser.yy.getActors(); const actorNames = diagObj.db.getActors();
for (let member in actors) delete actors[member]; for (let member in actors) delete actors[member];
let actorPos = 0; let actorPos = 0;
actorNames.forEach((actorName) => { actorNames.forEach((actorName) => {
@ -122,7 +119,7 @@ export const draw = function (text, id) {
diagram.attr('preserveAspectRatio', 'xMinYMin meet'); diagram.attr('preserveAspectRatio', 'xMinYMin meet');
diagram.attr('height', height + extraVertForTitle + 25); diagram.attr('height', height + extraVertForTitle + 25);
addSVGAccessibilityFields(parser.yy, diagram, id); addSVGAccessibilityFields(diagObj.db, diagram, id);
}; };
export const bounds = { export const bounds = {

View File

@ -380,8 +380,8 @@ const render = function (id, _txt, cb, container) {
break; break;
case 'gantt': case 'gantt':
cnf.gantt.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute; cnf.gantt.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
ganttRenderer.setConf(cnf.gantt); diag.renderer.setConf(cnf.gantt);
ganttRenderer.draw(txt, id, pkg.version, diag); diag.renderer.draw(txt, id, pkg.version, diag);
break; break;
case 'class': case 'class':
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute; cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
@ -409,15 +409,15 @@ const render = function (id, _txt, cb, container) {
infoRenderer.draw(txt, id, pkg.version, diag); infoRenderer.draw(txt, id, pkg.version, diag);
break; break;
case 'pie': case 'pie':
pieRenderer.draw(txt, id, pkg.version, diag); diag.renderer.draw(txt, id, pkg.version, diag);
break; break;
case 'er': case 'er':
erRenderer.setConf(cnf.er); // erRenderer.setConf(cnf.er);
erRenderer.draw(txt, id, pkg.version, diag); diag.renderer.draw(txt, id, pkg.version, diag);
break; break;
case 'journey': case 'journey':
journeyRenderer.setConf(cnf.journey); // journeyRenderer.setConf(cnf.journey);
journeyRenderer.draw(txt, id, pkg.version, diag); diag.renderer.draw(txt, id, pkg.version, diag);
break; break;
case 'requirement': case 'requirement':
requirementRenderer.setConf(cnf.requirement); requirementRenderer.setConf(cnf.requirement);