mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-01 11:24:16 +01:00
feat: Make the examples interactive in the documentation site.
Ctrl/Cmd + Enter and a run button is added. The feature was first implemented in https://github.com/mermaid-js/mermaid/pull/5330. This is a simplified version without introducing additional dependencies. Co-authored-by: Anton <m@antonz.org>
This commit is contained in:
@@ -18,20 +18,7 @@ const MermaidExample = async (md: MarkdownRenderer) => {
|
|||||||
'Missing MarkdownIt highlight function (should be automatically created by vitepress'
|
'Missing MarkdownIt highlight function (should be automatically created by vitepress'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return '';
|
||||||
// doing ```mermaid-example {line-numbers=5 highlight=14-17} is not supported
|
|
||||||
const langAttrs = '';
|
|
||||||
return `
|
|
||||||
<h5>Code:</h5>
|
|
||||||
<div class="language-mermaid">
|
|
||||||
<button class="copy"></button>
|
|
||||||
<span class="lang">mermaid</span>
|
|
||||||
${
|
|
||||||
// html is pre-escaped by the highlight function
|
|
||||||
// (it also adds `v-pre` to ignore Vue template syntax)
|
|
||||||
md.options.highlight(token.content, 'mermaid', langAttrs)
|
|
||||||
}
|
|
||||||
</div>`;
|
|
||||||
} else if (token.info.trim() === 'mermaid') {
|
} else if (token.info.trim() === 'mermaid') {
|
||||||
const key = index;
|
const key = index;
|
||||||
return `
|
return `
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<h5>Code:</h5>
|
||||||
|
<div class="language-mermaid">
|
||||||
|
<button class="copy"></button>
|
||||||
|
<span class="lang">mermaid</span>
|
||||||
|
<pre><code contenteditable="true" @input="updateCode" @keydown.meta.enter="renderChart" ref="editableContent" class="editable-code"></code></pre>
|
||||||
|
<div class="buttons-container">
|
||||||
|
<span>{{ ctrlSymbol }} + Enter</span><span>|</span>
|
||||||
|
<button @click="renderChart">Run ►</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-html="svg"></div>
|
<div v-html="svg"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -18,15 +28,29 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const svg = ref('');
|
const svg = ref('');
|
||||||
|
const code = ref(decodeURIComponent(props.graph));
|
||||||
|
const ctrlSymbol = ref(navigator.platform.includes('Mac') ? '⌘' : 'Ctrl');
|
||||||
|
const editableContent = ref(null);
|
||||||
|
|
||||||
let mut = null;
|
let mut = null;
|
||||||
|
|
||||||
|
const updateCode = (event) => {
|
||||||
|
code.value = event.target.innerText;
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
mut = new MutationObserver(() => renderChart());
|
mut = new MutationObserver(() => renderChart());
|
||||||
mut.observe(document.documentElement, { attributes: true });
|
mut.observe(document.documentElement, { attributes: true });
|
||||||
|
|
||||||
|
// Set the initial value of the contenteditable element
|
||||||
|
// We cannot bind using `{{ code }}` because it will rerender the whole component
|
||||||
|
// when the value changes, shifting the cursor when enter is used
|
||||||
|
editableContent.value.textContent = code.value;
|
||||||
|
|
||||||
await renderChart();
|
await renderChart();
|
||||||
|
|
||||||
//refresh images on first render
|
//refresh images on first render
|
||||||
const hasImages = /<img([\w\W]+?)>/.exec(decodeURIComponent(props.graph))?.length > 0;
|
const hasImages = /<img([\w\W]+?)>/.exec(code.value)?.length > 0;
|
||||||
if (hasImages)
|
if (hasImages)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let imgElements = document.getElementsByTagName('img');
|
let imgElements = document.getElementsByTagName('img');
|
||||||
@@ -51,16 +75,14 @@ onMounted(async () => {
|
|||||||
onUnmounted(() => mut.disconnect());
|
onUnmounted(() => mut.disconnect());
|
||||||
|
|
||||||
const renderChart = async () => {
|
const renderChart = async () => {
|
||||||
console.log('rendering chart' + props.id + props.graph);
|
console.log('rendering chart' + props.id + code.value);
|
||||||
const hasDarkClass = document.documentElement.classList.contains('dark');
|
const hasDarkClass = document.documentElement.classList.contains('dark');
|
||||||
const mermaidConfig = {
|
const mermaidConfig = {
|
||||||
securityLevel: 'loose',
|
securityLevel: 'loose',
|
||||||
startOnLoad: false,
|
startOnLoad: false,
|
||||||
theme: hasDarkClass ? 'dark' : 'default',
|
theme: hasDarkClass ? 'dark' : 'default',
|
||||||
};
|
};
|
||||||
|
let svgCode = await render(props.id, code.value, mermaidConfig);
|
||||||
console.log({ mermaidConfig });
|
|
||||||
let svgCode = await render(props.id, decodeURIComponent(props.graph), mermaidConfig);
|
|
||||||
// This is a hack to force v-html to re-render, otherwise the diagram disappears
|
// This is a hack to force v-html to re-render, otherwise the diagram disappears
|
||||||
// when **switching themes** or **reloading the page**.
|
// when **switching themes** or **reloading the page**.
|
||||||
// The cause is that the diagram is deleted during rendering (out of Vue's knowledge).
|
// The cause is that the diagram is deleted during rendering (out of Vue's knowledge).
|
||||||
@@ -70,3 +92,35 @@ const renderChart = async () => {
|
|||||||
svg.value = `${svgCode} <span style="display: none">${salt}</span>`;
|
svg.value = `${svgCode} <span style="display: none">${salt}</span>`;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.editable-code:focus {
|
||||||
|
outline: none; /* Removes the default focus indicator */
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons-container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons-container > span {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons-container > button {
|
||||||
|
color: #007bffbf;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons-container > button:hover {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user