mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-19 07:19:41 +02:00
Merge branch '5237-unified-layout-common-renderer' into 5237-unified-layout-flowchart
This commit is contained in:
@@ -1,2 +1,4 @@
|
|||||||
|
circo
|
||||||
handdrawnSeed
|
handdrawnSeed
|
||||||
|
neato
|
||||||
newbranch
|
newbranch
|
||||||
|
@@ -16,3 +16,5 @@ generated/
|
|||||||
# Ignore the files creates in /demos/dev except for example.html
|
# Ignore the files creates in /demos/dev except for example.html
|
||||||
demos/dev/**
|
demos/dev/**
|
||||||
!/demos/dev/example.html
|
!/demos/dev/example.html
|
||||||
|
# TODO: Lots of errors to fix
|
||||||
|
cypress/platform/state-refactor.html
|
||||||
|
@@ -33,7 +33,9 @@
|
|||||||
background-image: radial-gradient(#fff 51%, transparent 91%),
|
background-image: radial-gradient(#fff 51%, transparent 91%),
|
||||||
radial-gradient(#fff 51%, transparent 91%);
|
radial-gradient(#fff 51%, transparent 91%);
|
||||||
background-size: 20px 20px;
|
background-size: 20px 20px;
|
||||||
background-position: 0 0, 10px 10px;
|
background-position:
|
||||||
|
0 0,
|
||||||
|
10px 10px;
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
}
|
}
|
||||||
.malware {
|
.malware {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
@@ -118,7 +118,7 @@ The siteConfig
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218)
|
[config.ts:221](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L221)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@@ -1,209 +0,0 @@
|
|||||||
import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js';
|
|
||||||
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json.js';
|
|
||||||
import insertMarkers from './markers.js';
|
|
||||||
import { updateNodeBounds } from './shapes/util.js';
|
|
||||||
import {
|
|
||||||
clear as clearGraphlib,
|
|
||||||
clusterDb,
|
|
||||||
adjustClustersAndEdges,
|
|
||||||
findNonClusterChild,
|
|
||||||
sortNodesByHierarchy,
|
|
||||||
} from './mermaid-graphlib.js';
|
|
||||||
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
|
||||||
import { insertNode, positionNode, clear as clearNodes, setNodeElem } from './nodes.js';
|
|
||||||
import { insertCluster, clear as clearClusters } from './clusters.js';
|
|
||||||
import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges.js';
|
|
||||||
import { log } from '../logger.js';
|
|
||||||
import { getSubGraphTitleMargins } from '../utils/subGraphTitleMargins.js';
|
|
||||||
import { getConfig } from '../diagram-api/diagramAPI.js';
|
|
||||||
// import type { LayoutData, LayoutMethod } from '../rendering-util/types.js';
|
|
||||||
// import type { MermaidConfig } from '../config.type.js';
|
|
||||||
|
|
||||||
const recursiveRender = async (_elem, graph, diagramtype, id, parentCluster, siteConfig) => {
|
|
||||||
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
|
|
||||||
const dir = graph.graph().rankdir;
|
|
||||||
log.trace('Dir in recursive render - dir:', dir);
|
|
||||||
|
|
||||||
const elem = _elem.insert('g').attr('class', 'root');
|
|
||||||
if (!graph.nodes()) {
|
|
||||||
log.info('No nodes found for', graph);
|
|
||||||
} else {
|
|
||||||
log.info('Recursive render XXX', graph.nodes());
|
|
||||||
}
|
|
||||||
if (graph.edges().length > 0) {
|
|
||||||
log.trace('Recursive edges', graph.edge(graph.edges()[0]));
|
|
||||||
}
|
|
||||||
const clusters = elem.insert('g').attr('class', 'clusters');
|
|
||||||
const edgePaths = elem.insert('g').attr('class', 'edgePaths');
|
|
||||||
const edgeLabels = elem.insert('g').attr('class', 'edgeLabels');
|
|
||||||
const nodes = elem.insert('g').attr('class', 'nodes');
|
|
||||||
|
|
||||||
// Insert nodes, this will insert them into the dom and each node will get a size. The size is updated
|
|
||||||
// to the abstract node and is later used by dagre for the layout
|
|
||||||
await Promise.all(
|
|
||||||
graph.nodes().map(async function (v) {
|
|
||||||
const node = graph.node(v);
|
|
||||||
if (parentCluster !== undefined) {
|
|
||||||
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
|
|
||||||
// data.clusterPositioning = true;
|
|
||||||
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
|
|
||||||
graph.setNode(parentCluster.id, data);
|
|
||||||
if (!graph.parent(v)) {
|
|
||||||
log.trace('Setting parent', v, parentCluster.id);
|
|
||||||
graph.setParent(v, parentCluster.id, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
|
|
||||||
if (node && node.clusterNode) {
|
|
||||||
// const children = graph.children(v);
|
|
||||||
log.info('Cluster identified', v, node.width, graph.node(v));
|
|
||||||
const o = await recursiveRender(
|
|
||||||
nodes,
|
|
||||||
node.graph,
|
|
||||||
diagramtype,
|
|
||||||
id,
|
|
||||||
graph.node(v),
|
|
||||||
siteConfig
|
|
||||||
);
|
|
||||||
const newEl = o.elem;
|
|
||||||
updateNodeBounds(node, newEl);
|
|
||||||
node.diff = o.diff || 0;
|
|
||||||
log.info('Node bounds (abc123)', v, node, node.width, node.x, node.y);
|
|
||||||
setNodeElem(newEl, node);
|
|
||||||
|
|
||||||
log.warn('Recursive render complete ', newEl, node);
|
|
||||||
} else {
|
|
||||||
if (graph.children(v).length > 0) {
|
|
||||||
// This is a cluster but not to be rendered recursively
|
|
||||||
// Render as before
|
|
||||||
log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
|
|
||||||
log.info(findNonClusterChild(node.id, graph));
|
|
||||||
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
|
|
||||||
// insertCluster(clusters, graph.node(v));
|
|
||||||
} else {
|
|
||||||
log.info('Node - the non recursive path', v, node.id, node);
|
|
||||||
await insertNode(nodes, graph.node(v), dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Insert labels, this will insert them into the dom so that the width can be calculated
|
|
||||||
// Also figure out which edges point to/from clusters and adjust them accordingly
|
|
||||||
// Edges from/to clusters really points to the first child in the cluster.
|
|
||||||
// TODO: pick optimal child in the cluster to us as link anchor
|
|
||||||
graph.edges().forEach(function (e) {
|
|
||||||
const edge = graph.edge(e.v, e.w, e.name);
|
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
|
||||||
|
|
||||||
// Check if link is either from or to a cluster
|
|
||||||
log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
|
|
||||||
insertEdgeLabel(edgeLabels, edge);
|
|
||||||
});
|
|
||||||
|
|
||||||
graph.edges().forEach(function (e) {
|
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
|
||||||
});
|
|
||||||
log.info('#############################################');
|
|
||||||
log.info('### Layout ###');
|
|
||||||
log.info('#############################################');
|
|
||||||
log.info(graph);
|
|
||||||
dagreLayout(graph);
|
|
||||||
log.info('Graph after layout:', graphlibJson.write(graph));
|
|
||||||
// Move the nodes to the correct place
|
|
||||||
let diff = 0;
|
|
||||||
const { subGraphTitleTotalMargin } = getSubGraphTitleMargins(siteConfig);
|
|
||||||
sortNodesByHierarchy(graph).forEach(function (v) {
|
|
||||||
const node = graph.node(v);
|
|
||||||
log.info('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
|
|
||||||
log.info(
|
|
||||||
'Position ' + v + ': (' + node.x,
|
|
||||||
',' + node.y,
|
|
||||||
') width: ',
|
|
||||||
node.width,
|
|
||||||
' height: ',
|
|
||||||
node.height
|
|
||||||
);
|
|
||||||
if (node && node.clusterNode) {
|
|
||||||
// clusterDb[node.id].node = node;
|
|
||||||
node.y += subGraphTitleTotalMargin;
|
|
||||||
positionNode(node);
|
|
||||||
} else {
|
|
||||||
// Non cluster node
|
|
||||||
if (graph.children(v).length > 0) {
|
|
||||||
// A cluster in the non-recursive way
|
|
||||||
// positionCluster(node);
|
|
||||||
node.height += subGraphTitleTotalMargin;
|
|
||||||
insertCluster(clusters, node);
|
|
||||||
clusterDb[node.id].node = node;
|
|
||||||
} else {
|
|
||||||
node.y += subGraphTitleTotalMargin / 2;
|
|
||||||
positionNode(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Move the edge labels to the correct place after layout
|
|
||||||
graph.edges().forEach(function (e) {
|
|
||||||
const edge = graph.edge(e);
|
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
|
||||||
|
|
||||||
edge.points.forEach((point) => (point.y += subGraphTitleTotalMargin / 2));
|
|
||||||
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph, id);
|
|
||||||
positionEdgeLabel(edge, paths);
|
|
||||||
});
|
|
||||||
|
|
||||||
graph.nodes().forEach(function (v) {
|
|
||||||
const n = graph.node(v);
|
|
||||||
log.info(v, n.type, n.diff);
|
|
||||||
if (n.type === 'group') {
|
|
||||||
diff = n.diff;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { elem, diff };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const render = async (data4Layout, svg, element) => {
|
|
||||||
// Create the input mermaid.graph
|
|
||||||
const graph = new graphlib.Graph({
|
|
||||||
multigraph: true,
|
|
||||||
compound: true,
|
|
||||||
})
|
|
||||||
.setGraph({
|
|
||||||
rankdir: data4Layout.direction,
|
|
||||||
nodesep: data4Layout.nodeSpacing,
|
|
||||||
ranksep: data4Layout.rankSpacing,
|
|
||||||
marginx: 8,
|
|
||||||
marginy: 8,
|
|
||||||
})
|
|
||||||
.setDefaultEdgeLabel(function () {
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Org
|
|
||||||
|
|
||||||
insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId);
|
|
||||||
clearNodes();
|
|
||||||
clearEdges();
|
|
||||||
clearClusters();
|
|
||||||
clearGraphlib();
|
|
||||||
|
|
||||||
// Add the nodes and edges to the graph
|
|
||||||
data4Layout.nodes.forEach((node) => {
|
|
||||||
graph.setNode(node.id, { ...node });
|
|
||||||
});
|
|
||||||
|
|
||||||
log.warn('Graph at first:', JSON.stringify(graphlibJson.write(graph)));
|
|
||||||
adjustClustersAndEdges(graph);
|
|
||||||
log.warn('Graph after:', JSON.stringify(graphlibJson.write(graph)));
|
|
||||||
const siteConfig = getConfig();
|
|
||||||
await recursiveRender(
|
|
||||||
element,
|
|
||||||
graph,
|
|
||||||
data4Layout.type,
|
|
||||||
data4Layout.diagramId,
|
|
||||||
undefined,
|
|
||||||
siteConfig
|
|
||||||
);
|
|
||||||
};
|
|
@@ -15,7 +15,7 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
|
|||||||
classes = _classes;
|
classes = _classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('parentY', parent.node());
|
// console.log('parentY', parent.node());
|
||||||
|
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = parent
|
const shapeSvg = parent
|
||||||
@@ -34,9 +34,8 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
|
|||||||
labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];
|
labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const textNode = label.node();
|
const textNode = label.node();
|
||||||
console.log('parentX', parent, 'node',node,'labelText',labelText, textNode, node.labelType, 'label', label.node());
|
// console.log('parentX', parent, 'node',node,'labelText',labelText, textNode, node.labelType, 'label', label.node());
|
||||||
let text;
|
let text;
|
||||||
if (node.labelType === 'markdown') {
|
if (node.labelType === 'markdown') {
|
||||||
// text = textNode;
|
// text = textNode;
|
||||||
|
@@ -29,7 +29,7 @@ export const setConf = function (cnf) {
|
|||||||
*/
|
*/
|
||||||
export const addVertices = async function (vert, g, svgId, root, doc, diagObj) {
|
export const addVertices = async function (vert, g, svgId, root, doc, diagObj) {
|
||||||
const svg = root.select(`[id="${svgId}"]`);
|
const svg = root.select(`[id="${svgId}"]`);
|
||||||
console.log('SVG:', svg, svg.node(), 'root:', root, root.node());
|
// console.log('SVG:', svg, svg.node(), 'root:', root, root.node());
|
||||||
|
|
||||||
const keys = vert.keys();
|
const keys = vert.keys();
|
||||||
|
|
||||||
|
@@ -1,41 +1,37 @@
|
|||||||
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import common from '../common/common.js';
|
import common from '../common/common.js';
|
||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_DIAGRAM_DIRECTION,
|
CSS_DIAGRAM_CLUSTER,
|
||||||
STMT_STATE,
|
CSS_DIAGRAM_CLUSTER_ALT,
|
||||||
STMT_RELATION,
|
CSS_DIAGRAM_NOTE,
|
||||||
STMT_CLASSDEF,
|
CSS_DIAGRAM_STATE,
|
||||||
STMT_APPLYCLASS,
|
CSS_EDGE,
|
||||||
|
CSS_EDGE_NOTE_EDGE,
|
||||||
|
DEFAULT_NESTED_DOC_DIR,
|
||||||
DEFAULT_STATE_TYPE,
|
DEFAULT_STATE_TYPE,
|
||||||
DIVIDER_TYPE,
|
DIVIDER_TYPE,
|
||||||
G_EDGE_STYLE,
|
DOMID_STATE,
|
||||||
|
DOMID_TYPE_SPACER,
|
||||||
G_EDGE_ARROWHEADSTYLE,
|
G_EDGE_ARROWHEADSTYLE,
|
||||||
G_EDGE_LABELPOS,
|
G_EDGE_LABELPOS,
|
||||||
G_EDGE_LABELTYPE,
|
G_EDGE_LABELTYPE,
|
||||||
|
G_EDGE_STYLE,
|
||||||
G_EDGE_THICKNESS,
|
G_EDGE_THICKNESS,
|
||||||
CSS_EDGE,
|
NOTE,
|
||||||
DEFAULT_NESTED_DOC_DIR,
|
NOTE_ID,
|
||||||
|
PARENT,
|
||||||
|
PARENT_ID,
|
||||||
SHAPE_DIVIDER,
|
SHAPE_DIVIDER,
|
||||||
SHAPE_GROUP,
|
|
||||||
CSS_DIAGRAM_CLUSTER,
|
|
||||||
CSS_DIAGRAM_CLUSTER_ALT,
|
|
||||||
CSS_DIAGRAM_STATE,
|
|
||||||
SHAPE_STATE_WITH_DESC,
|
|
||||||
SHAPE_STATE,
|
|
||||||
SHAPE_START,
|
|
||||||
SHAPE_END,
|
SHAPE_END,
|
||||||
|
SHAPE_GROUP,
|
||||||
SHAPE_NOTE,
|
SHAPE_NOTE,
|
||||||
SHAPE_NOTEGROUP,
|
SHAPE_NOTEGROUP,
|
||||||
CSS_DIAGRAM_NOTE,
|
SHAPE_START,
|
||||||
DOMID_TYPE_SPACER,
|
SHAPE_STATE,
|
||||||
DOMID_STATE,
|
SHAPE_STATE_WITH_DESC,
|
||||||
NOTE_ID,
|
STMT_RELATION,
|
||||||
PARENT_ID,
|
STMT_STATE,
|
||||||
NOTE,
|
|
||||||
PARENT,
|
|
||||||
CSS_EDGE_NOTE_EDGE,
|
|
||||||
} from './stateCommon.js';
|
} from './stateCommon.js';
|
||||||
|
|
||||||
// List of nodes created from the parsed diagram statement items
|
// List of nodes created from the parsed diagram statement items
|
||||||
|
@@ -1,58 +0,0 @@
|
|||||||
import { log } from '../../logger.js';
|
|
||||||
import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
|
|
||||||
import type { LayoutData, LayoutMethod } from '../../rendering-util/types.js';
|
|
||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
|
||||||
import doLayout from '../../rendering-util/doLayout.js';
|
|
||||||
import performRender from '../../rendering-util/performRender.js';
|
|
||||||
import insertElementsForSize, {
|
|
||||||
getDiagramElements,
|
|
||||||
} from '../../rendering-util/inserElementsForSize.js';
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
const conf: Record<string, any> = {};
|
|
||||||
|
|
||||||
export const setConf = function (cnf: Record<string, any>) {
|
|
||||||
const keys = Object.keys(cnf);
|
|
||||||
for (const key of keys) {
|
|
||||||
conf[key] = cnf[key];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getClasses = function (
|
|
||||||
text: string,
|
|
||||||
diagramObj: any
|
|
||||||
): Record<string, DiagramStyleClassDef> {
|
|
||||||
diagramObj.db.extract(diagramObj.db.getRootDocV2());
|
|
||||||
return diagramObj.db.getClasses();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const draw = async function (text: string, id: string, _version: string, diag: any) {
|
|
||||||
log.info('Drawing state diagram (v2)', id);
|
|
||||||
const { securityLevel, state: conf } = getConfig();
|
|
||||||
|
|
||||||
// Extracting the data from the parsed structure into a more usable form
|
|
||||||
// Not related to the refactoring, but this is the first step in the rendering process
|
|
||||||
diag.db.extract(diag.db.getRootDocV2());
|
|
||||||
|
|
||||||
// The getData method provided in all supported diagrams is used to extract the data from the parsed structure
|
|
||||||
// into the Layout data format
|
|
||||||
const data4Layout = diag.db.getData() as LayoutData;
|
|
||||||
// Create the root SVG
|
|
||||||
const { svg, element } = getDiagramElements(id, securityLevel);
|
|
||||||
// For some diagrams this call is not needed, but in the state diagram it is
|
|
||||||
await insertElementsForSize(element, data4Layout);
|
|
||||||
|
|
||||||
console.log('data4Layout:', data4Layout);
|
|
||||||
|
|
||||||
// Now we have layout data with real sizes, we can perform the layout
|
|
||||||
const data4Rendering = doLayout(data4Layout, id, _version, 'dagre-wrapper');
|
|
||||||
|
|
||||||
// The performRender method provided in all supported diagrams is used to render the data
|
|
||||||
performRender(data4Rendering);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
setConf,
|
|
||||||
getClasses,
|
|
||||||
draw,
|
|
||||||
};
|
|
@@ -1,42 +1,20 @@
|
|||||||
import { log } from '../../logger.js';
|
|
||||||
import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
|
|
||||||
import type { LayoutData, LayoutMethod } from '../../rendering-util/types.js';
|
|
||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
import doLayout from '../../rendering-util/doLayout.js';
|
import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
|
||||||
import performRender from '../../rendering-util/performRender.js';
|
import { log } from '../../logger.js';
|
||||||
|
import { getDiagramElements } from '../../rendering-util/insertElementsForSize.js';
|
||||||
import { render } from '../../rendering-util/render.js';
|
import { render } from '../../rendering-util/render.js';
|
||||||
import insertElementsForSize, {
|
|
||||||
getDiagramElements,
|
|
||||||
} from '../../rendering-util/inserElementsForSize.js';
|
|
||||||
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
||||||
import {
|
import type { LayoutData } from '../../rendering-util/types.js';
|
||||||
DEFAULT_DIAGRAM_DIRECTION,
|
|
||||||
DEFAULT_NESTED_DOC_DIR,
|
|
||||||
STMT_STATE,
|
|
||||||
STMT_RELATION,
|
|
||||||
DEFAULT_STATE_TYPE,
|
|
||||||
DIVIDER_TYPE,
|
|
||||||
CSS_DIAGRAM,
|
|
||||||
} from './stateCommon.js';
|
|
||||||
import utils from '../../utils.js';
|
import utils from '../../utils.js';
|
||||||
|
import { CSS_DIAGRAM, DEFAULT_NESTED_DOC_DIR } from './stateCommon.js';
|
||||||
// Configuration
|
|
||||||
const conf: Record<string, any> = {};
|
|
||||||
|
|
||||||
export const setConf = function (cnf: Record<string, any>) {
|
|
||||||
const keys = Object.keys(cnf);
|
|
||||||
for (const key of keys) {
|
|
||||||
conf[key] = cnf[key];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the direction from the statement items.
|
* Get the direction from the statement items.
|
||||||
* Look through all of the documents (docs) in the parsedItems
|
* Look through all of the documents (docs) in the parsedItems
|
||||||
* Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction.
|
* Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction.
|
||||||
* @param {object[]} parsedItem - the parsed statement item to look through
|
* @param parsedItem - the parsed statement item to look through
|
||||||
* @param [defaultDir] - the direction to use if none is found
|
* @param defaultDir - the direction to use if none is found
|
||||||
* @returns {string}
|
* @returns The direction to use
|
||||||
*/
|
*/
|
||||||
const getDir = (parsedItem: any, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
|
const getDir = (parsedItem: any, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
|
||||||
let dir = defaultDir;
|
let dir = defaultDir;
|
||||||
@@ -54,7 +32,7 @@ const getDir = (parsedItem: any, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
|
|||||||
export const getClasses = function (
|
export const getClasses = function (
|
||||||
text: string,
|
text: string,
|
||||||
diagramObj: any
|
diagramObj: any
|
||||||
): Record<string, DiagramStyleClassDef> {
|
): Map<string, DiagramStyleClassDef> {
|
||||||
diagramObj.db.extract(diagramObj.db.getRootDocV2());
|
diagramObj.db.extract(diagramObj.db.getRootDocV2());
|
||||||
return diagramObj.db.getClasses();
|
return diagramObj.db.getClasses();
|
||||||
};
|
};
|
||||||
@@ -87,28 +65,29 @@ export const draw = async function (text: string, id: string, _version: string,
|
|||||||
// performRender(data4Rendering);
|
// performRender(data4Rendering);
|
||||||
|
|
||||||
data4Layout.type = diag.type;
|
data4Layout.type = diag.type;
|
||||||
// data4Layout.layoutAlgorithm = 'dagre-wrapper';
|
|
||||||
// data4Layout.layoutAlgorithm = 'elk';
|
|
||||||
data4Layout.layoutAlgorithm = layout;
|
data4Layout.layoutAlgorithm = layout;
|
||||||
data4Layout.direction = DIR;
|
data4Layout.direction = DIR;
|
||||||
data4Layout.nodeSpacing = conf.nodeSpacing || 50;
|
|
||||||
data4Layout.rankSpacing = conf.rankSpacing || 50;
|
// TODO: Should we move these two to baseConfig? These types are not there in StateConfig.
|
||||||
|
// @ts-expect-error TODO: Will be fixed after config refactor
|
||||||
|
data4Layout.nodeSpacing = conf?.nodeSpacing || 50;
|
||||||
|
// @ts-expect-error TODO: Will be fixed after config refactor
|
||||||
|
data4Layout.rankSpacing = conf?.rankSpacing || 50;
|
||||||
data4Layout.markers = ['barb'];
|
data4Layout.markers = ['barb'];
|
||||||
data4Layout.diagramId = id;
|
data4Layout.diagramId = id;
|
||||||
console.log('REF1:', data4Layout);
|
// console.log('REF1:', data4Layout);
|
||||||
await render(data4Layout, svg, element);
|
await render(data4Layout, svg, element);
|
||||||
const padding = 8;
|
const padding = 8;
|
||||||
utils.insertTitle(
|
utils.insertTitle(
|
||||||
element,
|
element,
|
||||||
'statediagramTitleText',
|
'statediagramTitleText',
|
||||||
conf.titleTopMargin,
|
conf?.titleTopMargin ?? 25,
|
||||||
diag.db.getDiagramTitle()
|
diag.db.getDiagramTitle()
|
||||||
);
|
);
|
||||||
setupViewPortForSVG(svg, padding, CSS_DIAGRAM, conf.useMaxWidth);
|
setupViewPortForSVG(svg, padding, CSS_DIAGRAM, conf?.useMaxWidth ?? true);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
|
||||||
getClasses,
|
getClasses,
|
||||||
draw,
|
draw,
|
||||||
};
|
};
|
||||||
|
@@ -23,6 +23,11 @@ export const getDiagramElements = (id, securityLevel) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// export function insertElementsForSize(el: SVGElement, data: LayoutData): void {
|
// export function insertElementsForSize(el: SVGElement, data: LayoutData): void {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param el
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
export function insertElementsForSize(el, data) {
|
export function insertElementsForSize(el, data) {
|
||||||
const nodesElem = el.insert('g').attr('class', 'nodes');
|
const nodesElem = el.insert('g').attr('class', 'nodes');
|
||||||
const edgesElem = el.insert('g').attr('class', 'edges');
|
const edgesElem = el.insert('g').attr('class', 'edges');
|
@@ -278,6 +278,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
clusterDb[e.w]
|
clusterDb[e.w]
|
||||||
);
|
);
|
||||||
if (clusterDb[e.v] && clusterDb[e.w] && clusterDb[e.v] === clusterDb[e.w]) {
|
if (clusterDb[e.v] && clusterDb[e.w] && clusterDb[e.v] === clusterDb[e.w]) {
|
||||||
|
// cspell:ignore trixing
|
||||||
log.warn('Fixing and trixing link to self - removing XXX', e.v, e.w, e.name);
|
log.warn('Fixing and trixing link to self - removing XXX', e.v, e.w, e.name);
|
||||||
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
||||||
v = getAnchorId(e.v);
|
v = getAnchorId(e.v);
|
||||||
|
@@ -6,7 +6,7 @@ import {
|
|||||||
extractDescendants,
|
extractDescendants,
|
||||||
sortNodesByHierarchy,
|
sortNodesByHierarchy,
|
||||||
} from './mermaid-graphlib.js';
|
} from './mermaid-graphlib.js';
|
||||||
import { setLogLevel, log } from '../logger.js';
|
import { setLogLevel, log } from '$root/logger.js';
|
||||||
|
|
||||||
describe('Graphlib decorations', () => {
|
describe('Graphlib decorations', () => {
|
||||||
let g;
|
let g;
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
import { RenderData } from './types';
|
|
||||||
const performRender = (data: RenderData) => { };
|
|
||||||
export default performRender;
|
|
@@ -1,25 +1,14 @@
|
|||||||
import { log } from '$root/logger.js';
|
|
||||||
import createLabel from './createLabel.js';
|
|
||||||
import { createText } from '$root/rendering-util/createText.ts';
|
|
||||||
import {
|
|
||||||
line,
|
|
||||||
curveBasis,
|
|
||||||
curveLinear,
|
|
||||||
curveNatural,
|
|
||||||
curveCardinal,
|
|
||||||
curveStep,
|
|
||||||
select,
|
|
||||||
curveMonotoneX,
|
|
||||||
curveMonotoneY,
|
|
||||||
curveCatmullRom,
|
|
||||||
} from 'd3';
|
|
||||||
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
||||||
import utils from '$root/utils.js';
|
|
||||||
import { evaluate } from '$root/diagrams/common/common.js';
|
import { evaluate } from '$root/diagrams/common/common.js';
|
||||||
|
import { log } from '$root/logger.js';
|
||||||
|
import { createText } from '$root/rendering-util/createText.ts';
|
||||||
|
import utils from '$root/utils.js';
|
||||||
import { getLineFunctionsWithOffset } from '$root/utils/lineWithOffset.js';
|
import { getLineFunctionsWithOffset } from '$root/utils/lineWithOffset.js';
|
||||||
import { getSubGraphTitleMargins } from '$root/utils/subGraphTitleMargins.js';
|
import { getSubGraphTitleMargins } from '$root/utils/subGraphTitleMargins.js';
|
||||||
import { addEdgeMarkers } from './edgeMarker.ts';
|
import { curveBasis, line, select } from 'd3';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
|
import createLabel from './createLabel.js';
|
||||||
|
import { addEdgeMarkers } from './edgeMarker.ts';
|
||||||
//import type { Edge } from '$root/rendering-util/types.d.ts';
|
//import type { Edge } from '$root/rendering-util/types.d.ts';
|
||||||
|
|
||||||
let edgeLabels = {};
|
let edgeLabels = {};
|
||||||
@@ -339,23 +328,23 @@ export const intersection = (node, outsidePoint, insidePoint) => {
|
|||||||
* and return an update path ending by the border of the node.
|
* and return an update path ending by the border of the node.
|
||||||
*
|
*
|
||||||
* @param {Array} _points
|
* @param {Array} _points
|
||||||
* @param {any} boundryNode
|
* @param {any} boundaryNode
|
||||||
* @returns {Array} Points
|
* @returns {Array} Points
|
||||||
*/
|
*/
|
||||||
const cutPathAtIntersect = (_points, boundryNode) => {
|
const cutPathAtIntersect = (_points, boundaryNode) => {
|
||||||
log.warn('abc88 cutPathAtIntersect', _points, boundryNode);
|
log.warn('abc88 cutPathAtIntersect', _points, boundaryNode);
|
||||||
let points = [];
|
let points = [];
|
||||||
let lastPointOutside = _points[0];
|
let lastPointOutside = _points[0];
|
||||||
let isInside = false;
|
let isInside = false;
|
||||||
_points.forEach((point) => {
|
_points.forEach((point) => {
|
||||||
// const node = clusterDb[edge.toCluster].node;
|
// const node = clusterDb[edge.toCluster].node;
|
||||||
log.info('abc88 checking point', point, boundryNode);
|
log.info('abc88 checking point', point, boundaryNode);
|
||||||
|
|
||||||
// check if point is inside the boundary rect
|
// check if point is inside the boundary rect
|
||||||
if (!outsideNode(boundryNode, point) && !isInside) {
|
if (!outsideNode(boundaryNode, point) && !isInside) {
|
||||||
// First point inside the rect found
|
// First point inside the rect found
|
||||||
// Calc the intersection coord between the point anf the last point outside the rect
|
// Calc the intersection coord between the point anf the last point outside the rect
|
||||||
const inter = intersection(boundryNode, lastPointOutside, point);
|
const inter = intersection(boundaryNode, lastPointOutside, point);
|
||||||
log.warn('abc88 inside', point, lastPointOutside, inter);
|
log.warn('abc88 inside', point, lastPointOutside, inter);
|
||||||
log.warn('abc88 intersection', inter);
|
log.warn('abc88 intersection', inter);
|
||||||
|
|
||||||
@@ -386,26 +375,6 @@ const cutPathAtIntersect = (_points, boundryNode) => {
|
|||||||
return points;
|
return points;
|
||||||
};
|
};
|
||||||
|
|
||||||
const calcOffset = function (src, dest, parentLookupDb) {
|
|
||||||
const ancestor = findCommonAncestor(src, dest, parentLookupDb);
|
|
||||||
if (ancestor === undefined || ancestor === 'root') {
|
|
||||||
return { x: 0, y: 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
const ancestorOffset = nodeDb[ancestor].offset;
|
|
||||||
return { x: ancestorOffset.posX, y: ancestorOffset.posY };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to insert a midpoint
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param p1
|
|
||||||
* @param p2
|
|
||||||
*/
|
|
||||||
function insertMidpoint(p1, p2) {
|
|
||||||
return [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an edge, this function will return the corner points of the edge. This is defined as:
|
* Given an edge, this function will return the corner points of the edge. This is defined as:
|
||||||
* one point that has a previous point and a next point such as the angle between the previous
|
* one point that has a previous point and a next point such as the angle between the previous
|
||||||
|
@@ -23,6 +23,7 @@ export const choice = (parent: SVG, node: Node) => {
|
|||||||
|
|
||||||
let choice;
|
let choice;
|
||||||
if (node.useRough) {
|
if (node.useRough) {
|
||||||
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const pointArr = points.map(function (d) {
|
const pointArr = points.map(function (d) {
|
||||||
return [d.x, d.y];
|
return [d.x, d.y];
|
||||||
@@ -41,6 +42,7 @@ export const choice = (parent: SVG, node: Node) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// center the circle around its coordinate
|
// center the circle around its coordinate
|
||||||
|
// @ts-ignore TODO: Fix rough typings
|
||||||
choice.attr('class', 'state-start').attr('r', 7).attr('width', 28).attr('height', 28);
|
choice.attr('class', 'state-start').attr('r', 7).attr('width', 28).attr('height', 28);
|
||||||
node.width = 28;
|
node.width = 28;
|
||||||
node.height = 28;
|
node.height = 28;
|
||||||
|
@@ -7,56 +7,49 @@ import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
|||||||
import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
|
import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
|
|
||||||
/**
|
// function applyNodePropertyBorders(
|
||||||
*
|
// rect: d3.Selection<SVGRectElement, unknown, null, undefined>,
|
||||||
* @param rect
|
// borders: string | undefined,
|
||||||
* @param borders
|
// totalWidth: number,
|
||||||
* @param totalWidth
|
// totalHeight: number
|
||||||
* @param totalHeight
|
// ) {
|
||||||
*/
|
// if (!borders) {
|
||||||
function applyNodePropertyBorders(
|
// return;
|
||||||
rect: d3.Selection<SVGRectElement, unknown, null, undefined>,
|
// }
|
||||||
borders: string | undefined,
|
// const strokeDashArray: number[] = [];
|
||||||
totalWidth: number,
|
// const addBorder = (length: number) => {
|
||||||
totalHeight: number
|
// strokeDashArray.push(length, 0);
|
||||||
) {
|
// };
|
||||||
if (!borders) {
|
// const skipBorder = (length: number) => {
|
||||||
return;
|
// strokeDashArray.push(0, length);
|
||||||
}
|
// };
|
||||||
const strokeDashArray: number[] = [];
|
// if (borders.includes('t')) {
|
||||||
const addBorder = (length: number) => {
|
// log.debug('add top border');
|
||||||
strokeDashArray.push(length, 0);
|
// addBorder(totalWidth);
|
||||||
};
|
// } else {
|
||||||
const skipBorder = (length: number) => {
|
// skipBorder(totalWidth);
|
||||||
strokeDashArray.push(0, length);
|
// }
|
||||||
};
|
// if (borders.includes('r')) {
|
||||||
if (borders.includes('t')) {
|
// log.debug('add right border');
|
||||||
log.debug('add top border');
|
// addBorder(totalHeight);
|
||||||
addBorder(totalWidth);
|
// } else {
|
||||||
} else {
|
// skipBorder(totalHeight);
|
||||||
skipBorder(totalWidth);
|
// }
|
||||||
}
|
// if (borders.includes('b')) {
|
||||||
if (borders.includes('r')) {
|
// log.debug('add bottom border');
|
||||||
log.debug('add right border');
|
// addBorder(totalWidth);
|
||||||
addBorder(totalHeight);
|
// } else {
|
||||||
} else {
|
// skipBorder(totalWidth);
|
||||||
skipBorder(totalHeight);
|
// }
|
||||||
}
|
// if (borders.includes('l')) {
|
||||||
if (borders.includes('b')) {
|
// log.debug('add left border');
|
||||||
log.debug('add bottom border');
|
// addBorder(totalHeight);
|
||||||
addBorder(totalWidth);
|
// } else {
|
||||||
} else {
|
// skipBorder(totalHeight);
|
||||||
skipBorder(totalWidth);
|
// }
|
||||||
}
|
|
||||||
if (borders.includes('l')) {
|
|
||||||
log.debug('add left border');
|
|
||||||
addBorder(totalHeight);
|
|
||||||
} else {
|
|
||||||
skipBorder(totalHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
rect.attr('stroke-dasharray', strokeDashArray.join(' '));
|
// rect.attr('stroke-dasharray', strokeDashArray.join(' '));
|
||||||
}
|
// }
|
||||||
|
|
||||||
export const drawRect = async (parent: SVGAElement, node: Node, options: RectOptions) => {
|
export const drawRect = async (parent: SVGAElement, node: Node, options: RectOptions) => {
|
||||||
const { themeVariables, handdrawnSeed } = getConfig();
|
const { themeVariables, handdrawnSeed } = getConfig();
|
||||||
@@ -77,6 +70,7 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt
|
|||||||
let rect;
|
let rect;
|
||||||
const { rx, ry, cssStyles, useRough } = node;
|
const { rx, ry, cssStyles, useRough } = node;
|
||||||
if (useRough) {
|
if (useRough) {
|
||||||
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, {
|
const options = userNodeOverrides(node, {
|
||||||
roughness: 0.7,
|
roughness: 0.7,
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { log } from '$root/logger.js';
|
|
||||||
import { updateNodeBounds } from './util.js';
|
import { updateNodeBounds } from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '$root/rendering-util/types.d.ts';
|
import type { Node } from '$root/rendering-util/types.d.ts';
|
||||||
@@ -27,6 +26,7 @@ export const forkJoin = (parent: SVG, node: Node, dir: string) => {
|
|||||||
|
|
||||||
let shape;
|
let shape;
|
||||||
if (node.useRough) {
|
if (node.useRough) {
|
||||||
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const roughNode = rc.rectangle(x, y, width, height, solidStateFill(lineColor));
|
const roughNode = rc.rectangle(x, y, width, height, solidStateFill(lineColor));
|
||||||
shape = shapeSvg.insert(() => roughNode);
|
shape = shapeSvg.insert(() => roughNode);
|
||||||
|
@@ -30,6 +30,7 @@ export const note = async (parent: SVGAElement, node: Node) => {
|
|||||||
|
|
||||||
if (useRough) {
|
if (useRough) {
|
||||||
// add the rect
|
// add the rect
|
||||||
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const roughNode = rc.rectangle(x, y, totalWidth, totalHeight, {
|
const roughNode = rc.rectangle(x, y, totalWidth, totalHeight, {
|
||||||
roughness: 0.7,
|
roughness: 0.7,
|
||||||
|
@@ -18,6 +18,7 @@ export const stateEnd = (parent: SVG, node: Node) => {
|
|||||||
let circle;
|
let circle;
|
||||||
let innerCircle;
|
let innerCircle;
|
||||||
if (node.useRough) {
|
if (node.useRough) {
|
||||||
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const roughNode = rc.circle(0, 0, 14, { ...solidStateFill(lineColor), roughness: 0.5 });
|
const roughNode = rc.circle(0, 0, 14, { ...solidStateFill(lineColor), roughness: 0.5 });
|
||||||
const roughInnerNode = rc.circle(0, 0, 5, { ...solidStateFill(lineColor), fillStyle: 'solid' });
|
const roughInnerNode = rc.circle(0, 0, 5, { ...solidStateFill(lineColor), fillStyle: 'solid' });
|
||||||
|
@@ -18,6 +18,7 @@ export const stateStart = (parent: SVG, node: Node) => {
|
|||||||
|
|
||||||
let circle;
|
let circle;
|
||||||
if (node.useRough) {
|
if (node.useRough) {
|
||||||
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const roughNode = rc.circle(0, 0, 14, solidStateFill(lineColor));
|
const roughNode = rc.circle(0, 0, 14, solidStateFill(lineColor));
|
||||||
circle = shapeSvg.insert(() => roughNode);
|
circle = shapeSvg.insert(() => roughNode);
|
||||||
@@ -26,6 +27,7 @@ export const stateStart = (parent: SVG, node: Node) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// center the circle around its coordinate
|
// center the circle around its coordinate
|
||||||
|
// @ts-ignore TODO: Fix typings
|
||||||
circle.attr('class', 'state-start').attr('r', 7).attr('width', 14).attr('height', 14);
|
circle.attr('class', 'state-start').attr('r', 7).attr('width', 14).attr('height', 14);
|
||||||
|
|
||||||
updateNodeBounds(node, circle);
|
updateNodeBounds(node, circle);
|
||||||
|
Reference in New Issue
Block a user