Skip to content

Commit 143efcb

Browse files
committed
[website] added logic to version switcher
1 parent 74866cd commit 143efcb

File tree

10 files changed

+325
-53
lines changed

10 files changed

+325
-53
lines changed

website/algoliaConfig.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
export const algoliaConfig = {
22
appName: 'PDUJZB0TBK',
33
openKey: '9ad1935d853b24ce3fe9c0039bcf7b40',
4-
mainSearchIndexName: 'intergalactic-docs',
5-
iconsSearchIndexName: 'intergalactic-docs-icons',
6-
illustrationsSearchIndexName: 'intergalactic-docs-illustrations',
74
};

website/algoliaIndexes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { currentBuildVersion } from './docs/.vitepress/vite.config';
2+
3+
export const algoliaIndexes = {
4+
mainSearchIndexName: `${currentBuildVersion}_intergalactic-docs`,
5+
iconsSearchIndexName: `${currentBuildVersion}_intergalactic-docs-icons`,
6+
illustrationsSearchIndexName: `${currentBuildVersion}_intergalactic-docs-illustrations`,
7+
};

website/docs/.vitepress/buildHooks.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { createWriteStream } from 'fs';
2-
import { resolve as resolvePath } from 'path';
3-
import { SitemapStream } from 'sitemap';
4-
import { UserConfig, DefaultTheme } from 'vitepress';
52
import fs from 'fs/promises';
3+
import { resolve as resolvePath } from 'path';
4+
65
import algoliasearch from 'algoliasearch';
76
import parseMarkdownMetadata from 'parse-md';
7+
import { SitemapStream } from 'sitemap';
8+
import type { UserConfig, DefaultTheme } from 'vitepress';
9+
10+
import { currentBuildVersion, LATEST } from './vite.config';
11+
import { algoliaConfig } from '../../algoliaConfig.js';
12+
import { algoliaIndexes } from '../../algoliaIndexes.js';
813
import iconsList from '../style/icon/icons-list.js';
914
import illustrationsList from '../style/illustration/illustrations-list.js';
1015

1116
import 'dotenv/config';
12-
import { algoliaConfig } from '../../algoliaConfig.js';
1317

1418
const excludeFromSearch = ['a11y-report'];
1519

@@ -31,6 +35,10 @@ if (process.env.CI) {
3135
}
3236
}
3337

