feat: ajout ToC
This commit is contained in:
parent
132bf5f060
commit
eaa1da8752
11 changed files with 145 additions and 18 deletions
|
@ -2,6 +2,7 @@
|
||||||
import { RouterView } from "vue-router";
|
import { RouterView } from "vue-router";
|
||||||
import TopBar from "./components/layout/TopBar.vue";
|
import TopBar from "./components/layout/TopBar.vue";
|
||||||
import SideBar from "./components/layout/SideBar.vue";
|
import SideBar from "./components/layout/SideBar.vue";
|
||||||
|
import TableOfContent from "./components/layout/TableOfContent.vue";
|
||||||
import { useConfigStore } from "./stores/config";
|
import { useConfigStore } from "./stores/config";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
@ -17,6 +18,9 @@ onMounted(() => {
|
||||||
<TopBar id="topbar" />
|
<TopBar id="topbar" />
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<SideBar />
|
<SideBar />
|
||||||
<div id="page"><RouterView /></div>
|
<div id="page">
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
<TableOfContent />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,20 +1,56 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUpdate } from "vue";
|
import { ref, onMounted, onBeforeUpdate } from "vue";
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
|
import { useTocStore } from "../stores/toc";
|
||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
path: string;
|
path: string;
|
||||||
|
order?: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const htmlContent = ref("");
|
const htmlContent = ref("");
|
||||||
|
const loadedPage = ref("");
|
||||||
|
|
||||||
|
var renderer = new marked.Renderer();
|
||||||
|
|
||||||
|
const toc = useTocStore();
|
||||||
|
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`;
|
||||||
|
};
|
||||||
|
|
||||||
|
marked.setOptions({
|
||||||
|
renderer: renderer,
|
||||||
|
});
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
const markdownFileUrl = `/${props.path}.md`;
|
const markdownFileUrl = `/${props.path}.md`;
|
||||||
|
if (loadedPage.value === markdownFileUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadedPage.value = markdownFileUrl;
|
||||||
console.log(`Chargement de l'URL ${markdownFileUrl}`);
|
console.log(`Chargement de l'URL ${markdownFileUrl}`);
|
||||||
axios
|
axios
|
||||||
.get(markdownFileUrl)
|
.get(markdownFileUrl)
|
||||||
.then((response) => (htmlContent.value = marked.parse(response.data)))
|
.then((response) => {
|
||||||
|
tocNbr = 1;
|
||||||
|
htmlContent.value = marked.parse(response.data);
|
||||||
|
console.log(toc);
|
||||||
|
})
|
||||||
.catch(
|
.catch(
|
||||||
() =>
|
() =>
|
||||||
(htmlContent.value = marked.parse(
|
(htmlContent.value = marked.parse(
|
||||||
|
|
22
src/components/layout/TableOfContent.vue
Normal file
22
src/components/layout/TableOfContent.vue
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useTocStore } from "../../stores/toc";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const toc = useTocStore();
|
||||||
|
const tocList = computed(() => {
|
||||||
|
return toc.getToc();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card" id="toc" v-if="tocList.length > 1">
|
||||||
|
<div class="card-header bg-primary">Sommaire</div>
|
||||||
|
<ul class="menu fg-dark">
|
||||||
|
<li v-for="(tocLine, index) in tocList" :key="index">
|
||||||
|
<router-link :to="`#${tocLine.anchor}`"
|
||||||
|
><span v-html="tocLine.text"></span
|
||||||
|
></router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,10 +1,10 @@
|
||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHashHistory } from "vue-router";
|
||||||
import HomeView from "../views/HomeView.vue";
|
import HomeView from "../views/HomeView.vue";
|
||||||
import RuleView from "../views/RuleView.vue";
|
import RuleView from "../views/RuleView.vue";
|
||||||
import JdrView from "../views/JdrView.vue";
|
import JdrView from "../views/JdrView.vue";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
|
@ -28,6 +28,15 @@ const router = createRouter({
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: () => import("../views/AboutView.vue"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
scrollBehavior(to, from, savedPosition) {
|
||||||
|
if (to.hash) {
|
||||||
|
return {
|
||||||
|
el: to.hash,
|
||||||
|
behavior: "smooth",
|
||||||
|
top: 64,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
27
src/stores/toc.ts
Normal file
27
src/stores/toc.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type TocLine from "@/types/TocLine";
|
||||||
|
|
||||||
|
export const useTocStore = defineStore("toc", () => {
|
||||||
|
const currentPage = ref("");
|
||||||
|
const tocLines = ref([] as TocLine[]);
|
||||||
|
|
||||||
|
function addTocLine(page: string, line: TocLine) {
|
||||||
|
if (page !== currentPage.value) {
|
||||||
|
tocLines.value = [];
|
||||||
|
currentPage.value = page;
|
||||||
|
}
|
||||||
|
tocLines.value.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getToc(): TocLine[] {
|
||||||
|
return tocLines.value.sort((a, b) => a.order - b.order);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetToc() {
|
||||||
|
tocLines.value = [];
|
||||||
|
currentPage.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return { currentPage, tocLines, addTocLine, getToc, resetToc };
|
||||||
|
});
|
|
@ -31,6 +31,18 @@
|
||||||
#page {
|
#page {
|
||||||
flex-grow:1;
|
flex-grow:1;
|
||||||
padding-left:18rem;
|
padding-left:18rem;
|
||||||
|
padding-right: 18rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc {
|
||||||
|
position: fixed;
|
||||||
|
top: 4rem;
|
||||||
|
right: 1rem;
|
||||||
|
width: 17rem;
|
||||||
|
.menu {
|
||||||
|
padding-left:0.5rem;
|
||||||
|
padding-right:0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
|
|
5
src/types/TocLine.ts
Normal file
5
src/types/TocLine.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default interface TocLine {
|
||||||
|
order: number;
|
||||||
|
text: string;
|
||||||
|
anchor: string;
|
||||||
|
}
|
|
@ -1,15 +1,18 @@
|
||||||
<template>
|
<script setup lang="ts">
|
||||||
<div class="about">
|
import { useConfigStore } from "@/stores/config";
|
||||||
<h1>This is an about page</h1>
|
import { onMounted } from "vue";
|
||||||
</div>
|
import { useTocStore } from "@/stores/toc";
|
||||||
</template>
|
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||||
|
|
||||||
<style>
|
const store = useConfigStore();
|
||||||
@media (min-width: 1024px) {
|
const toc = useTocStore();
|
||||||
.about {
|
|
||||||
min-height: 100vh;
|
onMounted(() => {
|
||||||
display: flex;
|
store.resetJdr();
|
||||||
align-items: center;
|
toc.resetToc();
|
||||||
}
|
});
|
||||||
}
|
</script>
|
||||||
</style>
|
|
||||||
|
<template>
|
||||||
|
<MarkdownFile path="pages/about"></MarkdownFile>
|
||||||
|
</template>
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useConfigStore } from "@/stores/config";
|
import { useConfigStore } from "@/stores/config";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
|
import { useTocStore } from "@/stores/toc";
|
||||||
import MarkdownFile from "../components/MarkdownFile.vue";
|
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||||
|
|
||||||
const store = useConfigStore();
|
const store = useConfigStore();
|
||||||
|
const toc = useTocStore();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
store.resetJdr();
|
store.resetJdr();
|
||||||
|
toc.resetToc();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useConfigStore } from "@/stores/config";
|
import { useConfigStore } from "@/stores/config";
|
||||||
|
import { useTocStore } from "@/stores/toc";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import MarkdownFile from "../components/MarkdownFile.vue";
|
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||||
|
|
||||||
const store = useConfigStore();
|
const store = useConfigStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const toc = useTocStore();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
toc.resetToc();
|
||||||
store.loadJdr(`${route.params.jdr}`);
|
store.loadJdr(`${route.params.jdr}`);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useConfigStore } from "@/stores/config";
|
import { useConfigStore } from "@/stores/config";
|
||||||
|
import { useTocStore } from "@/stores/toc";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import MarkdownFile from "../components/MarkdownFile.vue";
|
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||||
|
|
||||||
const store = useConfigStore();
|
const store = useConfigStore();
|
||||||
|
const toc = useTocStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
toc.resetToc();
|
||||||
store.loadJdr(`${route.params.jdr}`);
|
store.loadJdr(`${route.params.jdr}`);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue