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 TopBar from "./components/layout/TopBar.vue";
|
||||
import SideBar from "./components/layout/SideBar.vue";
|
||||
import TableOfContent from "./components/layout/TableOfContent.vue";
|
||||
import { useConfigStore } from "./stores/config";
|
||||
import { onMounted } from "vue";
|
||||
import axios from "axios";
|
||||
|
@ -17,6 +18,9 @@ onMounted(() => {
|
|||
<TopBar id="topbar" />
|
||||
<div id="wrapper">
|
||||
<SideBar />
|
||||
<div id="page"><RouterView /></div>
|
||||
<div id="page">
|
||||
<RouterView />
|
||||
</div>
|
||||
<TableOfContent />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,20 +1,56 @@
|
|||
<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";
|
||||
|
||||
const props = defineProps<{
|
||||
path: string;
|
||||
order?: number;
|
||||
}>();
|
||||
|
||||
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() {
|
||||
const markdownFileUrl = `/${props.path}.md`;
|
||||
if (loadedPage.value === markdownFileUrl) {
|
||||
return;
|
||||
}
|
||||
loadedPage.value = markdownFileUrl;
|
||||
console.log(`Chargement de l'URL ${markdownFileUrl}`);
|
||||
axios
|
||||
.get(markdownFileUrl)
|
||||
.then((response) => (htmlContent.value = marked.parse(response.data)))
|
||||
.then((response) => {
|
||||
tocNbr = 1;
|
||||
htmlContent.value = marked.parse(response.data);
|
||||
console.log(toc);
|
||||
})
|
||||
.catch(
|
||||
() =>
|
||||
(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 RuleView from "../views/RuleView.vue";
|
||||
import JdrView from "../views/JdrView.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
|
@ -28,6 +28,15 @@ const router = createRouter({
|
|||
component: () => import("../views/AboutView.vue"),
|
||||
},
|
||||
],
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (to.hash) {
|
||||
return {
|
||||
el: to.hash,
|
||||
behavior: "smooth",
|
||||
top: 64,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
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 {
|
||||
flex-grow:1;
|
||||
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 {
|
||||
|
|
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>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useConfigStore } from "@/stores/config";
|
||||
import { onMounted } from "vue";
|
||||
import { useTocStore } from "@/stores/toc";
|
||||
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
const store = useConfigStore();
|
||||
const toc = useTocStore();
|
||||
|
||||
onMounted(() => {
|
||||
store.resetJdr();
|
||||
toc.resetToc();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MarkdownFile path="pages/about"></MarkdownFile>
|
||||
</template>
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import { useConfigStore } from "@/stores/config";
|
||||
import { onMounted } from "vue";
|
||||
import { useTocStore } from "@/stores/toc";
|
||||
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||
|
||||
const store = useConfigStore();
|
||||
const toc = useTocStore();
|
||||
|
||||
onMounted(() => {
|
||||
store.resetJdr();
|
||||
toc.resetToc();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { useConfigStore } from "@/stores/config";
|
||||
import { useTocStore } from "@/stores/toc";
|
||||
import { onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||
|
||||
const store = useConfigStore();
|
||||
const route = useRoute();
|
||||
const toc = useTocStore();
|
||||
|
||||
onMounted(() => {
|
||||
toc.resetToc();
|
||||
store.loadJdr(`${route.params.jdr}`);
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { useConfigStore } from "@/stores/config";
|
||||
import { useTocStore } from "@/stores/toc";
|
||||
import { onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import MarkdownFile from "../components/MarkdownFile.vue";
|
||||
|
||||
const store = useConfigStore();
|
||||
const toc = useTocStore();
|
||||
const route = useRoute();
|
||||
|
||||
onMounted(() => {
|
||||
toc.resetToc();
|
||||
store.loadJdr(`${route.params.jdr}`);
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Reference in a new issue