mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-02-04 07:13:25 +08:00
Adjusting initial classDiagram shape rendering
This commit is contained in:
parent
1e400624e0
commit
abe23250c4
@ -33,6 +33,7 @@
|
|||||||
classA : +attr1
|
classA : +attr1
|
||||||
classA : attr2
|
classA : attr2
|
||||||
classA : method1()
|
classA : method1()
|
||||||
|
<<interface>> classB
|
||||||
classB : method2() int
|
classB : method2() int
|
||||||
</div>
|
</div>
|
||||||
<script src="./mermaid.js"></script>
|
<script src="./mermaid.js"></script>
|
||||||
|
@ -20,12 +20,12 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||||||
|
|
||||||
const elem = _elem.insert('g').attr('class', 'root'); // eslint-disable-line
|
const elem = _elem.insert('g').attr('class', 'root'); // eslint-disable-line
|
||||||
if (!graph.nodes()) {
|
if (!graph.nodes()) {
|
||||||
log.trace('No nodes found for', graph);
|
log.info('No nodes found for', graph);
|
||||||
} else {
|
} else {
|
||||||
log.trace('Recursive render', graph.nodes());
|
log.info('Recursive render', graph.nodes());
|
||||||
}
|
}
|
||||||
if (graph.edges().length > 0) {
|
if (graph.edges().length > 0) {
|
||||||
log.trace('Recursive edges', graph.edge(graph.edges()[0]));
|
log.info('Recursive edges', graph.edge(graph.edges()[0]));
|
||||||
}
|
}
|
||||||
const clusters = elem.insert('g').attr('class', 'clusters'); // eslint-disable-line
|
const clusters = elem.insert('g').attr('class', 'clusters'); // eslint-disable-line
|
||||||
const edgePaths = elem.insert('g').attr('class', 'edgePaths');
|
const edgePaths = elem.insert('g').attr('class', 'edgePaths');
|
||||||
@ -39,14 +39,14 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||||||
if (typeof parentCluster !== 'undefined') {
|
if (typeof parentCluster !== 'undefined') {
|
||||||
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
|
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
|
||||||
// data.clusterPositioning = true;
|
// data.clusterPositioning = true;
|
||||||
log.trace('Setting data for cluster', data);
|
log.info('Setting data for cluster', data);
|
||||||
graph.setNode(parentCluster.id, data);
|
graph.setNode(parentCluster.id, data);
|
||||||
graph.setParent(v, parentCluster.id, data);
|
graph.setParent(v, parentCluster.id, data);
|
||||||
}
|
}
|
||||||
log.trace('(Insert) Node ' + v + ': ' + JSON.stringify(graph.node(v)));
|
log.info('(Insert) Node ' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||||
if (node && node.clusterNode) {
|
if (node && node.clusterNode) {
|
||||||
// const children = graph.children(v);
|
// const children = graph.children(v);
|
||||||
log.trace('Cluster identified', v, node, graph.node(v));
|
log.info('Cluster identified', v, node, graph.node(v));
|
||||||
const newEl = recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
|
const newEl = recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
|
||||||
updateNodeBounds(node, newEl);
|
updateNodeBounds(node, newEl);
|
||||||
setNodeElem(newEl, node);
|
setNodeElem(newEl, node);
|
||||||
@ -56,12 +56,12 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||||||
if (graph.children(v).length > 0) {
|
if (graph.children(v).length > 0) {
|
||||||
// This is a cluster but not to be rendered recusively
|
// This is a cluster but not to be rendered recusively
|
||||||
// Render as before
|
// Render as before
|
||||||
log.trace('Cluster - the non recursive path', v, node.id, node, graph);
|
log.info('Cluster - the non recursive path', v, node.id, node, graph);
|
||||||
log.trace(findNonClusterChild(node.id, graph));
|
log.info(findNonClusterChild(node.id, graph));
|
||||||
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
|
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
|
||||||
// insertCluster(clusters, graph.node(v));
|
// insertCluster(clusters, graph.node(v));
|
||||||
} else {
|
} else {
|
||||||
log.trace('Node - the non recursive path', v, node.id, node);
|
log.info('Node - the non recursive path', v, node.id, node);
|
||||||
insertNode(nodes, graph.node(v), dir);
|
insertNode(nodes, graph.node(v), dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,11 +73,11 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||||||
// TODO: pick optimal child in the cluster to us as link anchor
|
// TODO: pick optimal child in the cluster to us as link anchor
|
||||||
graph.edges().forEach(function(e) {
|
graph.edges().forEach(function(e) {
|
||||||
const edge = graph.edge(e.v, e.w, e.name);
|
const edge = graph.edge(e.v, e.w, e.name);
|
||||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
||||||
|
|
||||||
// Check if link is either from or to a cluster
|
// Check if link is either from or to a cluster
|
||||||
log.trace('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
|
log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
|
||||||
insertEdgeLabel(edgeLabels, edge);
|
insertEdgeLabel(edgeLabels, edge);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,11 +89,11 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
|||||||
log.info('#############################################');
|
log.info('#############################################');
|
||||||
log.info(graph);
|
log.info(graph);
|
||||||
dagre.layout(graph);
|
dagre.layout(graph);
|
||||||
log.trace('Graph after layout:', graphlib.json.write(graph));
|
log.info('Graph after layout:', graphlib.json.write(graph));
|
||||||
// Move the nodes to the correct place
|
// Move the nodes to the correct place
|
||||||
graph.nodes().forEach(function(v) {
|
graph.nodes().forEach(function(v) {
|
||||||
const node = graph.node(v);
|
const node = graph.node(v);
|
||||||
log.trace('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
|
log.info('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||||
log.info(
|
log.info(
|
||||||
'Position ' + v + ': (' + node.x,
|
'Position ' + v + ': (' + node.x,
|
||||||
',' + node.y,
|
',' + node.y,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import intersect from './intersect/index.js';
|
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import { logger } from '../logger'; // eslint-disable-line
|
import { logger } from '../logger'; // eslint-disable-line
|
||||||
import { labelHelper, updateNodeBounds, insertPolygonShape } from './shapes/util';
|
import { labelHelper, updateNodeBounds, insertPolygonShape } from './shapes/util';
|
||||||
import { getConfig } from '../config';
|
import { getConfig } from '../config';
|
||||||
|
import intersect from './intersect/index.js';
|
||||||
import createLabel from './createLabel';
|
import createLabel from './createLabel';
|
||||||
import note from './shapes/note';
|
import note from './shapes/note';
|
||||||
|
import intersectRect from './intersect/intersect-rect';
|
||||||
|
|
||||||
const question = (parent, node) => {
|
const question = (parent, node) => {
|
||||||
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
|
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
|
||||||
@ -539,6 +540,214 @@ const end = (parent, node) => {
|
|||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const class_box = (parent, node) => {
|
||||||
|
const halfPadding = node.padding / 2;
|
||||||
|
const rowPadding = 4;
|
||||||
|
const lineHeight = 8;
|
||||||
|
|
||||||
|
let classes;
|
||||||
|
if (!node.classes) {
|
||||||
|
classes = 'node default';
|
||||||
|
} else {
|
||||||
|
classes = 'node ' + node.classes;
|
||||||
|
}
|
||||||
|
// Add outer g element
|
||||||
|
const shapeSvg = parent
|
||||||
|
.insert('g')
|
||||||
|
.attr('class', classes)
|
||||||
|
.attr('id', node.id);
|
||||||
|
|
||||||
|
// Create the title label and insert it after the rect
|
||||||
|
const rect = shapeSvg.insert('rect', ':first-child');
|
||||||
|
const topLine = shapeSvg.insert('line');
|
||||||
|
const bottomLine = shapeSvg.insert('line');
|
||||||
|
let maxWidth = 0;
|
||||||
|
let maxHeight = rowPadding;
|
||||||
|
|
||||||
|
const labelContainer = shapeSvg.insert('g').attr('class', 'label');
|
||||||
|
let verticalPos = 0;
|
||||||
|
const hasInterface = node.classData.annotations && node.classData.annotations[0];
|
||||||
|
|
||||||
|
// 1. Create the labels
|
||||||
|
const interfaceLabel = labelContainer
|
||||||
|
.node()
|
||||||
|
.appendChild(createLabel(node.classData.annotations[0], node.labelStyle, true, true));
|
||||||
|
const interfaceBBox = interfaceLabel.getBBox();
|
||||||
|
if (node.classData.annotations[0]) {
|
||||||
|
maxHeight += interfaceBBox.height + rowPadding;
|
||||||
|
maxWidth += interfaceBBox.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classTitleLabel = labelContainer
|
||||||
|
.node()
|
||||||
|
.appendChild(createLabel(node.labelText, node.labelStyle, true, true));
|
||||||
|
const classTitleBBox = classTitleLabel.getBBox();
|
||||||
|
maxHeight += classTitleBBox.height + rowPadding;
|
||||||
|
if (classTitleBBox.width > maxWidth) {
|
||||||
|
maxWidth = classTitleBBox.width;
|
||||||
|
}
|
||||||
|
const classAttributes = [];
|
||||||
|
node.classData.members.forEach(str => {
|
||||||
|
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
|
||||||
|
const bbox = lbl.getBBox();
|
||||||
|
if (bbox.width > maxWidth) {
|
||||||
|
maxWidth = bbox.width;
|
||||||
|
}
|
||||||
|
maxHeight += bbox.height + rowPadding;
|
||||||
|
classAttributes.push(lbl);
|
||||||
|
});
|
||||||
|
|
||||||
|
const classMethods = [];
|
||||||
|
node.classData.methods.forEach(str => {
|
||||||
|
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
|
||||||
|
const bbox = lbl.getBBox();
|
||||||
|
if (bbox.width > maxWidth) {
|
||||||
|
maxWidth = bbox.width;
|
||||||
|
}
|
||||||
|
maxHeight += bbox.height + rowPadding;
|
||||||
|
|
||||||
|
classMethods.push(lbl);
|
||||||
|
});
|
||||||
|
|
||||||
|
maxHeight += lineHeight;
|
||||||
|
|
||||||
|
// 2. Position the labels
|
||||||
|
|
||||||
|
// position the interface label
|
||||||
|
if (hasInterface) {
|
||||||
|
select(interfaceLabel).attr(
|
||||||
|
'transform',
|
||||||
|
'translate( ' +
|
||||||
|
-(maxWidth + node.padding - interfaceBBox.width / 2) / 2 +
|
||||||
|
', ' +
|
||||||
|
(-1 * maxHeight) / 2 +
|
||||||
|
')'
|
||||||
|
);
|
||||||
|
verticalPos = interfaceBBox.height + rowPadding;
|
||||||
|
}
|
||||||
|
// Positin the class title label
|
||||||
|
select(classTitleLabel).attr(
|
||||||
|
'transform',
|
||||||
|
'translate( ' + -maxWidth + node.padding / 2 + ', ' + ((-1 * maxHeight) / 2 + verticalPos) + ')'
|
||||||
|
);
|
||||||
|
verticalPos += classTitleBBox.height + rowPadding;
|
||||||
|
|
||||||
|
topLine
|
||||||
|
.attr('class', 'divider')
|
||||||
|
.attr('x1', -maxWidth / 2 - halfPadding)
|
||||||
|
.attr('x2', maxWidth / 2 + halfPadding)
|
||||||
|
.attr('y1', -maxHeight / 2 - halfPadding + lineHeight + verticalPos)
|
||||||
|
.attr('y2', -maxHeight / 2 - halfPadding + lineHeight + verticalPos);
|
||||||
|
|
||||||
|
verticalPos += lineHeight;
|
||||||
|
|
||||||
|
classAttributes.forEach(lbl => {
|
||||||
|
select(lbl).attr(
|
||||||
|
'transform',
|
||||||
|
'translate( ' +
|
||||||
|
-maxWidth / 2 +
|
||||||
|
', ' +
|
||||||
|
((-1 * maxHeight) / 2 + verticalPos + lineHeight / 2) +
|
||||||
|
')'
|
||||||
|
);
|
||||||
|
verticalPos += classTitleBBox.height + rowPadding;
|
||||||
|
});
|
||||||
|
|
||||||
|
bottomLine
|
||||||
|
.attr('class', 'divider')
|
||||||
|
.attr('x1', -maxWidth / 2 - halfPadding)
|
||||||
|
.attr('x2', maxWidth / 2 + halfPadding)
|
||||||
|
.attr('y1', -maxHeight / 2 - halfPadding + lineHeight + verticalPos)
|
||||||
|
.attr('y2', -maxHeight / 2 - halfPadding + lineHeight + verticalPos);
|
||||||
|
|
||||||
|
verticalPos += lineHeight;
|
||||||
|
|
||||||
|
classMethods.forEach(lbl => {
|
||||||
|
select(lbl).attr(
|
||||||
|
'transform',
|
||||||
|
'translate( ' + -maxWidth / 2 + ', ' + ((-1 * maxHeight) / 2 + verticalPos) + ')'
|
||||||
|
);
|
||||||
|
verticalPos += classTitleBBox.height + rowPadding;
|
||||||
|
});
|
||||||
|
//
|
||||||
|
let bbox;
|
||||||
|
if (getConfig().flowchart.htmlLabels) {
|
||||||
|
const div = interfaceLabel.children[0];
|
||||||
|
const dv = select(interfaceLabel);
|
||||||
|
bbox = div.getBoundingClientRect();
|
||||||
|
dv.attr('width', bbox.width);
|
||||||
|
dv.attr('height', bbox.height);
|
||||||
|
}
|
||||||
|
// bbox = labelContainer.getBBox();
|
||||||
|
|
||||||
|
// logger.info('Text 2', text2);
|
||||||
|
// const textRows = text2.slice(1, text2.length);
|
||||||
|
// let titleBox = text.getBBox();
|
||||||
|
// const descr = label
|
||||||
|
// .node()
|
||||||
|
// .appendChild(createLabel(textRows.join('<br/>'), node.labelStyle, true, true));
|
||||||
|
|
||||||
|
// if (getConfig().flowchart.htmlLabels) {
|
||||||
|
// const div = descr.children[0];
|
||||||
|
// const dv = select(descr);
|
||||||
|
// bbox = div.getBoundingClientRect();
|
||||||
|
// dv.attr('width', bbox.width);
|
||||||
|
// dv.attr('height', bbox.height);
|
||||||
|
// }
|
||||||
|
// // bbox = label.getBBox();
|
||||||
|
// // logger.info(descr);
|
||||||
|
// select(descr).attr(
|
||||||
|
// 'transform',
|
||||||
|
// 'translate( ' +
|
||||||
|
// // (titleBox.width - bbox.width) / 2 +
|
||||||
|
// (bbox.width > titleBox.width ? 0 : (titleBox.width - bbox.width) / 2) +
|
||||||
|
// ', ' +
|
||||||
|
// (titleBox.height + halfPadding + 5) +
|
||||||
|
// ')'
|
||||||
|
// );
|
||||||
|
// select(text).attr(
|
||||||
|
// 'transform',
|
||||||
|
// 'translate( ' +
|
||||||
|
// // (titleBox.width - bbox.width) / 2 +
|
||||||
|
// (bbox.width < titleBox.width ? 0 : -(titleBox.width - bbox.width) / 2) +
|
||||||
|
// ', ' +
|
||||||
|
// 0 +
|
||||||
|
// ')'
|
||||||
|
// );
|
||||||
|
// // Get the size of the label
|
||||||
|
|
||||||
|
// // Bounding box for title and text
|
||||||
|
// bbox = label.node().getBBox();
|
||||||
|
|
||||||
|
// // Center the label
|
||||||
|
// label.attr(
|
||||||
|
// 'transform',
|
||||||
|
// 'translate(' + -bbox.width / 2 + ', ' + (-bbox.height / 2 - halfPadding + 3) + ')'
|
||||||
|
// );
|
||||||
|
|
||||||
|
rect
|
||||||
|
.attr('class', 'outer title-state')
|
||||||
|
.attr('x', -maxWidth / 2 - halfPadding)
|
||||||
|
.attr('y', -(maxHeight / 2) - halfPadding)
|
||||||
|
.attr('width', maxWidth + node.padding)
|
||||||
|
.attr('height', maxHeight + node.padding);
|
||||||
|
|
||||||
|
// innerLine
|
||||||
|
// .attr('class', 'divider')
|
||||||
|
// .attr('x1', -bbox.width / 2 - halfPadding)
|
||||||
|
// .attr('x2', bbox.width / 2 + halfPadding)
|
||||||
|
// .attr('y1', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding)
|
||||||
|
// .attr('y2', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding);
|
||||||
|
|
||||||
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
|
node.intersect = function(point) {
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
||||||
|
|
||||||
const shapes = {
|
const shapes = {
|
||||||
question,
|
question,
|
||||||
rect,
|
rect,
|
||||||
@ -558,7 +767,8 @@ const shapes = {
|
|||||||
note,
|
note,
|
||||||
subroutine,
|
subroutine,
|
||||||
fork: forkJoin,
|
fork: forkJoin,
|
||||||
join: forkJoin
|
join: forkJoin,
|
||||||
|
class_box
|
||||||
};
|
};
|
||||||
|
|
||||||
let nodeElems = {};
|
let nodeElems = {};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import { logger } from '../../logger';
|
import { logger } from '../../logger';
|
||||||
import { getConfig } from '../../config';
|
import { getConfig } from '../../config';
|
||||||
|
@ -85,16 +85,17 @@ export const addClasses = function(classes, g) {
|
|||||||
// Set the shape based parameters
|
// Set the shape based parameters
|
||||||
switch (vertex.type) {
|
switch (vertex.type) {
|
||||||
case 'class':
|
case 'class':
|
||||||
_shape = 'rect';
|
_shape = 'class_box';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_shape = 'rect';
|
_shape = 'class_box';
|
||||||
}
|
}
|
||||||
// Add the node
|
// Add the node
|
||||||
g.setNode(vertex.id, {
|
g.setNode(vertex.id, {
|
||||||
labelStyle: styles.labelStyle,
|
labelStyle: styles.labelStyle,
|
||||||
shape: _shape,
|
shape: _shape,
|
||||||
labelText: vertexText,
|
labelText: vertexText,
|
||||||
|
classData: vertex,
|
||||||
rx: radious,
|
rx: radious,
|
||||||
ry: radious,
|
ry: radious,
|
||||||
class: classStr,
|
class: classStr,
|
||||||
|
@ -10,6 +10,11 @@ g.classGroup text {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
stroke: $nodeBorder;
|
||||||
|
stroke-width: 1;
|
||||||
|
}
|
||||||
|
|
||||||
g.clickable {
|
g.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user