Skip to content

Commit 3fb8d96

Browse files
alexeyzimarevw1am
andauthored
[DEV-780] Move versions from navbar to version selector (#899)
Co-authored-by: William Chong <[email protected]>
1 parent 4053d56 commit 3fb8d96

File tree

25 files changed

+650
-301
lines changed

25 files changed

+650
-301
lines changed

docs/.vuepress/client.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import CloudBanner from "./components/CloudBanner.vue";
66
import KapaWidget from './components/KapaWidget.vue';
77
import UserFeedback from './components/TocWithFeedback';
88
import {usePostHog} from "./lib/usePosthog";
9+
import SidebarLayout from "./layouts/SidebarLayout.vue";
910

1011
declare const __VERSIONS__: { latest: string, selected: string, all: string[] }
1112

@@ -30,14 +31,14 @@ const findEsMeta = (route) => {
3031
}
3132
}
3233

33-
interface ClientConfig {
34-
enhance?: (context: {
35-
app: any;
36-
router: Router;
37-
siteData: any;
38-
}) => void | Promise<void>;
39-
setup?: () => void;
40-
}
34+
// interface ClientConfig {
35+
// enhance?: (context: {
36+
// app: any;
37+
// router: Router;
38+
// siteData: any;
39+
// }) => void | Promise<void>;
40+
// setup?: () => void;
41+
// }
4142

4243
const removeHtml = (path: string) => path.replace(".html", "");
4344

@@ -55,9 +56,12 @@ const leave = (to: RouteLocationNormalized, from: RouteLocationNormalized) => {
5556
}
5657
}
5758

58-
const { posthog } = usePostHog();
59+
const {posthog} = usePostHog();
5960

6061
export default defineClientConfig({
62+
layouts: {
63+
Layout: SidebarLayout
64+
},
6165
enhance({app, router, _}) {
6266
app.component("CloudBanner", CloudBanner);
6367
app.component("KapaWidget", KapaWidget);
@@ -84,11 +88,11 @@ export default defineClientConfig({
8488
router.afterEach(() => {
8589
setTimeout(() => { // to ensure this runs after DOM updates
8690
try {
87-
const {code} = JSON.parse(localStorage.getItem('VUEPRESS_TAB_STORE'));
91+
const {code} = JSON.parse(localStorage.getItem('VUEPRESS_TAB_STORE')!);
8892
if (code) { // If a valid 'code' is found in localStorage
8993
Array.from(document.querySelectorAll('.vp-tab-nav'))
9094
.forEach((button: HTMLButtonElement) => {
91-
if (button.textContent.trim() === code) {
95+
if (button.textContent!.trim() === code) {
9296
button.click(); // click the button to switch the tab
9397
}
9498
});
@@ -135,13 +139,13 @@ export default defineClientConfig({
135139
});
136140
}, 1000);
137141
}
138-
142+
139143
// Check for 404 page after navigation completes
140144
setTimeout(() => {
141145
// Check for the specific elements with classes error-code and error-hint
142146
const errorCodeElement = document.querySelector('p.error-code');
143147
const errorHintElement = document.querySelector('p.error-hint');
144-
148+
145149
// If both elements exist, we're on a 404 page
146150
if (errorCodeElement && errorHintElement) {
147151
// Capture the 404 event in PostHog
@@ -154,15 +158,15 @@ export default defineClientConfig({
154158
});
155159
}
156160
}
157-
}, 50);
161+
}, 50);
158162
});
159163
router.beforeEach((to, from) => leave(to, from));
160164
},
161165
setup() {
162166
onMounted(() => {
163167
const route = useRoute();
164-
if (route.path !== "/");
168+
if (route.path !== "/") ;
165169
});
166170

167171
},
168-
} satisfies ClientConfig);
172+
});

docs/.vuepress/components/KapaWidget.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script setup>
22
function triggerKapa() {
33
window.Kapa.open();
4-
54
}
65
</script>
76

docs/.vuepress/components/TocWithFeedback.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import {
2020
usePageFrontmatter,
2121
useRoute,
2222
} from "vuepress/client";
23-
import PrintButton from "vuepress-theme-hope/modules/info/components/PrintButton";
24-
import { useMetaLocale } from "vuepress-theme-hope/modules/info/composables/index";
23+
import PrintButton from "vuepress-theme-hope/components/base/PrintButton";
24+
import { useMetaLocale } from "vuepress-theme-hope/composables/info/index";
2525

2626
import "../styles/toc.scss";
2727

