Skip to content

Commit 6ac7a33

Browse files
committed
feat: improve title anchor
1 parent 63bda56 commit 6ac7a33

File tree

6 files changed

+94
-14
lines changed

6 files changed

+94
-14
lines changed

docs/.vitepress/config.mts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {mathjaxContainerPreprocessor} from "./theme/mathjax/mathjaxContainerPrep
1111
import {withMermaid} from "vitepress-plugin-mermaid";
1212
import {mermaidSpaceConverter} from "./theme/mermaid/mermaidSpaceConverter";
1313
import {injectUpgradingPartsPlugin} from "./theme/upgrading/injectUpgradingPartsPlugin";
14+
import {titleAnchorPreprocessor} from "./theme/anchor/titleAnchorPreprocessor";
15+
import slugifyTitle from "./theme/anchor/slugify";
1416

1517
const defaultLocale: string = 'en';
1618
const supportLocales: string[] = [
@@ -76,6 +78,11 @@ const vitepressOptions: UserConfig = {
7678
]
7779
},
7880
markdown: {
81+
anchor: {
82+
slugify(str: string): string {
83+
return slugifyTitle(str);
84+
}
85+
},
7986
container: {
8087
infoLabel: '**Developer\'s Note:**',
8188
},
@@ -112,6 +119,7 @@ const vitepressOptions: UserConfig = {
112119
injectUpgradingPartsPlugin(md);
113120
mermaidSpaceConverter(md);
114121
exampleAutoAnchorPreprocessor(md);
122+
titleAnchorPreprocessor(md);
115123
mathjaxContainerPreprocessor(md);
116124
}
117125
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<script setup>
2+
3+
const props = defineProps({
4+
anchor: String
5+
})
6+
7+
function onClick() {
8+
const element = document.getElementById(props.anchor)
9+
if(!element) {
10+
return;
11+
}
12+
const y = element.getBoundingClientRect().top + window.scrollY - 109;
13+
window.scrollTo({ top: y, behavior: 'smooth' });
14+
history.pushState(null, '', `#${props.anchor}`);
15+
}
16+
</script>
17+
18+
<template>
19+
<div @click="onClick">
20+
<slot/>
21+
</div>
22+
</template>
23+
24+
<style scoped>
25+
26+
</style>

docs/.vitepress/theme/anchor/exampleAutoAnchorPreprocessor.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
import MarkdownIt from "markdown-it";
2-
import slugify from "@sindresorhus/slugify";
2+
import slugifyTitle from "./slugify";
33

44
export const exampleAutoAnchorPreprocessor = (md: MarkdownIt) => {
55
md.core.ruler.before('normalize', 'example-auto-anchor', (state) => {
6+
if (!state.src.includes("tip Example")) {
7+
return;
8+
}
69
const lines = state.src.split("\n");
710
const processedLines = lines.map((line) => {
811
if (line.startsWith(":::") && (line.includes(":::tip Example") || line.includes("::: tip Example"))) {
912
const title = line.split(/tip (.+)/)[1]
10-
const processedTitle = slugify(title
11-
.replace(" – ", "-")
12-
.replace(":", "-")
13-
.replace("\"", "")
14-
.replace("'", "")
15-
.replace("<", "")
16-
.replace(">", "")
17-
);
13+
const processedTitle = slugifyTitle(title);
1814
return `
19-
<div style="height: 0; color: rgba(0,0,0,0%);position: relative;top: 32px">
20-
<h3 id="${processedTitle}">${title}
15+
<div style="height: 0;
16+
color: rgba(0,0,0,0%);
17+
position: relative;
18+
top: 32px;">
19+
<TitleAnchor anchor="${processedTitle}">
20+
<h3 id="${processedTitle}" style="
21+
-webkit-user-select: none;
22+
-moz-user-select: none;
23+
-ms-user-select: none;
24+
user-select: none;
25+
">${title}
2126
<a class="header-anchor" href="#${processedTitle}">​</a>
22-
</h3>
27+
</h3>
28+
</TitleAnchor>
2329
</div>
2430
2531
${line}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import slugify from "@sindresorhus/slugify";
2+
3+
export default function slugifyTitle(title: string): string {
4+
return slugify(title
5+
.replace(" – ", "-")
6+
.replace(":", "-")
7+
.replace("\"", "")
8+
.replace("'", "")
9+
.replace("<", "")
10+
.replace(">", "")
11+
);
12+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import MarkdownIt from "markdown-it";
2+
import slugifyTitle from "./slugify";
3+
4+
export const titleAnchorPreprocessor = (md: MarkdownIt) => {
5+
md.core.ruler.before('normalize', 'title-anchor', (state) => {
6+
const lines = state.src.split("\n");
7+
const processedLines = lines.map((line) => {
8+
if (line.startsWith("#")) {
9+
const title = line.replace(new RegExp("#", "g"), "");
10+
const processedTitle = slugifyTitle(title);
11+
return `
12+
13+
<TitleAnchor anchor="${processedTitle}">
14+
15+
${line}
16+
17+
</TitleAnchor>
18+
19+
`;
20+
} else {
21+
return line;
22+
}
23+
});
24+
state.src = processedLines.join("\n");
25+
});
26+
}

docs/.vitepress/theme/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import type {Theme} from 'vitepress'
2+
import {useRoute} from 'vitepress'
23
import DefaultTheme from 'vitepress/theme'
34
import PluginTabs from './tabs/PluginTabs.vue'
45
import PluginTabsTab from './tabs/PluginTabsTab.vue'
56
import {provideTabsSharedState} from './tabs/useTabsSelectedState'
67
import PreferenceSwitch from './prefer/PreferenceSwitch.vue';
78
import mediumZoom from "medium-zoom";
8-
import {onMounted, watch, nextTick, h} from 'vue'
9-
import {useRoute} from 'vitepress'
9+
import {h, nextTick, onMounted, watch} from 'vue'
1010
import AuthorsComponent from "./author/PageAuthors.vue";
1111
import {NolebaseEnhancedReadabilitiesMenu, NolebaseEnhancedReadabilitiesScreenMenu} from "@nolebase/vitepress-plugin-enhanced-readabilities";
1212

1313
import './style/global.css'
1414
import '@nolebase/vitepress-plugin-enhanced-readabilities/client/style.css'
1515
import VersionSwitcher from "./versioning/VersionSwitcher.vue";
16+
import TitleAnchor from "./anchor/TitleAnchor.vue";
1617

1718
// noinspection JSUnusedGlobalSymbols
1819
export default {
@@ -22,6 +23,7 @@ export default {
2223
app.component('VersionSwitcher', VersionSwitcher)
2324
app.component('PluginTabs', PluginTabs)
2425
app.component('PluginTabsTab', PluginTabsTab)
26+
app.component('TitleAnchor', TitleAnchor)
2527
},
2628
Layout() {
2729
const children = {

0 commit comments

Comments
 (0)