mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 14:29:25 +02:00
Lazy loading fix for example-diagram and mindmaps
This commit is contained in:
@@ -14,6 +14,8 @@ async function createServer() {
|
||||
|
||||
app.use(vite.middlewares);
|
||||
app.use(express.static('./packages/mermaid/dist'));
|
||||
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
||||
app.use(express.static('./packages/mermaid-mindmap/dist'));
|
||||
app.use(express.static('demos'));
|
||||
app.use(express.static('cypress/platform'));
|
||||
|
||||
|
@@ -38,7 +38,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<pre class="mermaid" style="width: 50%">
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart LR
|
||||
subgraph one
|
||||
direction LR
|
||||
@@ -364,8 +364,8 @@ flowchart TD
|
||||
</pre>
|
||||
|
||||
<!-- <script src="./mermaid.js"></script> -->
|
||||
<script src="./packages/mermaid-mindmap/dist/mermaid-mindmap.js"></script>
|
||||
<script src="./packages/mermaid/dist/mermaid.js"></script>
|
||||
<script src="./mermaid-mindmap-detector.js"></script>
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
|
@@ -45,20 +45,28 @@
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
<pre id="diagram" class="mermaid">
|
||||
info
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
mindmap
|
||||
root
|
||||
ch1
|
||||
ch2
|
||||
example-diagram
|
||||
</pre
|
||||
>
|
||||
<script src="./packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script>
|
||||
<script src="./packages/mermaid-mindmap/dist/mermaid-example-diagram-detector.js"></script>
|
||||
<script src="./packages/mermaid/dist/mermaid.js"></script>
|
||||
<pre id="diagram" class="mermaid">
|
||||
mindmap
|
||||
root
|
||||
child1((Circle))
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2(Round rectangle)
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3[Square]
|
||||
grandchild 5
|
||||
::icon(mdi mdi-fire)
|
||||
gc6((grand<br/>child 6))
|
||||
::icon(mdi mdi-fire)
|
||||
</pre>
|
||||
<script src="./mermaid-mindmap-detector.js"></script>
|
||||
<script src="./mermaid-example-diagram-detector.js"></script>
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
|
@@ -1,3 +1,10 @@
|
||||
/**
|
||||
* Detector function that will be called by mermaid to determine if the diagram is this type of digram.
|
||||
*
|
||||
* @param txt The diagram text will be passed to the detector
|
||||
* @returns True if the diagram text matches a diagram of this type
|
||||
*/
|
||||
|
||||
export const detector = (txt: string) => {
|
||||
return txt.match(/^\s*example-diagram/) !== null;
|
||||
};
|
||||
|
@@ -13,21 +13,29 @@ export const getMessage = () => {
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the info flag
|
||||
*
|
||||
* @param {boolean} inf
|
||||
*/
|
||||
export const setInfo = (inf) => {
|
||||
info = inf;
|
||||
};
|
||||
|
||||
/** @returns Returns the info flag */
|
||||
export const getInfo = () => {
|
||||
return info;
|
||||
};
|
||||
|
||||
export const clear = () => {
|
||||
message = '';
|
||||
info = false;
|
||||
};
|
||||
|
||||
export default {
|
||||
setMessage,
|
||||
getMessage,
|
||||
setInfo,
|
||||
getInfo,
|
||||
clear: () => {
|
||||
message = '';
|
||||
info = false;
|
||||
},
|
||||
clear,
|
||||
};
|
||||
|
@@ -8,7 +8,7 @@
|
||||
|
||||
%%
|
||||
|
||||
"info" return 'info' ;
|
||||
"example-diagram" return 'example-diagram' ;
|
||||
[\s\n\r]+ return 'NL' ;
|
||||
[\s]+ return 'space';
|
||||
"showInfo" return 'showInfo';
|
||||
@@ -22,8 +22,8 @@
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
// %{ : info document 'EOF' { return yy; } }
|
||||
: info document 'EOF' { return yy; }
|
||||
// %{ : example-diagram document 'EOF' { return yy; } }
|
||||
: example-diagram document 'EOF' { return yy; }
|
||||
;
|
||||
|
||||
document
|
||||
|
@@ -54,8 +54,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.0",
|
||||
"cytoscape": "^3.23.0",
|
||||
"d3": "^7.0.0",
|
||||
"mermaid": "workspace:*",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@@ -3,6 +3,7 @@ import { select } from 'd3';
|
||||
import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
|
||||
import svgDraw from './svgDraw';
|
||||
import { BoundingBox, Layout } from 'non-layered-tidy-tree-layout';
|
||||
import cytoscape from 'cytoscape';
|
||||
import clone from 'fast-clone';
|
||||
import * as db from './mindmapDb';
|
||||
|
||||
|
269
packages/mermaid-mindmap/src/mindmapRendererTidyTree.js
Normal file
269
packages/mermaid-mindmap/src/mindmapRendererTidyTree.js
Normal file
@@ -0,0 +1,269 @@
|
||||
/** Created by knut on 14-12-11. */
|
||||
import { select } from 'd3';
|
||||
import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
|
||||
import svgDraw from './svgDraw';
|
||||
import { BoundingBox, Layout } from 'non-layered-tidy-tree-layout';
|
||||
import clone from 'fast-clone';
|
||||
import * as db from './mindmapDb';
|
||||
|
||||
/**
|
||||
* @param {any} svg The svg element to draw the diagram onto
|
||||
* @param {object} mindmap The maindmap data and hierarchy
|
||||
* @param section
|
||||
* @param {object} conf The configuration object
|
||||
*/
|
||||
function drawNodes(svg, mindmap, section, conf) {
|
||||
svgDraw.drawNode(svg, mindmap, section, conf);
|
||||
if (mindmap.children) {
|
||||
mindmap.children.forEach((child, index) => {
|
||||
drawNodes(svg, child, section < 0 ? index : section, conf);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param edgesElem
|
||||
* @param mindmap
|
||||
* @param parent
|
||||
* @param depth
|
||||
* @param section
|
||||
* @param conf
|
||||
*/
|
||||
function drawEdges(edgesElem, mindmap, parent, depth, section, conf) {
|
||||
if (parent) {
|
||||
svgDraw.drawEdge(edgesElem, mindmap, parent, depth, section, conf);
|
||||
}
|
||||
if (mindmap.children) {
|
||||
mindmap.children.forEach((child, index) => {
|
||||
drawEdges(edgesElem, child, mindmap, depth + 1, section < 0 ? index : section, conf);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mindmap
|
||||
* @param callback
|
||||
*/
|
||||
function eachNode(mindmap, callback) {
|
||||
callback(mindmap);
|
||||
if (mindmap.children) {
|
||||
mindmap.children.forEach((child) => {
|
||||
eachNode(child, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
/** @param {object} mindmap */
|
||||
function transpose(mindmap) {
|
||||
eachNode(mindmap, (node) => {
|
||||
const orgWidth = node.width;
|
||||
const orgX = node.x;
|
||||
node.width = node.height;
|
||||
node.height = orgWidth;
|
||||
node.x = node.y;
|
||||
node.y = orgX;
|
||||
});
|
||||
return mindmap;
|
||||
}
|
||||
/** @param {object} mindmap */
|
||||
function bottomToUp(mindmap) {
|
||||
log.debug('bottomToUp', mindmap);
|
||||
eachNode(mindmap.result, (node) => {
|
||||
// node.y = node.y - (node.y - bb.top) * 2 - node.height;
|
||||
node.y = node.y - (node.y - 0) * 2 - node.height;
|
||||
});
|
||||
return mindmap;
|
||||
}
|
||||
/** @param {object} mindmap The mindmap hierarchy */
|
||||
function rightToLeft(mindmap) {
|
||||
eachNode(mindmap.result, (node) => {
|
||||
// node.y = node.y - (node.y - bb.top) * 2 - node.height;
|
||||
node.x = node.x - (node.x - 0) * 2 - node.width;
|
||||
});
|
||||
return mindmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mindmap
|
||||
* @param dir
|
||||
*/
|
||||
function layout(mindmap, dir) {
|
||||
const bb = new BoundingBox(30, 60);
|
||||
|
||||
const layout = new Layout(bb);
|
||||
switch (dir) {
|
||||
case 'TB':
|
||||
return layout.layout(mindmap);
|
||||
case 'BT':
|
||||
return bottomToUp(layout.layout(mindmap));
|
||||
case 'RL': {
|
||||
transpose(mindmap);
|
||||
let newRes = layout.layout(mindmap);
|
||||
transpose(newRes.result);
|
||||
return rightToLeft(newRes);
|
||||
}
|
||||
case 'LR': {
|
||||
transpose(mindmap);
|
||||
let newRes = layout.layout(mindmap);
|
||||
transpose(newRes.result);
|
||||
return newRes;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
const dirFromIndex = (index) => {
|
||||
const dirNum = (index + 2) % 4;
|
||||
switch (dirNum) {
|
||||
case 0:
|
||||
return 'LR';
|
||||
case 1:
|
||||
return 'RL';
|
||||
case 2:
|
||||
return 'TB';
|
||||
case 3:
|
||||
return 'BT';
|
||||
default:
|
||||
return 'TB';
|
||||
}
|
||||
};
|
||||
|
||||
const mergeTrees = (node, trees) => {
|
||||
node.x = trees[0].result.x;
|
||||
node.y = trees[0].result.y;
|
||||
trees.forEach((tree) => {
|
||||
tree.result.children.forEach((child) => {
|
||||
const dx = node.x - tree.result.x;
|
||||
const dy = node.y - tree.result.y;
|
||||
eachNode(child, (childNode) => {
|
||||
const orgNode = db.getNodeById(childNode.id);
|
||||
if (orgNode) {
|
||||
orgNode.x = childNode.x + dx;
|
||||
orgNode.y = childNode.y + dy;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param node
|
||||
* @param conf
|
||||
*/
|
||||
function layoutMindmap(node, conf) {
|
||||
// BoundingBox(gap, bottomPadding)
|
||||
// const bb = new BoundingBox(10, 10);
|
||||
// const layout = new Layout(bb);
|
||||
// // const layout = new HorizontalLayout(bb);
|
||||
if (node.children.length === 0) {
|
||||
return node;
|
||||
}
|
||||
const trees = [];
|
||||
// node.children.forEach((child, index) => {
|
||||
// const tree = clone(node);
|
||||
// tree.children = [tree.children[index]];
|
||||
// trees.push(layout(tree, dirFromIndex(index), conf));
|
||||
// });
|
||||
|
||||
let cnt = 0;
|
||||
// For each direction, create a new tree with the same root, and add a ubset of the children to it.
|
||||
for (let i = 0; i < 4; i++) {
|
||||
// Calculate the number of the children of the root node that will be used in this direction
|
||||
const numChildren =
|
||||
Math.floor(node.children.length / 4) + (node.children.length % 4 > i ? 1 : 0);
|
||||
// Copy the original root node
|
||||
const tree = clone(node);
|
||||
// Setup the new copy with the children to be rendered in this direction
|
||||
tree.children = [];
|
||||
for (let j = 0; j < numChildren; j++) {
|
||||
tree.children.push(node.children[cnt]);
|
||||
cnt++;
|
||||
}
|
||||
if (tree.children.length > 0) {
|
||||
trees.push(layout(tree, dirFromIndex(i), conf));
|
||||
}
|
||||
}
|
||||
// Let each node know the direct of its tree for when we draw the branches.
|
||||
trees.forEach((tree, index) => {
|
||||
tree.result.direction = dirFromIndex(index);
|
||||
eachNode(tree.result, (node) => {
|
||||
node.direction = dirFromIndex(index);
|
||||
});
|
||||
});
|
||||
|
||||
// Merge the trees into a single tree
|
||||
mergeTrees(node, trees);
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* @param node
|
||||
* @param conf
|
||||
*/
|
||||
function positionNodes(node, conf) {
|
||||
svgDraw.positionNode(node, conf);
|
||||
if (node.children) {
|
||||
node.children.forEach((child) => {
|
||||
positionNodes(child, conf);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
*
|
||||
* @param {any} text
|
||||
* @param {any} id
|
||||
* @param {any} version
|
||||
* @param diagObj
|
||||
*/
|
||||
|
||||
export const draw = (text, id, version, diagObj) => {
|
||||
const conf = getConfig();
|
||||
|
||||
// This is done only for throwing the error if the text is not valid.
|
||||
diagObj.db.clear();
|
||||
// Parse the graph definition
|
||||
diagObj.parser.parse(text);
|
||||
|
||||
log.debug('Renering info diagram\n' + text);
|
||||
|
||||
const securityLevel = getConfig().securityLevel;
|
||||
// Handle root and Document for when rendering in sanbox mode
|
||||
let sandboxElement;
|
||||
if (securityLevel === 'sandbox') {
|
||||
sandboxElement = select('#i' + id);
|
||||
}
|
||||
const root =
|
||||
securityLevel === 'sandbox'
|
||||
? select(sandboxElement.nodes()[0].contentDocument.body)
|
||||
: select('body');
|
||||
// Parse the graph definition
|
||||
|
||||
const svg = root.select('#' + id);
|
||||
|
||||
svg.append('g');
|
||||
const mm = diagObj.db.getMindmap();
|
||||
|
||||
// Draw the graph and start with drawing the nodes without proper position
|
||||
// this gives us the size of the nodes and we can set the positions later
|
||||
|
||||
const edgesElem = svg.append('g');
|
||||
edgesElem.attr('class', 'mindmap-edges');
|
||||
const nodesElem = svg.append('g');
|
||||
nodesElem.attr('class', 'mindmap-nodes');
|
||||
drawNodes(nodesElem, mm, -1, conf);
|
||||
|
||||
// Next step is to layout the mindmap, giving each node a position
|
||||
|
||||
const positionedMindmap = layoutMindmap(mm, conf);
|
||||
|
||||
// After this we can draw, first the edges and the then nodes with the correct position
|
||||
drawEdges(edgesElem, positionedMindmap, null, 0, -1, conf);
|
||||
positionNodes(positionedMindmap, conf);
|
||||
|
||||
// Setup the view box and size of the svg element
|
||||
setupGraphViewbox(undefined, svg, conf.mindmap.padding, conf.mindmap.useMaxWidth);
|
||||
};
|
||||
|
||||
export default {
|
||||
draw,
|
||||
};
|
@@ -33,7 +33,7 @@ if (typeof document !== 'undefined') {
|
||||
detector: mindmapDetector,
|
||||
path: baseFolder,
|
||||
});
|
||||
// console.error(window.mermaid.detectors); // eslint-disable-line no-console
|
||||
console.error(window.mermaid.detectors); // eslint-disable-line no-console
|
||||
}
|
||||
},
|
||||
false
|
||||
|
@@ -105,6 +105,7 @@ const initThrowsErrors = function (
|
||||
mermaidAPI.updateSiteConfig({ startOnLoad: config?.startOnLoad });
|
||||
}
|
||||
|
||||
// generate the id of the diagram
|
||||
const idGenerator = new utils.initIdGenerator(conf.deterministicIds, conf.deterministicIDSeed);
|
||||
|
||||
let txt;
|
||||
@@ -222,7 +223,7 @@ const connectDiagram = (
|
||||
) => void
|
||||
) => {
|
||||
registerDiagram(id, diagram, callback);
|
||||
// Todo move this connect call to after the diagram is actually loaded
|
||||
// Todo move this connect call to after the diagram is actually loaded.
|
||||
callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox);
|
||||
};
|
||||
|
||||
|
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -275,14 +275,14 @@ importers:
|
||||
specifiers:
|
||||
'@braintree/sanitize-url': ^6.0.0
|
||||
concurrently: ^7.4.0
|
||||
cytoscape: ^3.23.0
|
||||
d3: ^7.0.0
|
||||
mermaid: workspace:*
|
||||
non-layered-tidy-tree-layout: ^2.0.2
|
||||
rimraf: ^3.0.2
|
||||
dependencies:
|
||||
'@braintree/sanitize-url': 6.0.0
|
||||
cytoscape: 3.23.0
|
||||
d3: 7.6.1
|
||||
mermaid: link:../mermaid
|
||||
non-layered-tidy-tree-layout: 2.0.2
|
||||
devDependencies:
|
||||
concurrently: 7.4.0
|
||||
@@ -4881,6 +4881,14 @@ packages:
|
||||
yauzl: 2.10.0
|
||||
dev: true
|
||||
|
||||
/cytoscape/3.23.0:
|
||||
resolution: {integrity: sha512-gRZqJj/1kiAVPkrVFvz/GccxsXhF3Qwpptl32gKKypO4IlqnKBjTOu+HbXtEggSGzC5KCaHp3/F7GgENrtsFkA==}
|
||||
engines: {node: '>=0.10'}
|
||||
dependencies:
|
||||
heap: 0.2.7
|
||||
lodash: 4.17.21
|
||||
dev: false
|
||||
|
||||
/d3-array/3.2.0:
|
||||
resolution: {integrity: sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -7251,6 +7259,10 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/heap/0.2.7:
|
||||
resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==}
|
||||
dev: false
|
||||
|
||||
/highlight.js/10.7.3:
|
||||
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
|
||||
dev: true
|
||||
|
Reference in New Issue
Block a user