95 lines
1.7 KiB
Vue
95 lines
1.7 KiB
Vue
<script setup lang="ts">
|
|
import mermaid from 'mermaid';
|
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
|
|
const props = defineProps<{
|
|
chart: string;
|
|
}>();
|
|
|
|
const svg = ref('');
|
|
const error = ref('');
|
|
|
|
let initialized = false;
|
|
let diagramId = 0;
|
|
|
|
function initMermaid() {
|
|
if (initialized) {
|
|
return;
|
|
}
|
|
|
|
mermaid.initialize({
|
|
startOnLoad: false,
|
|
theme: 'neutral',
|
|
securityLevel: 'strict',
|
|
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) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
initMermaid();
|
|
error.value = '';
|
|
diagramId += 1;
|
|
const { svg: result } = await mermaid.render(`mermaid-diagram-${diagramId}`, source.value);
|
|
svg.value = result;
|
|
} catch (cause) {
|
|
svg.value = '';
|
|
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>
|
|
<div v-else class="mermaid-diagram__canvas" v-html="svg" />
|
|
</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;
|
|
}
|
|
|
|
.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>
|