Files
web-frontend/docs/.vitepress/theme/components/MermaidDiagram.vue
2026-05-01 15:09:02 +07:00

100 lines
1.9 KiB
Vue

<script setup lang="ts">
import mermaid from 'mermaid';
import { computed, onMounted, ref, watch } from 'vue';
const props = defineProps<{
chart: string;
}>();
const container = ref<HTMLElement | null>(null);
const error = ref('');
let initialized = false;
let diagramId = 0;
function initMermaid() {
if (initialized) {
return;
}
mermaid.initialize({
startOnLoad: false,
theme: 'neutral',
securityLevel: 'loose',
fontFamily: 'Inter, Arial, sans-serif',
flowchart: {
useMaxWidth: true,
htmlLabels: true,
},
});
initialized = true;
}
const source = computed(() => props.chart.trim());
async function renderDiagram() {
if (!import.meta.client || !container.value) {
return;
}
try {
initMermaid();
error.value = '';
diagramId += 1;
container.value.removeAttribute('data-processed');
container.value.id = `mermaid-diagram-${diagramId}`;
container.value.textContent = source.value;
await mermaid.run({
nodes: [container.value],
});
} catch (cause) {
error.value = cause instanceof Error ? cause.message : 'Failed to render Mermaid diagram.';
}
}
onMounted(() => {
void renderDiagram();
});
watch(source, () => {
void renderDiagram();
});
</script>
<template>
<div class="mermaid-diagram">
<div v-if="error" class="mermaid-diagram__error">{{ error }}</div>
<pre v-else ref="container" class="mermaid-diagram__canvas mermaid" />
</div>
</template>
<style scoped>
.mermaid-diagram {
margin: 1.25rem 0 1.75rem;
border: 1px solid #d9dee7;
border-radius: 20px;
background: #fff;
overflow: hidden;
}
.mermaid-diagram__canvas {
padding: 1rem;
margin: 0;
white-space: pre-wrap;
}
.mermaid-diagram__canvas :deep(svg) {
display: block;
width: 100%;
height: auto;
}
.mermaid-diagram__error {
padding: 1rem 1.25rem;
color: #b42318;
background: #fef3f2;
font-size: 0.95rem;
}
</style>