Reputation: 471
I am using the mermaid library to build flowcharts. The principle of its work is that inside a block there is a pseudocode - commands of a special syntax, on the basis of which the flowchart is built in the block.
I want to be able to change the contents of the block dynamically, and the script rebuilds the block diagram every time.
How should I set up initialization? Perhaps I should add some callback function in the settings?
I initialized in this way:
mermaid.init({/*what setting parameters should be here?*/}, ".someClass"/*selector*/);
but the script doesn’t render any new commands. It only renders the commands that existed at the moment the document was loaded.
In other words, I want to edit a flowchart online.
function edit() {
const new_mermaid = document.createElement("div");
new_mermaid.classList.add("mermaid");
new_mermaid.classList.add(".someClass");
/*new_mermaid.innerHTML =
`graph TD
1[point 1] --> 2[point 2]`;*/
// it doesn't work when I append the new element dynamically!
new_mermaid.innerHTML = document.querySelector(".mermaid").innerHTML;
// it works always.
document.body.append(new_mermaid);
/* document.querySelector(".mermaid").innerHTML =
`
graph TD
A --> B`*/
// it doesn’t work with event listener
}
edit(); // it works
document.body.addEventListener("click", edit)
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>
// how to do it correctly?
mermaid.init({
noteMargin: 10
}, ".someClass");
</script>
<div class="mermaid someClass">
graph TD
1--> 2
3 --> 2
2 --> 1
</div>
Upvotes: 14
Views: 16056
Reputation: 45
Refer: https://mermaid.js.org/config/usage.html#api-usage
My minimal implementation:
<script>
VAR1 = 1;
VAR2 = 2;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.0/mermaid.min.js"></script>
<button onclick="mermaidDraw()">render</button>
<textarea>flowchart LR
A["`var1=${VAR1}`"] -->|"{VAR2}"| B("{VAR3}")</textarea>
<div class="mermaid"></div>
<pre id="err"></pre>
<script>
mermaid.initialize({
startOnLoad: true
});
eleM = document.querySelector('.mermaid');
eleE = document.querySelector('#err');
setTimeout(mermaidDraw, 200);
async function mermaidDraw() {
try {
graphDefinition = await mermaidEval('LocalFile.md');
const {
svg
} = await mermaid.render('graphDiv', graphDefinition);
eleM.innerHTML = svg;
} catch (err) {
if (err instanceof ReferenceError) {
varname = err.message.split(' ')[0];
window[varname] = varname;
setTimeout(mermaidDraw, 0);
}
console.error(err);
eleE.insertAdjacentHTML('beforeend', `🚫${err.message}\n`);
}
};
async function mermaidEval(url) {
//const response = await fetch(url);
//text = await response.text();
text = document.querySelector('textarea').value;
if (!text.match(/^[a-zA-Z]/)) {
// markdown ```mermaid, remove first and last line
text = text.split('\n').slice(1, -1).join('\n');
}
text = text.replace(/"`.*?`"/g, function(match) {
return eval(match.slice(1, -1));
});
text = text.replace(/"\{.*?\}"/g, function(match) {
return eval(match.slice(1, -1));
});
return text;
}
</script>
Use "`Text ${varName}`" or "{varName}"
in your mermaid raw content
Try this example in textarea
in Run code snippet:
flowchart LR
A["`var1=${VAR1}`"] -->|"{VAR2}"| B("{VAR1+VAR2}")
Upvotes: 1
Reputation: 311
Thanks for the answer above. I would like to add a react wrapper to the answer scope for whoever using react:
import React, {Component} from "react";
import mermaid from "mermaid";
export default class Mermaid extends Component {
constructor(props){
super(props)
this.state={
chart: this.props.chart || ""
}
mermaid.initialize({
mermaid : {
startOnLoad: false,
}
})
this.mermaidRef = React.createRef()
}
mermaidUpdate(){
var cb = function (svgGraph) {
this.mermaidRef.current.innerHTML = svgGraph
};
//console.log("this.state.chart", this.state.chart)
mermaid.mermaidAPI.render('id0', this.state.chart, cb.bind(this));
}
componentDidMount(){
this.mermaidUpdate()
}
componentDidUpdate(prevProps, prevState) {
//console.log("Mermiad prevProps.chart", prevProps.chart)
if (this.props.chart !== prevProps.chart) {
this.setState({chart:this.props.chart},()=>{
this.mermaidUpdate()
})
}
}
render() {
var outObj = (
<div
ref={this.mermaidRef}
className="mermaid"
>
{this.state.chart}
</div>
)
return outObj
}
}
Upvotes: 3
Reputation: 471
It seems, I know the answer. Look at the solution below:
document.querySelector("button").addEventListener("click", (e) => {
const output = document.querySelector(".flowchart");
if (output.firstChild !== null) {
output.innerHTML = "";
}
const code = document.querySelector(" textarea").value.trim();
let insert = function (code) {
output.innerHTML = code;
};
mermaid.render("preparedScheme", code, insert);
});
<script src="https://unpkg.com/[email protected]/dist/mermaid.min.js"></script>
<p>Input your data:</p>
<div class="input">
<textarea style="width:300px; height:200px"></textarea>
<br>
<button>render</button>
</div>
<div>
<p>output:</p>
<div class="render_container" style = "width:300px; height:200px; border:thin solid silver" >
<div class="flowchart"></div>
</div>
</div>
Upvotes: 15