Skip to content

Commit a8dc10c

Browse files
committed
feat: add version switcher
1 parent cc5c4ea commit a8dc10c

File tree

4 files changed

+201
-2
lines changed

4 files changed

+201
-2
lines changed

docs/.vitepress/config.mts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ const vitePressI18nOptions: VitePressI18nOptions = {
124124
}, {
125125
text: 'Introduction',
126126
link: '/intro'
127-
},
127+
}, {
128+
component: 'VersionSwitcher'
129+
}
128130
]
129131
}
130132
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<!--modified from https://github.com/IMB11/vitepress-versioning-plugin , original license is MIT-->
2+
3+
<script setup lang="ts">
4+
import {onMounted, ref, watch} from 'vue'
5+
import VPMenuLink from 'vitepress/dist/client/theme-default/components/VPMenuLink.vue'
6+
import VPFlyout from 'vitepress/dist/client/theme-default/components/VPFlyout.vue'
7+
import {parse} from "yaml";
8+
import {useRoute} from "vitepress";
9+
10+
const props = defineProps<{
11+
screenMenu?: boolean
12+
}>();
13+
const route = useRoute();
14+
15+
let versionList: string[] = [];
16+
const currentVersion = ref('');
17+
const latestVersion = ref('');
18+
const versions = ref<string[]>([]);
19+
20+
function refresh() {
21+
let version = latestVersion.value;
22+
23+
for (const v of versionList) {
24+
if (window.location.pathname.startsWith(`/${v}/`)) {
25+
version = v;
26+
break;
27+
}
28+
}
29+
30+
currentVersion.value = version;
31+
versions.value = versionList;
32+
}
33+
34+
async function init() {
35+
const versionDataFileUrl = `${window.location.origin}/versions.yml`;
36+
const versionDataFileContent = await (await fetch(versionDataFileUrl)).text();
37+
const versionData = parse(versionDataFileContent);
38+
versionList = versionData.versions;
39+
latestVersion.value = versionList[0];
40+
versionList.shift();
41+
refresh()
42+
}
43+
44+
const isOpen = ref(false);
45+
const toggle = () => {
46+
isOpen.value = !isOpen.value;
47+
};
48+
49+
onMounted(async () => init())
50+
watch(
51+
() => route.path,
52+
() => refresh()
53+
);
54+
</script>
55+
56+
<template>
57+
<VPFlyout v-if="!screenMenu" class="VPVersionSwitcher" icon="vpi-versioning" :button="currentVersion"
58+
:label="'Switch Version'">
59+
<div class="items">
60+
<VPMenuLink v-if="currentVersion != latestVersion" :item="{
61+
text: latestVersion,
62+
link: `/`,
63+
}"/>
64+
<template v-for="version in versions" :key="version">
65+
<VPMenuLink v-if="currentVersion != version" :item="{
66+
text: version,
67+
link: `/${version}/`,
68+
}"/>
69+
</template>
70+
</div>
71+
</VPFlyout>
72+
<div v-else class="VPScreenVersionSwitcher" :class="{ open: isOpen }">
73+
<button class="button" aria-controls="navbar-group-version" :aria-expanded="isOpen" @click="toggle">
74+
<span class="button-text"><span class="vpi-versioning icon"/>Switch Version</span>
75+
<span class="vpi-plus button-icon"/>
76+
</button>
77+
78+
<div id="navbar-group-version" class="items">
79+
<VPMenuLink :item="{
80+
text: latestVersion,
81+
link: `/`,
82+
}"/>
83+
<template v-for="version in versions" :key="version">
84+
<VPMenuLink :item="{
85+
text: version,
86+
link: `/${version}/`,
87+
}"/>
88+
</template>
89+
</div>
90+
</div>
91+
</template>
92+
93+
<style>
94+
.vpi-versioning.option-icon {
95+
margin-right: 2px !important;
96+
}
97+
98+
.vpi-versioning {
99+
--icon: url("data:image/svg+xml;charset=utf-8;base64,PHN2ZyB3aWR0aD0iNjRweCIgaGVpZ2h0PSI2NHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHN0cm9rZS13aWR0aD0iMi4yIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNvbG9yPSIjMDAwMDAwIj48cGF0aCBkPSJNMTcgN0MxOC4xMDQ2IDcgMTkgNi4xMDQ1NyAxOSA1QzE5IDMuODk1NDMgMTguMTA0NiAzIDE3IDNDMTUuODk1NCAzIDE1IDMuODk1NDMgMTUgNUMxNSA2LjEwNDU3IDE1Ljg5NTQgNyAxNyA3WiIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIuMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48L3BhdGg+PHBhdGggZD0iTTcgN0M4LjEwNDU3IDcgOSA2LjEwNDU3IDkgNUM5IDMuODk1NDMgOC4xMDQ1NyAzIDcgM0M1Ljg5NTQzIDMgNSAzLjg5NTQzIDUgNUM1IDYuMTA0NTcgNS44OTU0MyA3IDcgN1oiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyLjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PC9wYXRoPjxwYXRoIGQ9Ik03IDIxQzguMTA0NTcgMjEgOSAyMC4xMDQ2IDkgMTlDOSAxNy44OTU0IDguMTA0NTcgMTcgNyAxN0M1Ljg5NTQzIDE3IDUgMTcuODk1NCA1IDE5QzUgMjAuMTA0NiA1Ljg5NTQzIDIxIDcgMjFaIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi4yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD48cGF0aCBkPSJNNyA3VjE3IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi4yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD48cGF0aCBkPSJNMTcgN1Y4QzE3IDEwLjUgMTUgMTEgMTUgMTFMOSAxM0M5IDEzIDcgMTMuNSA3IDE2VjE3IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi4yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD48L3N2Zz4=")
100+
}
101+
</style>
102+
103+
<style scoped>
104+
.VPMenuLink {
105+
font-family: 'Helvetica Neue', sans-serif;
106+
}
107+
108+
.VPVersionSwitcher {
109+
font-family: 'Helvetica Neue', sans-serif;
110+
display: flex;
111+
align-items: center;
112+
}
113+
114+
.icon {
115+
padding: 8px;
116+
}
117+
118+
.title {
119+
padding: 0 24px 0 12px;
120+
line-height: 32px;
121+
font-size: 14px;
122+
font-weight: 700;
123+
color: var(--vp-c-text-1);
124+
}
125+
126+
.VPScreenVersionSwitcher {
127+
border-bottom: 1px solid var(--vp-c-divider);
128+
height: 48px;
129+
overflow: hidden;
130+
transition: border-color 0.5s;
131+
}
132+
133+
.VPScreenVersionSwitcher .items {
134+
visibility: hidden;
135+
}
136+
137+
.VPScreenVersionSwitcher.open .items {
138+
visibility: visible;
139+
}
140+
141+
.VPScreenVersionSwitcher.open {
142+
padding-bottom: 10px;
143+
height: auto;
144+
}
145+
146+
.VPScreenVersionSwitcher.open .button {
147+
padding-bottom: 6px;
148+
color: var(--vp-c-brand-1);
149+
}
150+
151+
.VPScreenVersionSwitcher.open .button-icon {
152+
/*rtl:ignore*/
153+
transform: rotate(45deg);
154+
}
155+
156+
.VPScreenVersionSwitcher button .icon {
157+
margin-right: 8px;
158+
}
159+
160+
.button {
161+
display: flex;
162+
justify-content: space-between;
163+
align-items: center;
164+
padding: 12px 4px 11px 0;
165+
width: 100%;
166+
line-height: 24px;
167+
font-size: 14px;
168+
font-weight: 500;
169+
color: var(--vp-c-text-1);
170+
transition: color 0.25s;
171+
}
172+
173+
.button:hover {
174+
color: var(--vp-c-brand-1);
175+
}
176+
177+
.button-icon {
178+
transition: transform 0.25s;
179+
}
180+
181+
.group:first-child {
182+
padding-top: 0;
183+
}
184+
185+
.group + .group,
186+
.group + .item {
187+
padding-top: 4px;
188+
}
189+
</style>

docs/.vitepress/theme/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import {provideTabsSharedState} from './tabs/useTabsSelectedState'
66
import PreferenceSwitch from './prefer/PreferenceSwitch.vue';
77
import mediumZoom from "medium-zoom";
88
import {onMounted, watch, nextTick, h} from 'vue'
9-
import {useData, useRoute} from 'vitepress'
9+
import {useRoute} from 'vitepress'
1010
import AuthorsComponent from "./components/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'
15+
import VersionSwitcher from "./components/VersionSwitcher.vue";
1516

1617
export default {
1718
extends: DefaultTheme,
1819
enhanceApp({app}) {
1920
provideTabsSharedState(app)
21+
app.component('VersionSwitcher', VersionSwitcher)
2022
app.component('PluginTabs', PluginTabs)
2123
app.component('PluginTabsTab', PluginTabsTab)
2224
},

docs/public/versions.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
versions:
2+
- 9.7.0
3+
- 9.6.1
4+
- 9.4.2
5+
- 9.3.0
6+
- 8.8.0

0 commit comments

Comments
 (0)