38+
const BASE_URL = `https://developer.semrush.com/intergalactic/${
39+
currentBuildVersion === LATEST ? '' : `${currentBuildVersion}/`
40+
}`;
41+
3442
const sitemapLinks: { url: string; lastmod?: number }[] = [];
3543
const searchObjects: {
3644
objectID: number;
@@ -116,10 +124,7 @@ const transformHtml: UserConfig<DefaultTheme.Config>['transformHtml'] = async (
116124
objectID: objectId++,
117125
title: title,
118126
type: level,
119-
url:
120-
'https://developer.semrush.com/intergalactic/' +
121-
pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2') +
122-
`#${id}`,
127+
url: BASE_URL + pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2') + `#${id}`,
123128
heading: true,
124129
hierarchy: { ...hierarchy },
125130
changelogPage: pageData.relativePath.includes('changelog'),
@@ -134,9 +139,7 @@ const transformHtml: UserConfig<DefaultTheme.Config>['transformHtml'] = async (
134139
title: metadata?.title ?? pageData.title,
135140
content: metadata?.title ?? pageData.title,
136141
type: 'content',
137-
url:
138-
'https://developer.semrush.com/intergalactic/' +
139-
pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2'),
142+
url: BASE_URL + pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2'),
140143
heading: false,
141144
hierarchy: { lvl0: hierarchy.lvl0, lvl1: hierarchy.lvl1 },
142145
changelogPage: pageData.relativePath.includes('changelog'),
@@ -147,7 +150,7 @@ const transformHtml: UserConfig<DefaultTheme.Config>['transformHtml'] = async (
147150
};
148151
const buildEnd: UserConfig<DefaultTheme.Config>['buildEnd'] = async ({ outDir }) => {
149152
const sitemap = new SitemapStream({
150-
hostname: 'https://developer.semrush.com/intergalactic/',
153+
hostname: BASE_URL,
151154
});
152155
const writeStream = createWriteStream(resolvePath(outDir, 'sitemap.xml'));
153156
sitemap.pipe(writeStream);
@@ -158,9 +161,9 @@ const buildEnd: UserConfig<DefaultTheme.Config>['buildEnd'] = async ({ outDir })
158161
if (process.env.CI) {
159162
// await fs.writeFile('search-index.json', JSON.stringify(searchObjects, null, 2));
160163
const client = algoliasearch(algoliaConfig.appName, process.env.ALGOLIA_SECRET_KEY!);
161-
const mainSearchIndex = client.initIndex(algoliaConfig.mainSearchIndexName);
162-
const iconsSearchIndex = client.initIndex(algoliaConfig.iconsSearchIndexName);
163-
const illustrationsSearchIndex = client.initIndex(algoliaConfig.illustrationsSearchIndexName!);
164+
const mainSearchIndex = client.initIndex(algoliaIndexes.mainSearchIndexName);
165+
const iconsSearchIndex = client.initIndex(algoliaIndexes.iconsSearchIndexName);
166+
const illustrationsSearchIndex = client.initIndex(algoliaIndexes.illustrationsSearchIndexName!);
164167

165168
const iconsSearchObjects = iconsList.icons.map((o, i) => ({ objectID: i, ...o }));
166169
const illustrationsSearchObjects = illustrationsList.illustrations.map((o, i) => ({

website/docs/.vitepress/config.ts

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
11
import 'dotenv/config';
22

3+
import { resolve as resolvePath } from 'path';
4+
35
import { defineConfig } from 'vitepress';
6+
7+
import { buildHooks } from './buildHooks';
8+
import { figmaIcon } from './figma-icon';
49
import { configureMarkdownIt } from './markdown-it-config';
5-
import { viteConfig } from './vite.config';
610
import { sideBarConfig } from './sidebarConfig';
7-
import { buildHooks } from './buildHooks';
11+
import { viteConfig, currentBuildVersion, LATEST } from './vite.config';
812
import { algoliaConfig } from '../../algoliaConfig';
9-
import { figmaIcon } from './figma-icon';
10-
import { resolve as resolvePath } from 'path';
13+
import { algoliaIndexes } from '../../algoliaIndexes';
1114

15+
const availableVersions = (process.env.AVAILABLE_VERSIONS ?? '').split(',');
1216
const gtmKey = 'GTM-PP7RKT7';
1317

18+
const links = availableVersions.filter((version) => {
19+
return version !== currentBuildVersion;
20+
});
21+
22+
let versions: any = null;
23+
24+
if (links.length > 0) {
25+
versions = {
26+
currentVersion: currentBuildVersion,
27+
items: links.reverse(),
28+
};
29+
}
30+
1431
// https://vitepress.dev/reference/site-config
1532
export default defineConfig({
16-
base: '/intergalactic/',
17-
outDir: resolvePath(__dirname, 'dist/intergalactic/'),
18-
title: 'Intergalactic',
33+
base: viteConfig.base,
34+
outDir: resolvePath(
35+
__dirname,
36+
`dist/intergalactic/${currentBuildVersion !== LATEST ? `${currentBuildVersion}/` : ''}`,
37+
),
38+
title: 'Intergalactic Design System',
1939
description: 'Design System',
2040
markdown: {
2141
config(md) {
@@ -81,13 +101,17 @@ export default defineConfig({
81101
copyright: 'Copyright © 2023-present Powered by Semrush. All rights reserved.',
82102
},
83103
siteTitle: false,
104+
notFound: {
105+
quote:
106+
'This page may not exist in this version or the URL might be incorrect. Please check your spelling or try a different documentation version.',
107+
},
84108

85109
search: {
86110
provider: 'algolia',
87111
options: {
88112
appId: algoliaConfig.appName,
89113
apiKey: algoliaConfig.openKey,
90-
indexName: algoliaConfig.mainSearchIndexName,
114+
indexName: algoliaIndexes.mainSearchIndexName,
91115
searchParameters: {
92116
attributesToRetrieve: [
93117
'hierarchy',
@@ -103,16 +127,15 @@ export default defineConfig({
103127
},
104128
},
105129
editLink: {
106-
pattern: 'https://github.com/semrush/intergalactic/edit/master/website/docs/:path',
130+
pattern: `https://github.com/semrush/intergalactic/edit/${currentBuildVersion}/website/docs/:path`,
107131
text: 'Edit this page on GitHub',
108132
},
133+
134+
// @ts-ignore. Need this for custom versions switcher
135+
versions,
136+
109137
// https://vitepress.dev/reference/default-theme-config
110138
nav: [
111-
// {
112-
// text: 'Roadmap',
113-
// link: 'https://github.com/orgs/semrush/projects/3/views/2',
114-
// target: '_blank',
115-
// },
116139
{
117140
text: 'Releases',
118141
link: 'https://github.com/semrush/intergalactic/releases',
@@ -131,12 +154,12 @@ export default defineConfig({
131154
{
132155
icon: 'github',
133156
link: 'https://github.com/semrush/intergalactic',
134-
ariaLabel: 'Go to GitHub repository',
157+
ariaLabel: 'GitHub repository',
135158
},
136159
{
137160
icon: { svg: figmaIcon },
138161
link: 'https://figma.com/@semrush',
139-
ariaLabel: 'Go to Intergalactic Design System Figma libraries',
162+
ariaLabel: 'Figma libraries',
140163
},
141164
],
142165
},

website/docs/.vitepress/theme/DevportalLogo.vue

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
<template>
2-
<a href="https://developer.semrush.com/" class="devportal-logo" target="_blank"
3-
aria-label="Go to Semrush Developer portal">
4-
<img src="/devportal-logo.svg" width="30" height="30" class="devportal-logo" />
2+
<a href="https://developer.semrush.com/" class="devportal-logo" target="_blank" aria-label="Developer home page">
3+
<img src="/devportal-logo.svg" width="30" height="30" class="devportal-logo" alt="Semrush Developer portal" />
54
Developer
65
</a>
7-
<a .href="homeUrl" class="title intergalactic-logo" aria-label="Go to Intergalactic Design System main page">
6+
<a :href="link" class="title intergalactic-logo" aria-label="Intergalactic Design System home page">
87
Intergalactic
98
</a>
109
</template>
1110
<script setup lang="ts">
12-
const homeUrl = process.env.NODE_ENV === 'development' ? '/' : '/intergalactic/';
1311
14-
import { useRouter } from 'vitepress';
12+
import { useData, useRouter } from 'vitepress';
1513
import { onMounted, onUnmounted } from 'vue';
1614
import {
1715
initAmplitude, initGlobalEventsHandler, disposeGlobalEventsHandler
1816
} from './amplitude/amplitude'
1917
18+
const latest = import.meta.env.VITE_LATEST;
19+
const { theme } = useData();
20+
const currentVersion = theme.value.versions.currentVersion;
21+
22+
const link = latest === currentVersion ? '/intergalactic/' : `/intergalactic/${currentVersion}/`
23+
2024
onMounted(() => {
2125
const router = useRouter();
2226
initAmplitude();

website/docs/.vitepress/theme/Page404.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<template>
2-
<OriginalNotFoundPate></OriginalNotFoundPate>
2+
<OriginalNotFoundPage></OriginalNotFoundPage>
33
</template>
44

55
<script setup>
6-
import OriginalNotFoundPate from 'vitepress/dist/client/theme-default/NotFound.vue'
6+
import OriginalNotFoundPage from 'vitepress/dist/client/theme-default/NotFound.vue'
77
import { onMounted } from 'vue';
88
import { logEvent } from './amplitude/amplitude';
99

website/docs/.vitepress/theme/VPNavBarMenu.vue

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,60 @@
11
<script lang="ts" setup>
2-
import { useData } from 'vitepress'
2+
import { watch, ref, onMounted, onUnmounted } from 'vue'
3+
import { useData, useRouter } from 'vitepress';
34
import VPNavBarMenuLink from 'vitepress/dist/client/theme-default/components/VPNavBarMenuLink.vue'
45
import VPNavBarMenuGroup from 'vitepress/dist/client/theme-default/components/VPNavBarMenuGroup.vue'
56
import VPFlyout from 'vitepress/dist/client/theme-default/components/VPFlyout.vue'
67
import PreferenceSwitch from './PreferenceSwitch.vue';
78
import { preferSemcoreUi } from './preferences'
89
9-
const { theme } = useData()
10+
const latest = import.meta.env.VITE_LATEST;
11+
const versionSwitcherSettings = ref<null | {text: string, items: any[]}>(null);
12+
13+
const { theme, page } = useData()
14+
const router = useRouter()
15+
16+
const handleHashChange = () => {
17+
versionSwitcherSettings.value = getSwitcher();
18+
};
19+
20+
onMounted(() => {
21+
versionSwitcherSettings.value = getSwitcher();
22+
window.addEventListener('hashchange', handleHashChange);
23+
});
24+
onUnmounted(() => {
25+
window.removeEventListener('hashchange', handleHashChange);
26+
});
27+
28+
watch(() => page.value, () => {
29+
versionSwitcherSettings.value = getSwitcher();
30+
});
31+
32+
function getSwitcher() {
33+
const origin = typeof window !== 'undefined' ? window.location.origin : '';
34+
const hash = typeof window !== 'undefined' ? window.location.hash : '';
35+
const currentVersion = theme.value.versions.currentVersion;
36+
const path = router.route.path.split('/');
37+
const isVersion = /v[0-9]+\.[0-9]+\.[0-9]+/.test(path[2]);
38+
39+
return {
40+
text: currentVersion,
41+
items: theme.value.versions.items.map((v: string) => {
42+
const link = `${origin}/intergalactic/${v === latest ? '' : `${v}/`}${path.slice(isVersion ? 3 : 2).join('/')}${hash}`;
43+
44+
return {
45+
text: v,
46+
target: '_blank',
47+
link,
48+
}
49+
}),
50+
}
51+
}
1052
</script>
1153

1254
<template>
1355
<nav v-if="theme.nav" aria-labelledby="main-nav-aria-label" class="VPNavBarMenu">
1456
<span id="main-nav-aria-label" class="visually-hidden">Main Navigation</span>
57+
<VPNavBarMenuGroup v-if="versionSwitcherSettings" :item="versionSwitcherSettings" />
1558
<template v-for="item in theme.nav" :key="item.text">
1659
<VPNavBarMenuLink v-if="'link' in item" :item="item" />
1760
<VPNavBarMenuGroup v-else :item="item" />
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<script lang="ts" setup>
2+
import VPNavScreenMenuLink from 'vitepress/dist/client/theme-default/components/VPNavScreenMenuLink.vue'
3+
import VPNavScreenMenuGroup from 'vitepress/dist/client/theme-default/components/VPNavScreenMenuGroup.vue'
4+
import VPNavScreenMenuGroupSection from 'vitepress/dist/client/theme-default/components/VPNavScreenMenuGroupSection.vue'
5+
import { watch, ref, onMounted, onUnmounted } from 'vue'
6+
import { useData, useRouter } from 'vitepress';
7+
8+
const latest = import.meta.env.VITE_LATEST;
9+
const versionSwitcherSettings = ref<null | {readonly text: string, readonly items: any[]}>(null);
10+
11+
const { theme, page } = useData()
12+
const router = useRouter()
13+
14+
const handleHashChange = () => {
15+
versionSwitcherSettings.value = getSwitcher();
16+
};
17+
18+
onMounted(() => {
19+
versionSwitcherSettings.value = getSwitcher()
20+
window.addEventListener('hashchange', handleHashChange)
21+
})
22+
onUnmounted(() => {
23+
window.removeEventListener('hashchange', handleHashChange)
24+
})
25+
26+
watch(() => page.value, () => {
27+
versionSwitcherSettings.value = getSwitcher();
28+
});
29+
30+
function getSwitcher() {
31+
const origin = typeof window !== 'undefined' ? window.location.origin : '';
32+
const hash = typeof window !== 'undefined' ? window.location.hash : '';
33+
const currentVersion = theme.value.versions.currentVersion;
34+
const path = router.route.path.split('/');
35+
const isVersion = /v[0-9]+\.[0-9]+\.[0-9]+/.test(path[2]);
36+
37+
return {
38+
text: currentVersion,
39+
items: theme.value.versions.items.map((v: string) => {
40+
const link = `${origin}/intergalactic/${v === latest ? '' : `${v}/`}${path.slice(isVersion ? 3 : 2).join('/')}${hash}`;
41+
42+
return {
43+
text: v,
44+
target: '_blank',
45+
link,
46+
}
47+
}),
48+
}
49+
}
50+
</script>
51+
52+
<template>
53+
<nav v-if="theme.nav" class="VPNavScreenMenu">
54+
<VPNavScreenMenuGroupSection v-if="versionSwitcherSettings" :text="versionSwitcherSettings.text" :items="versionSwitcherSettings.items" />
55+
<template v-for="item in theme.nav" :key="JSON.stringify(item)">
56+
<VPNavScreenMenuLink v-if="'link' in item" :item="item" />
57+
<component
58+
v-else-if="'component' in item"
59+
:is="item.component"
60+
v-bind="item.props"
61+
screen-menu
62+
/>
63+
<VPNavScreenMenuGroup
64+
v-else
65+
:text="item.text || ''"
66+
:items="item.items"
67+
/>
68+
</template>
69+
</nav>
70+
</template>

0 commit comments

Comments
 (0)