feat: add header components and version switcher for docs

This commit is contained in:
manukminasyan
2026-03-27 14:27:35 +04:00
parent 2c7c44ecbc
commit 0c13d589d8
4 changed files with 217 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
<script setup lang="ts">
import { useDocusI18n } from '#imports'
const appConfig = useAppConfig()
const site = useSiteConfig()
const { localePath, isEnabled, locales } = useDocusI18n()
const { currentVersion, isOldVersion, loadVersions } = useVersions()
onMounted(() => loadVersions())
const links = computed(() => appConfig.github && appConfig.github.url
? [
{
'icon': 'i-simple-icons-github',
'to': appConfig.github.url,
'target': '_blank',
'aria-label': 'GitHub',
},
]
: [])
</script>
<template>
<div class="sticky top-0 z-50">
<!-- Version Warning Banner -->
<div
v-if="isOldVersion"
class="bg-amber-100 dark:bg-amber-900/50 text-amber-800 dark:text-amber-200 px-4 py-2 text-center text-sm border-b border-amber-200 dark:border-amber-800"
>
You are viewing documentation for Comments {{ currentVersion }}.
<a
href="/comments/"
class="underline font-medium hover:text-amber-900 dark:hover:text-amber-100"
>
View the latest version &rarr;
</a>
</div>
<!-- Original Docus Header -->
<UHeader
:ui="{ center: 'flex-1' }"
:to="localePath('/')"
:title="appConfig.header?.title || site.name"
>
<AppHeaderCenter />
<template #title>
<AppHeaderLogo class="h-6 w-auto shrink-0" />
</template>
<template #right>
<AppVersionSwitcher />
<AppHeaderCTA />
<template v-if="isEnabled && locales.length > 1">
<ClientOnly>
<LanguageSelect />
<template #fallback>
<div class="h-8 w-8 animate-pulse bg-neutral-200 dark:bg-neutral-800 rounded-md" />
</template>
</ClientOnly>
<USeparator
orientation="vertical"
class="h-8"
/>
</template>
<UContentSearchButton class="lg:hidden" />
<ClientOnly>
<UColorModeButton />
<template #fallback>
<div class="h-8 w-8 animate-pulse bg-neutral-200 dark:bg-neutral-800 rounded-md" />
</template>
</ClientOnly>
<template v-if="links?.length">
<UButton
v-for="(link, index) of links"
:key="index"
v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
/>
</template>
</template>
<template #toggle="{ open, toggle }">
<IconMenuToggle
:open="open"
class="lg:hidden"
@click="toggle"
/>
</template>
<template #body>
<AppHeaderBody />
</template>
</UHeader>
</div>
</template>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
const appConfig = useAppConfig()
</script>
<template>
<UColorModeImage
v-if="appConfig.docus?.header?.logo?.dark || appConfig.docus?.header?.logo?.light"
:light="appConfig.docus?.header?.logo?.light || appConfig.docus?.header?.logo?.dark"
:dark="appConfig.docus?.header?.logo?.dark || appConfig.docus?.header?.logo?.light"
:alt="appConfig.docus?.header?.logo?.alt || appConfig.docus?.title"
class="h-8 w-auto shrink-0"
/>
<span v-else class="text-lg font-semibold">
{{ appConfig.docus?.title || 'Comments' }}
</span>
</template>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
const { versions, currentVersion, currentTitle, loadVersions } = useVersions()
onMounted(() => loadVersions())
function switchVersion(version: { version: string; path: string }): void {
if (version.version !== currentVersion) {
window.location.href = version.path
}
}
</script>
<template>
<div v-if="versions.length > 1" class="relative" @click.stop>
<UPopover>
<UButton
variant="ghost"
size="sm"
:label="currentTitle"
trailing-icon="i-lucide-chevron-down"
/>
<template #content>
<div class="p-1">
<button
v-for="version in versions"
:key="version.version"
class="w-full px-3 py-2 text-left text-sm rounded hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center gap-2"
:class="{ 'font-medium text-primary': version.version === currentVersion }"
@click="switchVersion(version)"
>
{{ version.title }}
</button>
</div>
</template>
</UPopover>
</div>
<UBadge v-else-if="currentVersion" variant="subtle" color="neutral">{{ currentVersion }}</UBadge>
</template>

View File

@@ -0,0 +1,60 @@
interface Version {
version: string
title: string
path: string
branch: string
isLatest: boolean
}
const versions = ref<Version[]>([])
const isLoaded = ref(false)
const isLoading = ref(false)
export function useVersions() {
const config = useRuntimeConfig()
const currentVersion = config.public.docsVersion || '1.x'
async function loadVersions() {
if (isLoaded.value || isLoading.value) return
isLoading.value = true
try {
const res = await fetch('/comments/versions.json')
if (res.ok) {
versions.value = await res.json()
}
} catch (e) {
console.warn('Failed to load versions.json:', e)
} finally {
isLoaded.value = true
isLoading.value = false
}
}
const latestVersion = computed(() =>
versions.value.find(v => v.isLatest)
)
const currentVersionInfo = computed(() =>
versions.value.find(v => v.version === currentVersion)
)
const isOldVersion = computed(() => {
if (!isLoaded.value) return false
return currentVersionInfo.value?.isLatest === false
})
const currentTitle = computed(() =>
currentVersionInfo.value?.title || currentVersion
)
return {
versions,
currentVersion,
currentTitle,
latestVersion,
isOldVersion,
isLoaded,
loadVersions,
}
}