docs/.vuepress/components/Version.vue

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<script setup lang="ts">
2+
import {computed, onMounted, onUnmounted, ref, watch} from "vue";
3+
import {useRoute, useRouter} from "vuepress/client";
4+
import type {VersionDetail} from "../lib/versioning";
5+
import VersionText from "./VersionText.vue";
6+
import VersionSection from "./VersionSection.vue";
7+
8+
interface Props {
9+
versions: VersionDetail[];
10+
current: VersionDetail;
11+
}
12+
13+
const props = defineProps<Props>();
14+
15+
const router = useRouter();
16+
const route = useRoute();
17+
const selectedVersion = ref(props.current);
18+
const isOpen = ref(false);
19+
20+
const latestVersion = computed(() => props.versions[0]?.version);
21+
const currentVersions = computed(() => props.versions.filter(v => !v.deprecated && !v.hide));
22+
const deprecatedVersions = computed(() => props.versions.filter(v => v.deprecated && !v.hide));
23+
24+
watch(() => props.current, (newCurrent) => selectedVersion.value = newCurrent);
25+
26+
const toggleDropdown = (): void => {
27+
isOpen.value = !isOpen.value;
28+
}
29+
30+
const closeDropdown = (): void => {
31+
isOpen.value = false;
32+
}
33+
34+
const handleVersionSelect = (version: VersionDetail): void => {
35+
const base = route.path.split('/').filter(seg => seg)[0];
36+
router.replace(`/${base}/${version.path}/${version.startPage}`);
37+
38+
closeDropdown();
39+
}
40+
41+
const handleClickOutside = (event: Event): void => {
42+
const target = event.target as HTMLElement;
43+
const dropdown = document.querySelector('.version-dropdown');
44+
45+
if (dropdown && !dropdown.contains(target)) {
46+
closeDropdown();
47+
}
48+
}
49+
50+
const isSelected = (version: VersionDetail): boolean => {
51+
return version.version === selectedVersion.value.version;
52+
}
53+
54+
onMounted(() => document.addEventListener('click', handleClickOutside));
55+
onUnmounted(() => document.removeEventListener('click', handleClickOutside));
56+
</script>
57+
58+
<template>
59+
<div class="version-dropdown">
60+
<button
61+
type="button"
62+
class="version-trigger"
63+
aria-label="Select documentation version"
64+
:aria-expanded="isOpen"
65+
aria-haspopup="listbox"
66+
@click="toggleDropdown"
67+
>
68+
<VersionText class="selected-version" :version="selectedVersion" :latest="latestVersion"/>
69+
<svg
70+
class="dropdown-arrow"
71+
:class="{ 'rotate-180': isOpen }"
72+
width="16"
73+
height="16"
74+
viewBox="0 0 16 16"
75+
fill="none"
76+
xmlns="http://www.w3.org/2000/svg"
77+
>
78+
<path
79+
d="M4 6L8 10L12 6"
80+
stroke="currentColor"
81+
stroke-width="2"
82+
stroke-linecap="round"
83+
stroke-linejoin="round"
84+
/>
85+
</svg>
86+
</button>
87+
88+
<div
89+
v-if="isOpen"
90+
class="dropdown-menu"
91+
role="listbox"
92+
aria-label="Version options"
93+
>
94+
<!-- Current versions -->
95+
<VersionSection
96+
:versions="currentVersions"
97+
title="Current"
98+
:latest="latestVersion"
99+
:is-selected="isSelected"
100+
@select="handleVersionSelect"
101+
/>
102+
103+
<!-- Deprecated versions -->
104+
<VersionSection
105+
:versions="deprecatedVersions"
106+
title="Deprecated"
107+
:margin-top="'8px'"
108+
:latest="latestVersion"
109+
:is-selected="isSelected"
110+
:is-deprecated="true"
111+
@select="handleVersionSelect"
112+
/>
113+
</div>
114+
</div>
115+
</template>
116+
117+
<style lang="scss" scoped>
118+
.version-dropdown {
119+
position: relative;
120+
margin: 1rem 0.5rem 0;
121+
122+
.version-trigger {
123+
width: 100%;
124+
padding: 0.5rem 1rem;
125+
border: 1px solid var(--vp-c-border);
126+
border-radius: 0.375rem;
127+
background-color: transparent;
128+
font-size: 16px;
129+
font-weight: 500;
130+
color: var(--text-color);
131+
cursor: pointer;
132+
display: flex;
133+
align-items: center;
134+
justify-content: space-between;
135+
text-align: left;
136+
transition: all 0.2s ease;
137+
138+
&:hover,
139+
&:focus {
140+
outline: 2px solid rgba(78, 87, 90, 0.2);
141+
outline-offset: 1px;
142+
}
143+
144+
&:focus {
145+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
146+
}
147+
148+
.selected-version {
149+
flex: 1;
150+
}
151+
152+
.dropdown-arrow {
153+
flex-shrink: 0;
154+
margin-left: 0.5rem;
155+
transition: transform 0.2s ease;
156+
157+
&.rotate-180 {
158+
transform: rotate(180deg);
159+
}
160+
}
161+
}
162+
163+
.dropdown-menu {
164+
position: absolute;
165+
top: 100%;
166+
left: 0;
167+
right: 0;
168+
z-index: 50;
169+
margin-top: 0.3rem;
170+
border: 1px solid var(--vp-c-divider);
171+
border-radius: 0.5rem;
172+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
173+
0 4px 6px -2px rgba(0, 0, 0, 0.05);
174+
max-height: 250px;
175+
overflow-y: auto;
176+
background-color: var(--vp-c-bg-elv);
177+
178+
// Webkit scrollbar styles
179+
&::-webkit-scrollbar {
180+
width: 8px;
181+
background-color: var(--vp-c-bg-elv);
182+
}
183+
184+
&::-webkit-scrollbar-track {
185+
background-color: var(--vp-c-bg-elv);
186+
}
187+
188+
&::-webkit-scrollbar-thumb {
189+
background-color: rgba(255, 255, 255, 0.4);
190+
border-radius: 4px;
191+
}
192+
193+
// Firefox scrollbar
194+
scrollbar-width: thin;
195+
scrollbar-color: rgb(161, 161, 161) var(--vp-c-bg-elv);
196+
197+
// Styles for dropdown-separator and dropdown-item are now in VersionSection.vue
198+
}
199+
}
200+
</style>

0 commit comments

Comments
 (0)