chore: tweak
This commit is contained in:
parent
3b0208b356
commit
86a50e9601
@ -1,237 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { useWindowScroll } from '@vueuse/core'
|
||||
import { ref, watchPostEffect } from 'vue'
|
||||
import { useSidebar } from '../../composables/sidebar.js'
|
||||
import { useData } from '../../composables/data.js'
|
||||
import NavBarAppearance from './NavBarAppearance.vue'
|
||||
import NavBarExtra from './NavBarExtra.vue'
|
||||
import NavBarHamburger from './NavBarHamburger.vue'
|
||||
import NavBarMenu from './NavBarMenu.vue'
|
||||
import NavBarSearch from './NavBarSearch.vue'
|
||||
import NavBarSocialLinks from './NavBarSocialLinks.vue'
|
||||
import NavBarTitle from './NavBarTitle.vue'
|
||||
import NavBarTranslations from './NavBarTranslations.vue'
|
||||
|
||||
defineProps<{
|
||||
isScreenOpen: boolean
|
||||
}>()
|
||||
defineEmits<(e: 'toggleScreen') => void>()
|
||||
|
||||
const { frontmatter } = useData()
|
||||
|
||||
const { y } = useWindowScroll()
|
||||
const { hasSidebar } = useSidebar()
|
||||
|
||||
const classes = ref<Record<string, boolean>>({})
|
||||
watchPostEffect(() => {
|
||||
classes.value = {
|
||||
'has-sidebar': hasSidebar.value,
|
||||
'top': frontmatter.value.pageLayout === 'home' && y.value === 0,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="navbar-wrapper" :class="classes">
|
||||
<div class="container">
|
||||
<div class="title">
|
||||
<NavBarTitle />
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="curtain" />
|
||||
<div class="content-body">
|
||||
<NavBarSearch class="search" />
|
||||
<NavBarMenu class="menu" />
|
||||
<NavBarTranslations class="translations" />
|
||||
<NavBarAppearance class="appearance" />
|
||||
<NavBarSocialLinks class="social-links" />
|
||||
<NavBarExtra class="extra" />
|
||||
<NavBarHamburger
|
||||
class="hamburger"
|
||||
:active="isScreenOpen"
|
||||
@click="$emit('toggleScreen')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.navbar-wrapper {
|
||||
position: relative;
|
||||
height: var(--vp-nav-height);
|
||||
padding: 0 8px 0 24px;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
border-bottom: 1px solid transparent;
|
||||
transition: var(--t-color);
|
||||
transition-property: background-color, color, border-bottom;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-wrapper {
|
||||
padding: 0 32px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navbar-wrapper.has-sidebar {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navbar-wrapper:not(.has-sidebar, .top) {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
border-bottom-color: var(--vp-c-gutter);
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
max-width: calc(var(--vp-layout-max-width) - 64px);
|
||||
height: var(--vp-nav-height);
|
||||
margin: 0 auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.container :deep(*) {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navbar-wrapper.has-sidebar .container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
flex-shrink: 0;
|
||||
height: calc(var(--vp-nav-height) - 1px);
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navbar-wrapper.has-sidebar .title {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
width: var(--vp-sidebar-width);
|
||||
height: var(--vp-nav-height);
|
||||
padding: 0 32px;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.navbar-wrapper.has-sidebar .title {
|
||||
width:
|
||||
calc(
|
||||
(100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) -
|
||||
32px
|
||||
);
|
||||
padding-left:
|
||||
max(
|
||||
32px,
|
||||
calc((100% - (var(--vp-layout-max-width) - 64px)) / 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navbar-wrapper.has-sidebar .content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-right: 32px;
|
||||
padding-left: var(--vp-sidebar-width);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.navbar-wrapper.has-sidebar .content {
|
||||
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);
|
||||
padding-left:
|
||||
calc(
|
||||
(100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.content-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: calc(var(--vp-nav-height) - 1px);
|
||||
transition: var(--t-color);
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navbar-wrapper:not(.top) .content-body {
|
||||
position: relative;
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.content-body {
|
||||
column-gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.menu + .translations::before,
|
||||
.menu + .appearance::before,
|
||||
.menu + .social-links::before,
|
||||
.translations + .appearance::before,
|
||||
.translations + .social-links::before,
|
||||
.appearance + .social-links::before {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
margin-left: 8px;
|
||||
content: "";
|
||||
background-color: var(--vp-c-divider);
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
.menu + .appearance::before,
|
||||
.translations + .appearance::before {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.appearance + .social-links::before {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navbar-wrapper.has-sidebar .curtain {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: calc(100% - var(--vp-sidebar-width));
|
||||
height: 0;
|
||||
border-bottom: solid 1px var(--vp-c-divider);
|
||||
transition: border-bottom var(--t-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.navbar-wrapper.has-sidebar .curtain {
|
||||
width:
|
||||
calc(
|
||||
100% -
|
||||
((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))
|
||||
);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
86
theme/src/client/components/Nav/VPNav.vue
Normal file
86
theme/src/client/components/Nav/VPNav.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, provide, watchEffect } from 'vue'
|
||||
import { useNav } from '../../composables/nav.js'
|
||||
import { useData } from '../../composables/data.js'
|
||||
import { inBrowser } from '../../utils/index.js'
|
||||
import VPNavbar from './VPNavBar.vue'
|
||||
import VPNavScreen from './VPNavScreen.vue'
|
||||
|
||||
const { page, frontmatter } = useData()
|
||||
|
||||
const { isScreenOpen, closeScreen, toggleScreen } = useNav()
|
||||
|
||||
const fixedInclude = ['blog', 'friends', 'blog-archives', 'blog-tags']
|
||||
|
||||
const fixed = computed(() => {
|
||||
return fixedInclude.includes(page.value.type as string)
|
||||
})
|
||||
|
||||
const hasNavbar = computed(() => {
|
||||
return frontmatter.value.navbar !== false
|
||||
})
|
||||
|
||||
provide('close-screen', closeScreen)
|
||||
|
||||
watchEffect(() => {
|
||||
if (inBrowser) {
|
||||
document.documentElement.classList.toggle('hide-nav', !hasNavbar.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-nav" :class="{ fixed }">
|
||||
<VPNavbar :is-screen-open="isScreenOpen" @toggle-screen="toggleScreen">
|
||||
<template #nav-bar-title-before>
|
||||
<slot name="nav-bar-title-before" />
|
||||
</template>
|
||||
<template #nav-bar-title-after>
|
||||
<slot name="nav-bar-title-after" />
|
||||
</template>
|
||||
<template #nav-bar-content-before>
|
||||
<slot name="nav-bar-content-before" />
|
||||
</template>
|
||||
<template #nav-bar-content-after>
|
||||
<slot name="nav-bar-content-after" />
|
||||
</template>
|
||||
</VPNavbar>
|
||||
|
||||
<VPNavScreen :open="isScreenOpen">
|
||||
<template #nav-screen-content-before>
|
||||
<slot name="nav-screen-content-before" />
|
||||
</template>
|
||||
<template #nav-screen-content-after>
|
||||
<slot name="nav-screen-content-after" />
|
||||
</template>
|
||||
</VPNavScreen>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vp-nav {
|
||||
position: relative;
|
||||
top: var(--vp-layout-top-height, 0);
|
||||
|
||||
/* rtl:ignore */
|
||||
left: 0;
|
||||
z-index: var(--vp-z-index-nav);
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.vp-nav.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.vp-nav.fixed :deep(.vp-navbar) {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
border-bottom-color: var(--vp-c-gutter);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-nav {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
268
theme/src/client/components/Nav/VPNavBar.vue
Normal file
268
theme/src/client/components/Nav/VPNavBar.vue
Normal file
@ -0,0 +1,268 @@
|
||||
<script lang="ts" setup>
|
||||
import { useWindowScroll } from '@vueuse/core'
|
||||
import { ref, watchPostEffect } from 'vue'
|
||||
import { useSidebar } from '../../composables/sidebar.js'
|
||||
import { useData } from '../../composables/data.js'
|
||||
import VPNavBarAppearance from './VPNavBarAppearance.vue'
|
||||
import VPNavBarExtra from './VPNavBarExtra.vue'
|
||||
import VPNavBarHamburger from './VPNavBarHamburger.vue'
|
||||
import VPNavBarMenu from './VPNavBarMenu.vue'
|
||||
import VPNavBarSearch from './VPNavBarSearch.vue'
|
||||
import VPNavBarSocialLinks from './VPNavBarSocialLinks.vue'
|
||||
import VPNavBarTitle from './VPNavBarTitle.vue'
|
||||
import VPNavBarTranslations from './VPNavBarTranslations.vue'
|
||||
|
||||
defineProps<{
|
||||
isScreenOpen: boolean
|
||||
}>()
|
||||
defineEmits<(e: 'toggleScreen') => void>()
|
||||
|
||||
const { frontmatter } = useData()
|
||||
|
||||
const { y } = useWindowScroll()
|
||||
const { hasSidebar } = useSidebar()
|
||||
|
||||
const classes = ref<Record<string, boolean>>({})
|
||||
watchPostEffect(() => {
|
||||
classes.value = {
|
||||
'has-sidebar': hasSidebar.value,
|
||||
'home': frontmatter.value.pageLayout === 'home',
|
||||
'top': y.value === 0,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-navbar" :class="classes">
|
||||
<div class="wrapper">
|
||||
<div class="container">
|
||||
<div class="title">
|
||||
<VPNavBarTitle>
|
||||
<template #nav-bar-title-before>
|
||||
<slot name="nav-bar-title-before" />
|
||||
</template>
|
||||
<template #nav-bar-title-after>
|
||||
<slot name="nav-bar-title-after" />
|
||||
</template>
|
||||
</VPNavBarTitle>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="content-body">
|
||||
<slot name="nav-bar-content-before" />
|
||||
<VPNavBarSearch class="search" />
|
||||
<VPNavBarMenu class="menu" />
|
||||
<VPNavBarTranslations class="translations" />
|
||||
<VPNavBarAppearance class="appearance" />
|
||||
<VPNavBarSocialLinks class="social-links" />
|
||||
<VPNavBarExtra class="extra" />
|
||||
<slot name="nav-bar-content-after" />
|
||||
<VPNavBarHamburger
|
||||
class="hamburger"
|
||||
:active="isScreenOpen"
|
||||
@click="$emit('toggleScreen')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider">
|
||||
<div class="divider-line" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vp-navbar {
|
||||
position: relative;
|
||||
height: var(--vp-nav-height);
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
transition: var(--t-color);
|
||||
transition-property: background-color, color, border-bottom;
|
||||
}
|
||||
|
||||
.vp-navbar:not(.home) {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar:not(.home) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.vp-navbar:not(.has-sidebar, .home.top) {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
padding: 0 8px 0 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.wrapper {
|
||||
padding: 0 32px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar.has-sidebar .wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
max-width: calc(var(--vp-layout-max-width) - 64px);
|
||||
height: var(--vp-nav-height);
|
||||
margin: 0 auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex-shrink: 0;
|
||||
height: calc(var(--vp-nav-height) - 1px);
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
.container > .title,
|
||||
.container > .content {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.container :deep(*) {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar.has-sidebar .container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar.has-sidebar .title {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
width: var(--vp-sidebar-width);
|
||||
height: var(--vp-nav-height);
|
||||
padding: 0 32px;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.vp-navbar.has-sidebar .title {
|
||||
width: calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px);
|
||||
padding-left:
|
||||
max(32px,
|
||||
calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar.has-sidebar .content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-right: 32px;
|
||||
padding-left: var(--vp-sidebar-width);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.vp-navbar.has-sidebar .content {
|
||||
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);
|
||||
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
|
||||
}
|
||||
}
|
||||
|
||||
.content-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
height: var(--vp-nav-height);
|
||||
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar:not(.home.top) .content-body {
|
||||
position: relative;
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
}
|
||||
|
||||
.vp-navbar:not(.has-sidebar, .home.top) .content-body {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.content-body {
|
||||
column-gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.menu + .translations::before,
|
||||
.menu + .appearance::before,
|
||||
.menu + .social-links::before,
|
||||
.translations + .appearance::before,
|
||||
.translations + .social-links::before,
|
||||
.appearance + .social-links::before {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
margin-left: 8px;
|
||||
content: "";
|
||||
background-color: var(--vp-c-divider);
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
.menu + .appearance::before,
|
||||
.translations + .appearance::before {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.appearance + .social-links::before {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.vp-navbar.has-sidebar .divider {
|
||||
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
|
||||
}
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
.vp-navbar:not(.home) .divider-line {
|
||||
background-color: var(--vp-c-gutter);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar:not(.home.top) .divider-line {
|
||||
background-color: var(--vp-c-gutter);
|
||||
}
|
||||
|
||||
.vp-navbar:not(.has-sidebar, .home.top) .divider {
|
||||
background-color: var(--vp-c-gutter);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -8,19 +8,19 @@ const { theme } = useData()
|
||||
<template>
|
||||
<div
|
||||
v-if="theme.appearance && theme.appearance !== 'force-dark'"
|
||||
class="navbar-appearance"
|
||||
class="vp-navbar-appearance"
|
||||
>
|
||||
<VPSwitchAppearance />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.navbar-appearance {
|
||||
.vp-navbar-appearance {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.navbar-appearance {
|
||||
.vp-navbar-appearance {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@ -10,10 +10,6 @@ import VPSwitchAppearance from '../VPSwitchAppearance.vue'
|
||||
const { theme } = useData()
|
||||
const { localeLinks, currentLang } = useLangs()
|
||||
|
||||
const hasExtraContent = computed(
|
||||
() => theme.value.appearance || theme.value.social,
|
||||
)
|
||||
|
||||
const social = computed(() => {
|
||||
const includes = theme.value.navbarSocialInclude ?? []
|
||||
if (!includes.length)
|
||||
@ -23,10 +19,21 @@ const social = computed(() => {
|
||||
({ icon }) => typeof icon === 'string' && includes.includes(icon),
|
||||
)
|
||||
})
|
||||
|
||||
const hasExtraContent = computed(
|
||||
() =>
|
||||
(localeLinks.value.length && currentLang.value.label)
|
||||
|| theme.value.appearance
|
||||
|| social.value?.length,
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPFlyout v-if="hasExtraContent" class="navbar-extra" label="extra navigation">
|
||||
<VPFlyout
|
||||
v-if="hasExtraContent"
|
||||
class="vp-navbar-extra"
|
||||
label="extra navigation"
|
||||
>
|
||||
<div
|
||||
v-if="localeLinks.length && currentLang.label"
|
||||
class="group translations"
|
||||
@ -60,19 +67,19 @@ const social = computed(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.navbar-extra {
|
||||
.vp-navbar-extra {
|
||||
display: none;
|
||||
margin-right: -12px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-extra {
|
||||
.vp-navbar-extra {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.navbar-extra {
|
||||
.vp-navbar-extra {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ defineEmits<(e: 'click') => void>()
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="navbar-hamburger"
|
||||
class="vp-navbar-hamburger"
|
||||
:class="{ active }"
|
||||
aria-label="mobile navigation"
|
||||
:aria-expanded="active"
|
||||
@ -25,7 +25,7 @@ defineEmits<(e: 'click') => void>()
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.navbar-hamburger {
|
||||
.vp-navbar-hamburger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -34,7 +34,7 @@ defineEmits<(e: 'click') => void>()
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-hamburger {
|
||||
.vp-navbar-hamburger {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -77,42 +77,42 @@ defineEmits<(e: 'click') => void>()
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.navbar-hamburger:hover .top {
|
||||
.vp-navbar-hamburger:hover .top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.navbar-hamburger:hover .middle {
|
||||
.vp-navbar-hamburger:hover .middle {
|
||||
top: 6px;
|
||||
left: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.navbar-hamburger:hover .bottom {
|
||||
.vp-navbar-hamburger:hover .bottom {
|
||||
top: 12px;
|
||||
left: 0;
|
||||
transform: translateX(8px);
|
||||
}
|
||||
|
||||
.navbar-hamburger.active .top {
|
||||
.vp-navbar-hamburger.active .top {
|
||||
top: 6px;
|
||||
transform: translateX(0) rotate(225deg);
|
||||
}
|
||||
|
||||
.navbar-hamburger.active .middle {
|
||||
.vp-navbar-hamburger.active .middle {
|
||||
top: 6px;
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
.navbar-hamburger.active .bottom {
|
||||
.vp-navbar-hamburger.active .bottom {
|
||||
top: 6px;
|
||||
transform: translateX(0) rotate(135deg);
|
||||
}
|
||||
|
||||
.navbar-hamburger.active:hover .top,
|
||||
.navbar-hamburger.active:hover .middle,
|
||||
.navbar-hamburger.active:hover .bottom {
|
||||
.vp-navbar-hamburger.active:hover .top,
|
||||
.vp-navbar-hamburger.active:hover .middle,
|
||||
.vp-navbar-hamburger.active:hover .bottom {
|
||||
background-color: var(--vp-c-text-2);
|
||||
transition:
|
||||
top 0.25s,
|
||||
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { useData } from '../../composables/data.js'
|
||||
import NavBarMenuGroup from './NavBarMenuGroup.vue'
|
||||
import NavBarMenuLink from './NavBarMenuLink.vue'
|
||||
import VPNavBarMenuGroup from './VPNavBarMenuGroup.vue'
|
||||
import VPNavBarMenuLink from './VPNavBarMenuLink.vue'
|
||||
|
||||
const { theme } = useData()
|
||||
</script>
|
||||
@ -10,23 +10,23 @@ const { theme } = useData()
|
||||
<nav
|
||||
v-if="theme.navbar"
|
||||
aria-labelledby="main-nav-aria-label"
|
||||
class="navbar-menu"
|
||||
class="vp-navbar-menu"
|
||||
>
|
||||
<span id="main-nav-aria-label" class="visually-hidden">Main Navigation</span>
|
||||
<template v-for="item in theme.navbar" :key="item.text">
|
||||
<NavBarMenuLink v-if="'link' in item" :item="item" />
|
||||
<NavBarMenuGroup v-else :item="item" />
|
||||
<VPNavBarMenuLink v-if="'link' in item" :item="item" />
|
||||
<VPNavBarMenuGroup v-else :item="item" />
|
||||
</template>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.navbar-menu {
|
||||
.vp-navbar-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-menu {
|
||||
.vp-navbar-menu {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { resolveRouteFullPath } from 'vuepress/client'
|
||||
import type { NavItem, NavItemWithChildren } from '../../../shared/index.js'
|
||||
import { isActive } from '../../utils/index.js'
|
||||
import VPFlyout from '../VPFlyout.vue'
|
||||
@ -15,7 +16,7 @@ function isChildActive(navItem: NavItem) {
|
||||
if ('link' in navItem) {
|
||||
return isActive(
|
||||
page.value.path,
|
||||
navItem.link,
|
||||
resolveRouteFullPath(navItem.link),
|
||||
!!props.item.activeMatch,
|
||||
)
|
||||
}
|
||||
@ -28,8 +29,11 @@ const childrenActive = computed(() => isChildActive(props.item))
|
||||
|
||||
<template>
|
||||
<VPFlyout
|
||||
class="navbar-menu-group" :class="{
|
||||
active: isActive(page.path, item.activeMatch, !!item.activeMatch) || childrenActive,
|
||||
class="vp-navbar-menu-group" :class="{
|
||||
active: isActive(
|
||||
page.path, item.activeMatch,
|
||||
!!item.activeMatch,
|
||||
) || childrenActive,
|
||||
}"
|
||||
:button="item.text"
|
||||
:items="item.items"
|
||||
@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { resolveRoutePath } from 'vuepress/client'
|
||||
import type { NavItemWithLink } from '../../../shared/index.js'
|
||||
import { isActive } from '../../utils/index.js'
|
||||
import VPLink from '../VPLink.vue'
|
||||
@ -17,15 +18,18 @@ const { page } = useData()
|
||||
class="navbar-menu-link" :class="{
|
||||
active: isActive(
|
||||
page.path,
|
||||
item.activeMatch || item.link,
|
||||
item.activeMatch || resolveRoutePath(item.link),
|
||||
!!item.activeMatch,
|
||||
),
|
||||
}"
|
||||
:href="item.link"
|
||||
:no-icon="true"
|
||||
no-icon
|
||||
:target="item.target"
|
||||
:rel="item.rel"
|
||||
tabindex="0"
|
||||
>
|
||||
<VPIcon v-if="item.icon" :name="item.icon" />
|
||||
<i v-text="item.text" />
|
||||
<span v-html="item.text" />
|
||||
</VPLink>
|
||||
</template>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="navbar-search">
|
||||
<div class="vp-navbar-search">
|
||||
<DocSearch />
|
||||
</div>
|
||||
</template>
|
||||
@ -19,18 +19,18 @@ const social = computed(() => {
|
||||
<template>
|
||||
<VPSocialLinks
|
||||
v-if="social"
|
||||
class="navbar-social-links"
|
||||
class="vp-navbar-social-links"
|
||||
:links="social"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.navbar-social-links {
|
||||
.vp-navbar-social-links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.navbar-social-links {
|
||||
.vp-navbar-social-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@ -11,14 +11,18 @@ const routeLocale = useRouteLocale()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="navbar-title" :class="{ 'has-sidebar': hasSidebar }">
|
||||
<div class="vp-navbar-title" :class="{ 'has-sidebar': hasSidebar }">
|
||||
<VPLink class="title" :href="theme.home ?? routeLocale">
|
||||
<slot name="nav-bar-title-before" />
|
||||
|
||||
<VPImage
|
||||
v-if="theme.logo"
|
||||
class="logo"
|
||||
:image="{ light: theme.logo, dark: theme.logoDark || theme.logo }"
|
||||
/>
|
||||
{{ site.title }}
|
||||
<span>{{ site.title }}</span>
|
||||
|
||||
<slot name="nav-bar-title-after" />
|
||||
</VPLink>
|
||||
</div>
|
||||
</template>
|
||||
@ -36,16 +40,12 @@ const routeLocale = useRouteLocale()
|
||||
transition: opacity var(--t-color), color var(--t-color), border-bottom var(--t-color);
|
||||
}
|
||||
|
||||
.title:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.title {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.navbar-title.has-sidebar .title {
|
||||
.vp-navbar-title.has-sidebar .title {
|
||||
border-bottom-color: var(--vp-c-divider);
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ const { currentLang, localeLinks } = useLangs()
|
||||
<template>
|
||||
<VPFlyout
|
||||
v-if="localeLinks.length && currentLang.label"
|
||||
class="navbar-translations"
|
||||
class="vp-navbar-translations"
|
||||
icon="vpi-languages"
|
||||
:label="theme.selectLanguageText || 'Change Language'"
|
||||
>
|
||||
@ -28,12 +28,12 @@ const { currentLang, localeLinks } = useLangs()
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar-translations {
|
||||
.vp-navbar-translations {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.navbar-translations {
|
||||
.vp-navbar-translations {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { useScrollLock } from '@vueuse/core'
|
||||
import { inBrowser } from '../../utils/index.js'
|
||||
import NavScreenAppearance from './NavScreenAppearance.vue'
|
||||
import NavScreenMenu from './NavScreenMenu.vue'
|
||||
import NavScreenSocialLinks from './NavScreenSocialLinks.vue'
|
||||
import NavScreenTranslates from './NavScreenTranslations.vue'
|
||||
import VPNavScreenAppearance from './VPNavScreenAppearance.vue'
|
||||
import VPNavScreenMenu from './VPNavScreenMenu.vue'
|
||||
import VPNavScreenSocialLinks from './VPNavScreenSocialLinks.vue'
|
||||
import VPNavScreenTranslates from './VPNavScreenTranslations.vue'
|
||||
|
||||
defineProps<{
|
||||
open: boolean
|
||||
@ -19,25 +19,30 @@ const isLocked = useScrollLock(inBrowser ? document.body : null)
|
||||
@enter="isLocked = true"
|
||||
@after-leave="isLocked = false"
|
||||
>
|
||||
<div v-if="open" id="navScreen" class="nav-screen">
|
||||
<div v-if="open" id="navScreen" class="vp-nav-screen">
|
||||
<div class="container">
|
||||
<NavScreenMenu class="menu" />
|
||||
<NavScreenTranslates class="translations" />
|
||||
<NavScreenAppearance class="appearance" />
|
||||
<NavScreenSocialLinks class="social-links" />
|
||||
<slot name="nav-screen-content-before" />
|
||||
<VPNavScreenMenu class="menu" />
|
||||
<VPNavScreenTranslates class="translations" />
|
||||
<VPNavScreenAppearance class="appearance" />
|
||||
<VPNavScreenSocialLinks class="social-links" />
|
||||
<slot name="nav-screen-content-after" />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav-screen {
|
||||
.vp-nav-screen {
|
||||
position: fixed;
|
||||
inset: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) 0 0;
|
||||
top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 1px);
|
||||
|
||||
/* rtl:ignore */
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
/* rtl:ignore */
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0 32px;
|
||||
overflow-y: auto;
|
||||
@ -53,28 +58,28 @@ const isLocked = useScrollLock(inBrowser ? document.body : null)
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.nav-screen.fade-enter-active,
|
||||
.nav-screen.fade-leave-active {
|
||||
.vp-nav-screen.fade-enter-active,
|
||||
.vp-nav-screen.fade-leave-active {
|
||||
transition: opacity var(--t-color);
|
||||
}
|
||||
|
||||
.nav-screen.fade-enter-active .container,
|
||||
.nav-screen.fade-leave-active .container {
|
||||
.vp-nav-screen.fade-enter-active .container,
|
||||
.vp-nav-screen.fade-leave-active .container {
|
||||
transition: transform var(--t-color);
|
||||
}
|
||||
|
||||
.nav-screen.fade-enter-from,
|
||||
.nav-screen.fade-leave-to {
|
||||
.vp-nav-screen.fade-enter-from,
|
||||
.vp-nav-screen.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nav-screen.fade-enter-from .container,
|
||||
.nav-screen.fade-leave-to .container {
|
||||
.vp-nav-screen.fade-enter-from .container,
|
||||
.vp-nav-screen.fade-leave-to .container {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.nav-screen {
|
||||
.vp-nav-screen {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ const { theme } = useData()
|
||||
<template>
|
||||
<div
|
||||
v-if="theme.appearance && theme.appearance !== 'force-dark'"
|
||||
class="nav-screen-appearance"
|
||||
class="vp-nav-screen-appearance"
|
||||
>
|
||||
<p class="text">
|
||||
{{ theme.appearanceText ?? 'Appearance' }}
|
||||
@ -18,7 +18,7 @@ const { theme } = useData()
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav-screen-appearance {
|
||||
.vp-nav-screen-appearance {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@ -1,21 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { useData } from '../../composables/data.js'
|
||||
import NavScreenMenuGroup from './NavScreenMenuGroup.vue'
|
||||
import NavScreenMenuLink from './NavScreenMenuLink.vue'
|
||||
import VPNavScreenMenuGroup from './VPNavScreenMenuGroup.vue'
|
||||
import VPNavScreenMenuLink from './VPNavScreenMenuLink.vue'
|
||||
|
||||
const { theme } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-if="theme.navbar" class="nav-screen-menu">
|
||||
<nav v-if="theme.navbar" class="vp-nav-screen-menu">
|
||||
<template v-for="item in theme.navbar" :key="item.text">
|
||||
<NavScreenMenuLink
|
||||
<VPNavScreenMenuLink
|
||||
v-if="'link' in item"
|
||||
:text="item.text"
|
||||
:link="item.link"
|
||||
:icon="item.icon"
|
||||
/>
|
||||
<NavScreenMenuGroup
|
||||
<VPNavScreenMenuGroup
|
||||
v-else
|
||||
:text="item.text || ''"
|
||||
:items="item.items"
|
||||
@ -1,8 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import VPIcon from '../VPIcon.vue'
|
||||
import NavScreenMenuGroupLink from './NavScreenMenuGroupLink.vue'
|
||||
import NavScreenMenuGroupSection from './NavScreenMenuGroupSection.vue'
|
||||
import VPNavScreenMenuGroupLink from './VPNavScreenMenuGroupLink.vue'
|
||||
import VPNavScreenMenuGroupSection from './VPNavScreenMenuGroupSection.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
text: string
|
||||
@ -22,7 +22,7 @@ function toggle() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="nav-screen-menu-group" :class="{ open: isOpen }">
|
||||
<div class="vp-nav-screen-menu-group" :class="{ open: isOpen }">
|
||||
<button
|
||||
class="button"
|
||||
:aria-controls="groupId"
|
||||
@ -31,7 +31,7 @@ function toggle() {
|
||||
>
|
||||
<span class="button-text">
|
||||
<VPIcon v-if="icon" :name="icon" />
|
||||
<i v-text="text" />
|
||||
<span v-html="text" />
|
||||
</span>
|
||||
<span class="vpi-plus button-icon" />
|
||||
</button>
|
||||
@ -39,7 +39,7 @@ function toggle() {
|
||||
<div :id="groupId" class="items">
|
||||
<template v-for="item in items" :key="item.text">
|
||||
<div v-if="'link' in item" :key="item.text" class="item">
|
||||
<NavScreenMenuGroupLink
|
||||
<VPNavScreenMenuGroupLink
|
||||
:text="item.text"
|
||||
:link="item.link"
|
||||
:icon="item.icon"
|
||||
@ -47,7 +47,7 @@ function toggle() {
|
||||
</div>
|
||||
|
||||
<div v-else class="group">
|
||||
<NavScreenMenuGroupSection
|
||||
<VPNavScreenMenuGroupSection
|
||||
:text="item.text"
|
||||
:items="item.items"
|
||||
:icon="item.icon"
|
||||
@ -59,22 +59,22 @@ function toggle() {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav-screen-menu-group {
|
||||
.vp-nav-screen-menu-group {
|
||||
height: 48px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
transition: border-color var(--t-color);
|
||||
}
|
||||
|
||||
.nav-screen-menu-group .items {
|
||||
.vp-nav-screen-menu-group .items {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.nav-screen-menu-group.open .items {
|
||||
.vp-nav-screen-menu-group.open .items {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.nav-screen-menu-group.open {
|
||||
.vp-nav-screen-menu-group.open {
|
||||
height: auto;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
@ -96,7 +96,7 @@ function toggle() {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.nav-screen-menu-group.open .button {
|
||||
.vp-nav-screen-menu-group.open .button {
|
||||
padding-bottom: 6px;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
@ -110,15 +110,11 @@ function toggle() {
|
||||
transform 0.25s;
|
||||
}
|
||||
|
||||
.nav-screen-menu-group.open .button-icon {
|
||||
.vp-nav-screen-menu-group.open .button-icon {
|
||||
/* rtl:ignore */
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.button-text i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.group:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
@ -14,7 +14,7 @@ const closeScreen = inject('close-screen') as () => void
|
||||
|
||||
<template>
|
||||
<VPLink
|
||||
class="nav-screen-menu-group-link"
|
||||
class="vp-nav-screen-menu-group-link"
|
||||
:href="link"
|
||||
@click="closeScreen"
|
||||
>
|
||||
@ -24,7 +24,7 @@ const closeScreen = inject('close-screen') as () => void
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav-screen-menu-group-link {
|
||||
.vp-nav-screen-menu-group-link {
|
||||
display: block;
|
||||
margin-left: 12px;
|
||||
font-size: 14px;
|
||||
@ -34,7 +34,7 @@ const closeScreen = inject('close-screen') as () => void
|
||||
transition: color var(--t-color);
|
||||
}
|
||||
|
||||
.nav-screen-menu-group-link:hover {
|
||||
.vp-nav-screen-menu-group-link:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
</style>
|
||||
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { NavItemWithLink } from '../../../shared/index.js'
|
||||
import VPIcon from '../VPIcon.vue'
|
||||
import NavScreenMenuGroupLink from './NavScreenMenuGroupLink.vue'
|
||||
import VPNavScreenMenuGroupLink from './VPNavScreenMenuGroupLink.vue'
|
||||
|
||||
defineProps<{
|
||||
icon?: string | { svg: string }
|
||||
@ -11,12 +11,12 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="nav-screen-menu-group-section">
|
||||
<div class="vp-nav-screen-menu-group-section">
|
||||
<p v-if="text" class="title">
|
||||
<VPIcon v-if="icon" :name="icon" />
|
||||
{{ text }}
|
||||
</p>
|
||||
<NavScreenMenuGroupLink
|
||||
<VPNavScreenMenuGroupLink
|
||||
v-for="item in items"
|
||||
:key="item.text"
|
||||
:text="item.text"
|
||||
@ -27,7 +27,7 @@ defineProps<{
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav-screen-menu-group-section {
|
||||
.vp-nav-screen-menu-group-section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@ -13,14 +13,14 @@ const closeScreen = inject('close-screen') as () => void
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPLink class="nav-screen-menu-link" :href="link" @click="closeScreen">
|
||||
<VPLink class="vp-nav-screen-menu-link" :href="link" @click="closeScreen">
|
||||
<VPIcon v-if="icon" :name="icon" />
|
||||
<i v-text="text" />
|
||||
</VPLink>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav-screen-menu-link {
|
||||
.vp-nav-screen-menu-link {
|
||||
display: block;
|
||||
padding: 12px 0 11px;
|
||||
font-size: 14px;
|
||||
@ -33,7 +33,7 @@ const closeScreen = inject('close-screen') as () => void
|
||||
color var(--t-color);
|
||||
}
|
||||
|
||||
.nav-screen-menu-link:hover {
|
||||
.vp-nav-screen-menu-link:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
</style>
|
||||
@ -8,7 +8,7 @@ const { theme } = useData()
|
||||
<template>
|
||||
<VPSocialLinks
|
||||
v-if="theme.social"
|
||||
class="VPNavScreenSocialLinks"
|
||||
class="vp-nav-screen-social-links"
|
||||
:links="theme.social"
|
||||
/>
|
||||
</template>
|
||||
@ -14,7 +14,7 @@ function toggle() {
|
||||
<template>
|
||||
<div
|
||||
v-if="localeLinks.length && currentLang.label"
|
||||
class="nav-screen-translations"
|
||||
class="vp-nav-screen-translations"
|
||||
:class="{ open: isOpen }"
|
||||
>
|
||||
<button class="title" @click="toggle">
|
||||
@ -34,12 +34,12 @@ function toggle() {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-screen-translations {
|
||||
.vp-nav-screen-translations {
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-screen-translations.open {
|
||||
.vp-nav-screen-translations.open {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, provide } from 'vue'
|
||||
import { useNav } from '../../composables/nav.js'
|
||||
import { useData } from '../../composables/data.js'
|
||||
import Navbar from './NavBar.vue'
|
||||
import NavScreen from './NavScreen.vue'
|
||||
|
||||
const { page } = useData()
|
||||
|
||||
const { isScreenOpen, closeScreen, toggleScreen } = useNav()
|
||||
|
||||
const fixedInclude = ['blog', 'friends', 'blog-archives', 'blog-tags']
|
||||
|
||||
const fixed = computed(() => {
|
||||
return fixedInclude.includes(page.value.type as string)
|
||||
})
|
||||
|
||||
provide('close-screen', closeScreen)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="nav-wrapper" :class="{ fixed }">
|
||||
<Navbar :is-screen-open="isScreenOpen" @toggle-screen="toggleScreen" />
|
||||
<NavScreen :open="isScreenOpen" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav-wrapper {
|
||||
position: relative;
|
||||
top: var(--vp-layout-top-height, 0);
|
||||
|
||||
/* rtl:ignore */
|
||||
left: 0;
|
||||
z-index: var(--vp-z-index-nav);
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-wrapper.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.nav-wrapper.fixed :deep(.navbar-wrapper) {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
border-bottom-color: var(--vp-c-gutter);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.nav-wrapper {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -26,7 +26,7 @@ function onSubmit() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="encrypt-form">
|
||||
<div class="vp-encrypt-form">
|
||||
<p class="encrypt-text" v-html="info ?? 'Only Password can access this site'" />
|
||||
<p class="encrypt-input-wrapper">
|
||||
<span class="vpi-lock icon-lock" />
|
||||
@ -47,7 +47,7 @@ function onSubmit() {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.encrypt-form {
|
||||
.vp-encrypt-form {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ const title = computed(() => avatar.value?.name || site.value.title)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="global-encrypt-wrapper">
|
||||
<div class="vp-global-encrypt">
|
||||
<div class="global-encrypt-container">
|
||||
<div v-if="avatar || title" class="profile">
|
||||
<p v-if="avatar" class="avatar" :class="{ circle: avatar.circle }">
|
||||
@ -30,7 +30,7 @@ const title = computed(() => avatar.value?.name || site.value.title)
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.global-encrypt-wrapper {
|
||||
.vp-global-encrypt {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
@ -40,7 +40,7 @@ const title = computed(() => avatar.value?.name || site.value.title)
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.global-encrypt-wrapper {
|
||||
.vp-global-encrypt {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
|
||||
@ -8,7 +8,7 @@ const { comparePage } = usePageEncrypt()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-encrypt-wrapper">
|
||||
<div class="vp-page-encrypt">
|
||||
<div class="logo">
|
||||
<span class="vpi-lock icon-lock-head" />
|
||||
</div>
|
||||
@ -17,7 +17,7 @@ const { comparePage } = usePageEncrypt()
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page-encrypt-wrapper .logo {
|
||||
.vp-page-encrypt .logo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ const { comparePage } = usePageEncrypt()
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.page-encrypt-wrapper {
|
||||
.vp-page-encrypt {
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
margin: 40px auto 0;
|
||||
|
||||
@ -10,7 +10,7 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="menu-group">
|
||||
<div class="vp-menu-group">
|
||||
<p v-if="text" class="title">
|
||||
<VPIcon v-if="icon" :name="icon" />
|
||||
<span v-text="text" />
|
||||
@ -23,20 +23,20 @@ defineProps<{
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.menu-group {
|
||||
.vp-menu-group {
|
||||
padding: 12px 12px 0;
|
||||
margin: 12px -12px 0;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
transition: border-top var(--t-color);
|
||||
}
|
||||
|
||||
.menu-group:first-child {
|
||||
.vp-menu-group:first-child {
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.menu-group + .menu-group {
|
||||
.vp-menu-group + .vp-menu-group {
|
||||
margin-top: 12px;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ const { page } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="menu-link">
|
||||
<div class="vp-menu-link">
|
||||
<VPLink
|
||||
:class="{
|
||||
active: isActive(
|
||||
@ -30,7 +30,7 @@ const { page } = useData()
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.menu-group + .menu-link {
|
||||
.vp-menu-group + .vp-menu-link {
|
||||
padding: 12px 12px 0;
|
||||
margin: 12px -12px 0;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
|
||||
@ -33,7 +33,7 @@ function focusOnTargetAnchor({ target }: Event) {
|
||||
<span ref="backToTop" tabindex="-1" />
|
||||
<a
|
||||
href="#VPContent"
|
||||
class="skip-link visually-hidden"
|
||||
class="vp-skip-link visually-hidden"
|
||||
@click="focusOnTargetAnchor"
|
||||
>
|
||||
Skip to content
|
||||
@ -41,7 +41,7 @@ function focusOnTargetAnchor({ target }: Event) {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.skip-link {
|
||||
.vp-skip-link {
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
z-index: 999;
|
||||
@ -55,7 +55,7 @@ function focusOnTargetAnchor({ target }: Event) {
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
.vp-skip-link:focus {
|
||||
width: auto;
|
||||
height: auto;
|
||||
clip: auto;
|
||||
@ -63,7 +63,7 @@ function focusOnTargetAnchor({ target }: Event) {
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.skip-link {
|
||||
.vp-skip-link {
|
||||
top: 14px;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { watch } from 'vue'
|
||||
import VPBackdrop from '../components/VPBackdrop.vue'
|
||||
import VPContent from '../components/VPContent.vue'
|
||||
import VPLocalNav from '../components/VPLocalNav.vue'
|
||||
import Nav from '../components/Nav/index.vue'
|
||||
import VPNav from '../components/Nav/VPNav.vue'
|
||||
import VPSidebar from '../components/VPSidebar.vue'
|
||||
import VPSkipLink from '../components/VPSkipLink.vue'
|
||||
import VPFooter from '../components/VPFooter.vue'
|
||||
@ -42,7 +42,26 @@ useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
|
||||
|
||||
<VPBackdrop :show="isSidebarOpen" @click="closeSidebar" />
|
||||
|
||||
<Nav />
|
||||
<VPNav>
|
||||
<template #nav-bar-title-before>
|
||||
<slot name="nav-bar-title-before" />
|
||||
</template>
|
||||
<template #nav-bar-title-after>
|
||||
<slot name="nav-bar-title-after" />
|
||||
</template>
|
||||
<template #nav-bar-content-before>
|
||||
<slot name="nav-bar-content-before" />
|
||||
</template>
|
||||
<template #nav-bar-content-after>
|
||||
<slot name="nav-bar-content-after" />
|
||||
</template>
|
||||
<template #nav-screen-content-before>
|
||||
<slot name="nav-screen-content-before" />
|
||||
</template>
|
||||
<template #nav-screen-content-after>
|
||||
<slot name="nav-screen-content-after" />
|
||||
</template>
|
||||
</VPNav>
|
||||
|
||||
<VPLocalNav :open="isSidebarOpen" :show-outline="isPageDecrypted" @open-menu="openSidebar" />
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouteLocale, withBase } from 'vuepress/client'
|
||||
import Nav from '../components/Nav/index.vue'
|
||||
import VPNav from '../components/Nav/VPNav.vue'
|
||||
import VPSkipLink from '../components/VPSkipLink.vue'
|
||||
import VPFooter from '../components/VPFooter.vue'
|
||||
import { useData } from '../composables/data.js'
|
||||
@ -14,7 +14,27 @@ const { theme } = useData()
|
||||
<slot name="layout-top" />
|
||||
<VPSkipLink />
|
||||
|
||||
<Nav />
|
||||
<VPNav>
|
||||
<template #nav-bar-title-before>
|
||||
<slot name="nav-bar-title-before" />
|
||||
</template>
|
||||
<template #nav-bar-title-after>
|
||||
<slot name="nav-bar-title-after" />
|
||||
</template>
|
||||
<template #nav-bar-content-before>
|
||||
<slot name="nav-bar-content-before" />
|
||||
</template>
|
||||
<template #nav-bar-content-after>
|
||||
<slot name="nav-bar-content-after" />
|
||||
</template>
|
||||
<template #nav-screen-content-before>
|
||||
<slot name="nav-screen-content-before" />
|
||||
</template>
|
||||
<template #nav-screen-content-after>
|
||||
<slot name="nav-screen-content-after" />
|
||||
</template>
|
||||
</VPNav>
|
||||
|
||||
<div id="VPContent" class="vp-content">
|
||||
<slot name="not-found">
|
||||
<div class="vp-not-found">
|
||||
|
||||
@ -1,8 +1,21 @@
|
||||
.navbar-search {
|
||||
.vp-navbar-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.vp-navbar-search {
|
||||
flex-grow: 1;
|
||||
padding-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar-search {
|
||||
padding-left: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
/* plugin-docsearch */
|
||||
.DocSearch {
|
||||
--docsearch-primary-color: var(--vp-c-brand-1);
|
||||
@ -27,7 +40,7 @@
|
||||
--docsearch-hit-shadow: none;
|
||||
}
|
||||
|
||||
.navbar-search .DocSearch-Button {
|
||||
.vp-navbar-search .DocSearch-Button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center !important;
|
||||
@ -39,20 +52,20 @@
|
||||
transition: border-color var(--t-color), background var(--t-color);
|
||||
}
|
||||
|
||||
.navbar-search .DocSearch-Button:hover {
|
||||
.vp-navbar-search .DocSearch-Button:hover {
|
||||
background: var(--docsearch-searchbox-focus-background);
|
||||
}
|
||||
|
||||
.navbar-search .DocSearch-Button:focus {
|
||||
.vp-navbar-search .DocSearch-Button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
.navbar-search .DocSearch-Button:focus:not(:focus-visible) {
|
||||
.vp-navbar-search .DocSearch-Button:focus:not(:focus-visible) {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.navbar-search #docsearch-container {
|
||||
.vp-navbar-search #docsearch-container {
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
@ -166,7 +179,7 @@
|
||||
/* plugin-docsearch */
|
||||
|
||||
/* plugin-search */
|
||||
.navbar-search .search-box input {
|
||||
.vp-navbar-search .search-box input {
|
||||
padding: 0 0.3rem 0 1.655rem;
|
||||
background-position: 0.5rem 0.4rem;
|
||||
}
|
||||
@ -174,26 +187,26 @@
|
||||
/* plugin-search */
|
||||
/* stylelint-disable-next-line order/order */
|
||||
@media (min-width: 768px) {
|
||||
.navbar-search {
|
||||
.vp-navbar-search {
|
||||
flex-grow: 1;
|
||||
padding-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.navbar-search {
|
||||
.vp-navbar-search {
|
||||
padding-left: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.navbar-search {
|
||||
.vp-navbar-search {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-search .DocSearch-Button {
|
||||
.vp-navbar-search .DocSearch-Button {
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
@ -203,7 +216,7 @@
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.navbar-search .DocSearch-Button:hover {
|
||||
.vp-navbar-search .DocSearch-Button:hover {
|
||||
background: var(--docsearch-searchbox-focus-background);
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
@ -26,13 +26,24 @@ export interface PlumeNormalFrontmatter extends PageFrontmatter {
|
||||
*/
|
||||
pageClass?: string
|
||||
|
||||
/**
|
||||
* 是否显示导航栏
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
navbar?: boolean
|
||||
|
||||
/**
|
||||
* 是否显示返回顶部按钮
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
backToTop?: boolean
|
||||
|
||||
/**
|
||||
* 当前页面是否显示 外部链接图标
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
externalLinkIcon?: boolean
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ export interface NavItemWithLink {
|
||||
text: string
|
||||
link: string
|
||||
icon?: string | { svg: string }
|
||||
rel?: string
|
||||
target?: string
|
||||
|
||||
/**
|
||||
* `activeMatch` is expected to be a regex string. We can't use actual
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user