improvement: separate md loader & renderer
This commit is contained in:
parent
6d360e1026
commit
d0f284dc36
2 changed files with 122 additions and 88 deletions
|
@ -1,93 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUpdate } from "vue";
|
||||
import { marked } from "marked";
|
||||
import { useTocStore } from "../stores/toc";
|
||||
|
||||
import axios from "axios";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useConfigStore } from "@/stores/config";
|
||||
import MarkdownRender from "./markdown/MarkdownRender.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
path: string;
|
||||
order?: number;
|
||||
}>();
|
||||
|
||||
const htmlContent = ref("");
|
||||
const loadedPage = ref("");
|
||||
|
||||
var specialQuote: Map<string, { class: string; text: string }> = new Map();
|
||||
specialQuote.set("NOTE", { class: "info", text: "Information :" });
|
||||
specialQuote.set("SUCCESS", { class: "success", text: "Success :" });
|
||||
specialQuote.set("WARNING", { class: "warning", text: "Warning :" });
|
||||
specialQuote.set("DANGER", { class: "danger", text: "Danger :" });
|
||||
|
||||
var renderer = new marked.Renderer();
|
||||
|
||||
const toc = useTocStore();
|
||||
const config = useConfigStore();
|
||||
const route = useRoute();
|
||||
var tocNbr = 1;
|
||||
|
||||
renderer.heading = function (text, level, raw) {
|
||||
var anchor = "#" + raw.toLowerCase().replace(/[^\w]+/g, "-");
|
||||
if (level === 2) {
|
||||
toc.addTocLine(route.path, {
|
||||
anchor: anchor,
|
||||
order: (props.order ?? 1) * 100 + tocNbr,
|
||||
text: text,
|
||||
});
|
||||
tocNbr++;
|
||||
}
|
||||
return `<h${level} id="${anchor}">${text}</h${level}>\n`;
|
||||
};
|
||||
|
||||
renderer.blockquote = function (quote) {
|
||||
var bqClass = "";
|
||||
var newQuote = quote;
|
||||
for (const [key, quoteData] of specialQuote) {
|
||||
if (quote.includes(`[!${key}]`)) {
|
||||
bqClass = `bg-${quoteData?.class}`;
|
||||
newQuote = newQuote.replace(
|
||||
`[!${key}]`,
|
||||
`<strong>${quoteData?.text}</strong>`
|
||||
);
|
||||
}
|
||||
}
|
||||
newQuote = newQuote.replace("\n", "<br />");
|
||||
return `<blockquote class="${bqClass}">${newQuote}</blockquote>`;
|
||||
};
|
||||
|
||||
const variable = {
|
||||
name: "variable",
|
||||
level: "inline", // Is this a block-level or inline-level tokenizer?
|
||||
start(src: string) {
|
||||
return src.match(/{{/)?.index;
|
||||
}, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(
|
||||
src: string
|
||||
): { type: string; raw: string; [index: string]: any } | undefined {
|
||||
const rule = /\{\{([A-Za-z0-9_]+)\}\}/; // Regex for the complete token, anchor to string start
|
||||
const match = rule.exec(src);
|
||||
if (match) {
|
||||
return {
|
||||
// Token to generate
|
||||
type: "variable", // Should match "name" above
|
||||
raw: match[0], // Text to consume from the source
|
||||
["varName"]: match[1],
|
||||
};
|
||||
}
|
||||
},
|
||||
renderer(token: { type: string; raw: string; [index: string]: any }): string {
|
||||
const varName = token["varName"] as string | null;
|
||||
const value = `${config.getVar(varName ?? "")}`;
|
||||
return value;
|
||||
},
|
||||
};
|
||||
|
||||
marked.setOptions({
|
||||
renderer: renderer,
|
||||
});
|
||||
marked.use({ extensions: [variable] });
|
||||
const markdown = ref("");
|
||||
|
||||
function refresh() {
|
||||
const markdownFileUrl = `/${props.path}.md`;
|
||||
|
@ -99,21 +22,17 @@ function refresh() {
|
|||
axios
|
||||
.get(markdownFileUrl)
|
||||
.then((response) => {
|
||||
tocNbr = 1;
|
||||
htmlContent.value = marked.parse(response.data);
|
||||
markdown.value = response.data;
|
||||
})
|
||||
.catch(
|
||||
() =>
|
||||
(htmlContent.value = marked.parse(
|
||||
"# 404 Not Found \n \n La page recherchée n'a pas pu être trouvée"
|
||||
))
|
||||
(markdown.value =
|
||||
"# 404 Not Found \n \n La page recherchée n'a pas pu être trouvée")
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
refresh();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
|
@ -123,7 +42,11 @@ onBeforeUpdate(() => {
|
|||
|
||||
<template>
|
||||
<article>
|
||||
<div v-html="htmlContent" class="markdown"></div>
|
||||
<MarkdownRender
|
||||
:markdown="markdown"
|
||||
:order="order"
|
||||
v-if="markdown !== ''"
|
||||
></MarkdownRender>
|
||||
<slot></slot>
|
||||
</article>
|
||||
</template>
|
||||
|
|
111
src/components/markdown/MarkdownRender.vue
Normal file
111
src/components/markdown/MarkdownRender.vue
Normal file
|
@ -0,0 +1,111 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUpdate } from "vue";
|
||||
import { marked } from "marked";
|
||||
import { useTocStore } from "../../stores/toc";
|
||||
|
||||
import { useRoute } from "vue-router";
|
||||
import { useConfigStore } from "@/stores/config";
|
||||
|
||||
const props = defineProps<{
|
||||
markdown: string;
|
||||
order?: number;
|
||||
}>();
|
||||
|
||||
const htmlContent = ref("");
|
||||
|
||||
var specialQuote: Map<string, { class: string; text: string }> = new Map();
|
||||
specialQuote.set("NOTE", { class: "info", text: "Information :" });
|
||||
specialQuote.set("SUCCESS", { class: "success", text: "Success :" });
|
||||
specialQuote.set("WARNING", { class: "warning", text: "Warning :" });
|
||||
specialQuote.set("DANGER", { class: "danger", text: "Danger :" });
|
||||
|
||||
var renderer = new marked.Renderer();
|
||||
|
||||
const toc = useTocStore();
|
||||
const config = useConfigStore();
|
||||
const route = useRoute();
|
||||
var tocNbr = 1;
|
||||
|
||||
renderer.heading = function (text, level, raw) {
|
||||
var anchor = "#" + raw.toLowerCase().replace(/[^\w]+/g, "-");
|
||||
if (level === 2) {
|
||||
toc.addTocLine(route.path, {
|
||||
anchor: anchor,
|
||||
order: (props.order ?? 1) * 100 + tocNbr,
|
||||
text: text,
|
||||
});
|
||||
tocNbr++;
|
||||
}
|
||||
return `<h${level} id="${anchor}">${text}</h${level}>\n`;
|
||||
};
|
||||
|
||||
renderer.blockquote = function (quote) {
|
||||
var bqClass = "";
|
||||
var newQuote = quote;
|
||||
for (const [key, quoteData] of specialQuote) {
|
||||
if (quote.includes(`[!${key}]`)) {
|
||||
bqClass = `bg-${quoteData?.class}`;
|
||||
newQuote = newQuote.replace(
|
||||
`[!${key}]`,
|
||||
`<strong>${quoteData?.text}</strong>`
|
||||
);
|
||||
}
|
||||
}
|
||||
newQuote = newQuote.replace("\n", "<br />");
|
||||
return `<blockquote class="${bqClass}">${newQuote}</blockquote>`;
|
||||
};
|
||||
|
||||
const variable = {
|
||||
name: "variable",
|
||||
level: "inline", // Is this a block-level or inline-level tokenizer?
|
||||
start(src: string) {
|
||||
return src.match(/{{/)?.index;
|
||||
}, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(
|
||||
src: string
|
||||
): { type: string; raw: string; [index: string]: any } | undefined {
|
||||
const rule = /\{\{([A-Za-z0-9_]+)\}\}/; // Regex for the complete token, anchor to string start
|
||||
const match = rule.exec(src);
|
||||
if (match) {
|
||||
return {
|
||||
// Token to generate
|
||||
type: "variable", // Should match "name" above
|
||||
raw: match[0], // Text to consume from the source
|
||||
["varName"]: match[1],
|
||||
};
|
||||
}
|
||||
},
|
||||
renderer(token: { type: string; raw: string; [index: string]: any }): string {
|
||||
const varName = token["varName"] as string | null;
|
||||
const value = `${config.getVar(varName ?? "")}`;
|
||||
return value;
|
||||
},
|
||||
};
|
||||
|
||||
marked.setOptions({
|
||||
renderer: renderer,
|
||||
});
|
||||
marked.use({ extensions: [variable] });
|
||||
|
||||
function render() {
|
||||
tocNbr = 1;
|
||||
if (htmlContent.value === "") {
|
||||
htmlContent.value = marked.parse(props.markdown);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
render();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article>
|
||||
<div v-html="htmlContent" class="markdown"></div>
|
||||
<slot></slot>
|
||||
</article>
|
||||
</template>
|
Loading…
Reference in a new issue