mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-21 08:19:43 +02:00
Merge pull request #1370 from mermaid-js/feature/1295_generic_rendering_engine
Feature/1295 generic rendering engine
This commit is contained in:
@@ -100,6 +100,20 @@ double_arrow_circle
|
||||
Lets try to make these types semantic free so that diagram type semantics does not find its way in to this more generic layer.
|
||||
|
||||
|
||||
Required edgeData for proper rendering:
|
||||
|
||||
| property | description |
|
||||
| ---------- | ---------------------------------------- |
|
||||
| id | Id of the edge |
|
||||
| arrowHead | overlap between arrowHead and arrowType? |
|
||||
| arrowType | overlap between arrowHead and arrowType? |
|
||||
| style | |
|
||||
| labelStyle | |
|
||||
| label | overlap between label and labelText? |
|
||||
| labelPos | |
|
||||
| labelType | overlap between label and labelText? |
|
||||
|
||||
|
||||
# Markers
|
||||
|
||||
Define what markers that should be included in the diagram with the insert markers function. The function takes two arguments, first the element in which the markers should be included and a list of the markers that should be added.
|
||||
|
@@ -149,7 +149,39 @@ const roundedWithTitle = (parent, node) => {
|
||||
return shapeSvg;
|
||||
};
|
||||
|
||||
const shapes = { rect, roundedWithTitle, noteGroup };
|
||||
const divider = (parent, node) => {
|
||||
// Add outer g element
|
||||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', node.classes)
|
||||
.attr('id', node.id);
|
||||
|
||||
// add the rect
|
||||
const rect = shapeSvg.insert('rect', ':first-child');
|
||||
|
||||
const padding = 0 * node.padding;
|
||||
const halfPadding = padding / 2;
|
||||
|
||||
// center the rect around its coordinate
|
||||
rect
|
||||
.attr('class', 'divider')
|
||||
.attr('x', node.x - node.width / 2 - halfPadding)
|
||||
.attr('y', node.y - node.height / 2)
|
||||
.attr('width', node.width + padding)
|
||||
.attr('height', node.height + padding);
|
||||
|
||||
const rectBox = rect.node().getBBox();
|
||||
node.width = rectBox.width;
|
||||
node.height = rectBox.height;
|
||||
|
||||
node.intersect = function(point) {
|
||||
return intersectRect(node, point);
|
||||
};
|
||||
|
||||
return shapeSvg;
|
||||
};
|
||||
|
||||
const shapes = { rect, roundedWithTitle, noteGroup, divider };
|
||||
|
||||
let clusterElems = {};
|
||||
|
||||
|
@@ -1,9 +1,13 @@
|
||||
const createLabel = (vertexText, style) => {
|
||||
const createLabel = (vertexText, style, isTitle) => {
|
||||
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
svgLabel.setAttribute('style', style.replace('color:', 'fill:'));
|
||||
let rows = [];
|
||||
if (vertexText) {
|
||||
rows = vertexText.split(/\n|<br\s*\/?>/gi);
|
||||
if (typeof vertexText === 'string') {
|
||||
rows = vertexText.split(/\\n|\n|<br\s*\/?>/gi);
|
||||
} else if (Array.isArray(vertexText)) {
|
||||
rows = vertexText;
|
||||
} else {
|
||||
rows = [];
|
||||
}
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
@@ -11,6 +15,11 @@ const createLabel = (vertexText, style) => {
|
||||
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
||||
tspan.setAttribute('dy', '1em');
|
||||
tspan.setAttribute('x', '0');
|
||||
if (isTitle) {
|
||||
tspan.setAttribute('class', 'title-row');
|
||||
} else {
|
||||
tspan.setAttribute('class', 'row');
|
||||
}
|
||||
tspan.textContent = rows[j].trim();
|
||||
svgLabel.appendChild(tspan);
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
||||
};
|
||||
|
||||
export const positionEdgeLabel = edge => {
|
||||
logger.info('Moving label', edge.id, edge.label, edgeLabels[edge.id]);
|
||||
const el = edgeLabels[edge.id];
|
||||
el.attr('transform', 'translate(' + edge.x + ', ' + edge.y + ')');
|
||||
};
|
||||
|
@@ -14,7 +14,10 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f
|
||||
import { logger as log } from '../logger';
|
||||
|
||||
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
log.trace('Graph in recursive render:', graphlib.json.write(graph), parentCluster);
|
||||
log.info('Graph in recursive render:', graphlib.json.write(graph), parentCluster);
|
||||
const dir = graph.graph().rankdir;
|
||||
log.warn('Dir in recursive render - dir:', dir);
|
||||
|
||||
const elem = _elem.insert('g').attr('class', 'root'); // eslint-disable-line
|
||||
if (!graph.nodes()) {
|
||||
log.trace('No nodes found for', graph);
|
||||
@@ -59,7 +62,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
// insertCluster(clusters, graph.node(v));
|
||||
} else {
|
||||
log.trace('Node - the non recursive path', v, node.id, node);
|
||||
insertNode(nodes, graph.node(v));
|
||||
insertNode(nodes, graph.node(v), dir);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -79,14 +82,14 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
});
|
||||
|
||||
graph.edges().forEach(function(e) {
|
||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||
});
|
||||
log.trace('#############################################');
|
||||
log.trace('### Layout ###');
|
||||
log.trace('#############################################');
|
||||
log.trace(graph);
|
||||
log.info('#############################################');
|
||||
log.info('### Layout ###');
|
||||
log.info('#############################################');
|
||||
log.info(graph);
|
||||
dagre.layout(graph);
|
||||
log.warn('Graph after layout:', graphlib.json.write(graph));
|
||||
log.trace('Graph after layout:', graphlib.json.write(graph));
|
||||
// Move the nodes to the correct place
|
||||
graph.nodes().forEach(function(v) {
|
||||
const node = graph.node(v);
|
||||
@@ -119,7 +122,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
// Move the edge labels to the correct place after layout
|
||||
graph.edges().forEach(function(e) {
|
||||
const edge = graph.edge(e);
|
||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
||||
|
||||
insertEdge(edgePaths, edge, clusterDb, diagramtype);
|
||||
positionEdgeLabel(edge);
|
||||
@@ -138,7 +141,7 @@ export const render = (elem, graph, markers, diagramtype, id) => {
|
||||
log.warn('Graph before:', graphlib.json.write(graph));
|
||||
adjustClustersAndEdges(graph);
|
||||
log.warn('Graph after:', graphlib.json.write(graph));
|
||||
|
||||
log.warn('Graph ever after:', graph.graph());
|
||||
recursiveRender(elem, graph, diagramtype);
|
||||
};
|
||||
|
||||
|
@@ -31,13 +31,17 @@ const isDecendant = (id, ancenstorId) => {
|
||||
};
|
||||
|
||||
const edgeInCluster = (edge, clusterId) => {
|
||||
log.info('Decendants of ', clusterId, ' is ', decendants[clusterId]);
|
||||
log.info('Edge is ', edge);
|
||||
// Edges to/from the cluster is not in the cluster, they are in the parent
|
||||
if (!(edge.v === clusterId || edge.w === clusterId)) return false;
|
||||
if (edge.v === clusterId) return false;
|
||||
if (edge.w === clusterId) return false;
|
||||
|
||||
if (!decendants[clusterId]) {
|
||||
log.debug('Tilt, ', clusterId, ',not in decendants');
|
||||
return false;
|
||||
}
|
||||
log.info('Here ');
|
||||
|
||||
if (decendants[clusterId].indexOf(edge.v) >= 0) return true;
|
||||
if (isDecendant(edge.v, clusterId)) return true;
|
||||
@@ -80,17 +84,26 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
||||
const edges = graph.edges(node);
|
||||
log.debug('Copying Edges', edges);
|
||||
edges.forEach(edge => {
|
||||
log.trace('Edge', edge);
|
||||
log.info('Edge', edge);
|
||||
const data = graph.edge(edge.v, edge.w, edge.name);
|
||||
log.trace('Edge data', data, rootId);
|
||||
log.info('Edge data', data, rootId);
|
||||
try {
|
||||
// Do not copy edges in and out of the root cluster, they belong to the parent graph
|
||||
if (edgeInCluster(edge, rootId)) {
|
||||
log.trace('Copying as ', edge.v, edge.w, data, edge.name);
|
||||
log.info('Copying as ', edge.v, edge.w, data, edge.name);
|
||||
newGraph.setEdge(edge.v, edge.w, data, edge.name);
|
||||
log.trace('newGraph edges ', newGraph.edges(), newGraph.edge(newGraph.edges()[0]));
|
||||
log.info('newGraph edges ', newGraph.edges(), newGraph.edge(newGraph.edges()[0]));
|
||||
} else {
|
||||
log.trace('Skipping copy of edge as ', rootId, edge.v, edge.w, clusterId);
|
||||
log.info(
|
||||
'Skipping copy of edge ',
|
||||
edge.v,
|
||||
'-->',
|
||||
edge.w,
|
||||
' rootId: ',
|
||||
rootId,
|
||||
' clusterId:',
|
||||
clusterId
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
@@ -316,12 +329,14 @@ export const extractor = (graph, depth) => {
|
||||
depth
|
||||
);
|
||||
|
||||
const graphSettings = graph.graph();
|
||||
|
||||
const clusterGraph = new graphlib.Graph({
|
||||
multigraph: true,
|
||||
compound: true
|
||||
})
|
||||
.setGraph({
|
||||
rankdir: 'TB',
|
||||
rankdir: graphSettings.rankdir === 'TB' ? 'LR' : 'TB',
|
||||
// Todo: set proper spacing
|
||||
nodesep: 50,
|
||||
ranksep: 50,
|
||||
|
@@ -314,6 +314,34 @@ describe('Graphlib decorations', () => {
|
||||
// expect(edgeData.data).toBe('link2');
|
||||
// expect(validate(g)).toBe(true);
|
||||
});
|
||||
it('adjustClustersAndEdges the extracted graphs shall contain the correct links GLB20', function () {
|
||||
/*
|
||||
a --> b
|
||||
subgraph b [Test]
|
||||
c --> d -->e
|
||||
end
|
||||
*/
|
||||
g.setNode('a', { data: 1 });
|
||||
g.setNode('b', { data: 2 });
|
||||
g.setNode('c', { data: 3 });
|
||||
g.setNode('d', { data: 3 });
|
||||
g.setNode('e', { data: 3 });
|
||||
g.setParent('c', 'b');
|
||||
g.setParent('d', 'b');
|
||||
g.setParent('e', 'b');
|
||||
g.setEdge('a', 'b', { data: 'link1' }, '1');
|
||||
g.setEdge('c', 'd', { data: 'link2' }, '2');
|
||||
g.setEdge('d', 'e', { data: 'link2' }, '2');
|
||||
|
||||
logger.info('Graph before', graphlib.json.write(g))
|
||||
adjustClustersAndEdges(g);
|
||||
const bGraph = g.node('b').graph;
|
||||
// logger.trace('Graph after', graphlib.json.write(g))
|
||||
logger.info('Graph after', graphlib.json.write(bGraph));
|
||||
expect(bGraph.nodes().length).toBe(3);
|
||||
expect(bGraph.edges().length).toBe(2);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
describe('extractDecendants', function () {
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import intersect from './intersect/index.js';
|
||||
import { select } from 'd3';
|
||||
import { logger } from '../logger'; // eslint-disable-line
|
||||
import { labelHelper, updateNodeBounds, insertPolygonShape } from './shapes/util';
|
||||
import createLabel from './createLabel';
|
||||
import note from './shapes/note';
|
||||
|
||||
const question = (parent, node) => {
|
||||
@@ -253,6 +255,7 @@ const rect = (parent, node) => {
|
||||
const rect = shapeSvg.insert('rect', ':first-child');
|
||||
|
||||
rect
|
||||
.attr('class', 'basic')
|
||||
.attr('rx', node.rx)
|
||||
.attr('ry', node.ry)
|
||||
.attr('x', -bbox.width / 2 - halfPadding)
|
||||
@@ -268,6 +271,74 @@ const rect = (parent, node) => {
|
||||
|
||||
return shapeSvg;
|
||||
};
|
||||
const rectWithTitle = (parent, node) => {
|
||||
// const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes);
|
||||
|
||||
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 innerRect = shapeSvg.insert('rect');
|
||||
const innerLine = shapeSvg.insert('line');
|
||||
|
||||
const label = shapeSvg.insert('g').attr('class', 'label');
|
||||
|
||||
const text2 = node.labelText.flat();
|
||||
logger.info('Label text', text2[0]);
|
||||
|
||||
const text = label.node().appendChild(createLabel(text2[0], node.labelStyle, true));
|
||||
const textRows = text2.slice(1, text2.length);
|
||||
let titleBox = text.getBBox();
|
||||
const descr = label
|
||||
.node()
|
||||
.appendChild(createLabel(textRows.join('<br/>'), node.labelStyle, true));
|
||||
|
||||
logger.info(descr);
|
||||
const halfPadding = node.padding / 2;
|
||||
select(descr).attr('transform', 'translate( 0' + ', ' + (titleBox.height + halfPadding) + ')');
|
||||
// Get the size of the label
|
||||
|
||||
// Bounding box for title and text
|
||||
const 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', -bbox.width / 2 - halfPadding)
|
||||
.attr('y', -bbox.height / 2 - halfPadding)
|
||||
.attr('width', bbox.width + node.padding)
|
||||
.attr('height', bbox.height + 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 stadium = (parent, node) => {
|
||||
const { shapeSvg, bbox } = labelHelper(parent, node);
|
||||
@@ -335,6 +406,41 @@ const start = (parent, node) => {
|
||||
|
||||
return shapeSvg;
|
||||
};
|
||||
|
||||
const forkJoin = (parent, node, dir) => {
|
||||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', 'node default')
|
||||
.attr('id', node.id);
|
||||
|
||||
let width = 70;
|
||||
let height = 10;
|
||||
|
||||
if (dir === 'LR') {
|
||||
width = 10;
|
||||
height = 70;
|
||||
}
|
||||
|
||||
const shape = shapeSvg
|
||||
.append('rect')
|
||||
.style('stroke', 'black')
|
||||
.style('fill', 'black')
|
||||
.attr('x', (-1 * width) / 2)
|
||||
.attr('y', (-1 * height) / 2)
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.attr('class', 'fork-join');
|
||||
|
||||
updateNodeBounds(node, shape);
|
||||
node.height = node.height + node.padding / 2;
|
||||
node.width = node.width + node.padding / 2;
|
||||
node.intersect = function(point) {
|
||||
return intersect.rect(node, point);
|
||||
};
|
||||
|
||||
return shapeSvg;
|
||||
};
|
||||
|
||||
const end = (parent, node) => {
|
||||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
@@ -367,6 +473,7 @@ const end = (parent, node) => {
|
||||
const shapes = {
|
||||
question,
|
||||
rect,
|
||||
rectWithTitle,
|
||||
circle,
|
||||
stadium,
|
||||
hexagon,
|
||||
@@ -379,13 +486,15 @@ const shapes = {
|
||||
cylinder,
|
||||
start,
|
||||
end,
|
||||
note
|
||||
note,
|
||||
fork: forkJoin,
|
||||
join: forkJoin
|
||||
};
|
||||
|
||||
let nodeElems = {};
|
||||
|
||||
export const insertNode = (elem, node) => {
|
||||
nodeElems[node.id] = shapes[node.shape](elem, node);
|
||||
export const insertNode = (elem, node, dir) => {
|
||||
nodeElems[node.id] = shapes[node.shape](elem, node, dir);
|
||||
};
|
||||
export const setNodeElem = (elem, node) => {
|
||||
nodeElems[node.id] = elem;
|
||||
|
54
src/dagre-wrapper/patterns.js
Normal file
54
src/dagre-wrapper/patterns.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||
*/
|
||||
|
||||
// import { logger } from '../logger';
|
||||
|
||||
// Only add the number of markers that the diagram needs
|
||||
const insertPatterns = (elem, patternArray, type, id) => {
|
||||
patternArray.forEach(patternName => {
|
||||
patterns[patternName](elem, type, id);
|
||||
});
|
||||
};
|
||||
|
||||
{
|
||||
/* <svg height="10" width="10" xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
{' '}
|
||||
<defs>
|
||||
{' '}
|
||||
<pattern id="circles-1" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
{' '}
|
||||
<image
|
||||
xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSJ3aGl0ZSIgLz4KICA8Y2lyY2xlIGN4PSIxIiBjeT0iMSIgcj0iMSIgZmlsbD0iYmxhY2siLz4KPC9zdmc+"
|
||||
x="0"
|
||||
y="0"
|
||||
width="10"
|
||||
height="10"
|
||||
>
|
||||
{' '}
|
||||
</image>{' '}
|
||||
</pattern>{' '}
|
||||
</defs>{' '}
|
||||
</svg>; */
|
||||
}
|
||||
|
||||
const dots = (elem, type) => {
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', type + '-barbEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 20)
|
||||
.attr('markerHeight', 14)
|
||||
.attr('markerUnits', 0)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 19,7 L9,13 L14,7 L9,1 Z');
|
||||
};
|
||||
|
||||
// TODO rename the class diagram markers to something shape descriptive and semanitc free
|
||||
const patterns = {
|
||||
dots
|
||||
};
|
||||
export default insertPatterns;
|
@@ -132,6 +132,7 @@ export const addState = function(id, type, doc, descr, note) {
|
||||
}
|
||||
}
|
||||
if (descr) {
|
||||
logger.info('Adding state ', id, descr);
|
||||
if (typeof descr === 'string') addDescription(id, descr.trim());
|
||||
|
||||
if (typeof descr === 'object') {
|
||||
|
@@ -42,6 +42,9 @@ const setupNode = (g, parent, node, altFlag) => {
|
||||
if (node.start === false) {
|
||||
shape = 'end';
|
||||
}
|
||||
if (node.type !== 'default') {
|
||||
shape = node.type;
|
||||
}
|
||||
|
||||
if (!nodeDb[node.id]) {
|
||||
nodeDb[node.id] = {
|
||||
@@ -52,9 +55,27 @@ const setupNode = (g, parent, node, altFlag) => {
|
||||
};
|
||||
}
|
||||
|
||||
// Description
|
||||
// Build of the array of description strings accordinging
|
||||
if (node.description) {
|
||||
nodeDb[node.id].description = node.description;
|
||||
if (Array.isArray(nodeDb[node.id].description)) {
|
||||
// There already is an array of strings,add to it
|
||||
nodeDb[node.id].shape = 'rectWithTitle';
|
||||
nodeDb[node.id].description.push(node.description);
|
||||
} else {
|
||||
if (nodeDb[node.id].description.length > 0) {
|
||||
// if there is a description already transformit to an array
|
||||
nodeDb[node.id].shape = 'rectWithTitle';
|
||||
if (nodeDb[node.id].description === node.id) {
|
||||
// If the previous description was the is, remove it
|
||||
nodeDb[node.id].description = [node.description];
|
||||
} else {
|
||||
nodeDb[node.id].description = [nodeDb[node.id].description, node.description];
|
||||
}
|
||||
} else {
|
||||
nodeDb[node.id].shape = 'rect';
|
||||
nodeDb[node.id].description = node.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save data for description and group so that for instance a statement without description overwrites
|
||||
@@ -64,7 +85,7 @@ const setupNode = (g, parent, node, altFlag) => {
|
||||
if (!nodeDb[node.id].type && node.doc) {
|
||||
logger.info('Setting cluser for ', node.id);
|
||||
nodeDb[node.id].type = 'group';
|
||||
nodeDb[node.id].shape = 'roundedWithTitle';
|
||||
nodeDb[node.id].shape = node.type === 'divider' ? 'divider' : 'roundedWithTitle';
|
||||
nodeDb[node.id].classes =
|
||||
nodeDb[node.id].classes +
|
||||
' ' +
|
||||
@@ -155,10 +176,12 @@ const setupDoc = (g, parent, doc, altFlag) => {
|
||||
setupNode(g, parent, item.state1, altFlag);
|
||||
setupNode(g, parent, item.state2, altFlag);
|
||||
const edgeData = {
|
||||
id: 'edge' + cnt,
|
||||
arrowhead: 'normal',
|
||||
arrowType: 'arrow_barb',
|
||||
style: 'fill:none',
|
||||
labelStyle: '',
|
||||
label: item.description,
|
||||
arrowheadStyle: 'fill: #333',
|
||||
labelpos: 'c',
|
||||
labelType: 'text'
|
||||
|
@@ -36,6 +36,9 @@
|
||||
|
||||
.edgeLabel {
|
||||
background-color: $edgeLabelBackground;
|
||||
rect {
|
||||
opacity: 0.5;
|
||||
}
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@@ -85,6 +85,16 @@ g.stateGroup line {
|
||||
fill: $nodeBkg;
|
||||
stroke: $nodeBorder;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
.statediagram-cluster rect.outer {
|
||||
rx: 5px;
|
||||
ry: 5px;
|
||||
}
|
||||
.statediagram-state .divider {
|
||||
stroke: $nodeBorder;
|
||||
}
|
||||
|
||||
.statediagram-state .title-state {
|
||||
rx: 5px;
|
||||
ry: 5px;
|
||||
}
|
||||
@@ -100,10 +110,14 @@ g.stateGroup line {
|
||||
ry:0;
|
||||
}
|
||||
|
||||
.statediagram-state rect {
|
||||
.statediagram-state rect.basic {
|
||||
rx: 5px;
|
||||
ry: 5px;
|
||||
}
|
||||
.statediagram-state rect.divider {
|
||||
stroke-dasharray: 10,10;
|
||||
fill: #efefef;
|
||||
}
|
||||
|
||||
.note-edge {
|
||||
stroke-dasharray: 5;
|
||||
|
Reference in New Issue
Block a user