feat(theme): new design for local nav and global header (#3359)

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>
pull/3391/head
Kia King Ishii 1 year ago committed by GitHub
parent 621254300d
commit d10bf42c26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,6 @@ import { useData } from '../composables/data'
import { useSidebar } from '../composables/sidebar' import { useSidebar } from '../composables/sidebar'
import VPDocAside from './VPDocAside.vue' import VPDocAside from './VPDocAside.vue'
import VPDocFooter from './VPDocFooter.vue' import VPDocFooter from './VPDocFooter.vue'
import VPDocOutlineDropdown from './VPDocOutlineDropdown.vue'
const { theme } = useData() const { theme } = useData()
@ -43,7 +42,6 @@ const pageName = computed(() =>
<div class="content"> <div class="content">
<div class="content-container"> <div class="content-container">
<slot name="doc-before" /> <slot name="doc-before" />
<VPDocOutlineDropdown />
<main class="main"> <main class="main">
<Content <Content
class="vp-doc" class="vp-doc"
@ -70,16 +68,6 @@ const pageName = computed(() =>
width: 100%; width: 100%;
} }
.VPDoc .VPDocOutlineDropdown {
display: none;
}
@media (min-width: 960px) and (max-width: 1279px) {
.VPDoc .VPDocOutlineDropdown {
display: block;
}
}
@media (min-width: 768px) { @media (min-width: 768px) {
.VPDoc { .VPDoc {
padding: 48px 32px 128px; padding: 48px 32px 128px;
@ -88,7 +76,7 @@ const pageName = computed(() =>
@media (min-width: 960px) { @media (min-width: 960px) {
.VPDoc { .VPDoc {
padding: 32px 32px 0; padding: 48px 32px 0;
} }
.VPDoc:not(.has-sidebar) .container { .VPDoc:not(.has-sidebar) .container {
@ -147,7 +135,7 @@ const pageName = computed(() =>
.aside-container { .aside-container {
position: fixed; position: fixed;
top: 0; top: 0;
padding-top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 32px); padding-top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 48px);
width: 224px; width: 224px;
height: 100vh; height: 100vh;
overflow-x: hidden; overflow-x: hidden;
@ -171,7 +159,7 @@ const pageName = computed(() =>
.aside-content { .aside-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px)); min-height: calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px));
padding-bottom: 32px; padding-bottom: 32px;
} }

@ -80,9 +80,8 @@ useActiveAnchor(container, marker)
} }
.outline-title { .outline-title {
letter-spacing: 0.4px; line-height: 32px;
line-height: 28px; font-size: 14px;
font-size: 13px;
font-weight: 600; font-weight: 600;
} }
</style> </style>

@ -1,85 +0,0 @@
<script setup lang="ts">
import { ref, shallowRef } from 'vue'
import { useData } from '../composables/data'
import { getHeaders, resolveTitle, type MenuItem } from '../composables/outline'
import VPDocOutlineItem from './VPDocOutlineItem.vue'
import { onContentUpdated } from 'vitepress'
import VPIconChevronRight from './icons/VPIconChevronRight.vue'
const { frontmatter, theme } = useData()
const open = ref(false)
onContentUpdated(() => {
open.value = false
})
const headers = shallowRef<MenuItem[]>([])
onContentUpdated(() => {
headers.value = getHeaders(
frontmatter.value.outline ?? theme.value.outline
)
})
</script>
<template>
<div class="VPDocOutlineDropdown" v-if="headers.length > 0">
<button @click="open = !open" :class="{ open }">
{{ resolveTitle(theme) }}
<VPIconChevronRight class="icon" />
</button>
<div class="items" v-if="open">
<VPDocOutlineItem :headers="headers" />
</div>
</div>
</template>
<style scoped>
.VPDocOutlineDropdown {
margin-bottom: 48px;
}
.VPDocOutlineDropdown button {
display: block;
font-size: 14px;
font-weight: 500;
line-height: 24px;
border: 1px solid var(--vp-c-border);
padding: 4px 12px;
color: var(--vp-c-text-2);
background-color: var(--vp-c-default-soft);
border-radius: 8px;
transition: color 0.5s;
}
.VPDocOutlineDropdown button:hover {
color: var(--vp-c-text-1);
transition: color 0.25s;
}
.VPDocOutlineDropdown button.open {
color: var(--vp-c-text-1);
}
.icon {
display: inline-block;
vertical-align: middle;
width: 16px;
height: 16px;
fill: currentColor;
}
:deep(.outline-link) {
font-size: 14px;
font-weight: 400;
}
.open > .icon {
transform: rotate(90deg);
}
.items {
margin-top: 12px;
border-left: 1px solid var(--vp-c-divider);
}
</style>

@ -14,7 +14,7 @@ function onClick({ target: el }: Event) {
</script> </script>
<template> <template>
<ul :class="root ? 'root' : 'nested'"> <ul class="VPDocOutlineItem" :class="root ? 'root' : 'nested'">
<li v-for="{ children, link, title } in headers"> <li v-for="{ children, link, title } in headers">
<a class="outline-link" :href="link" @click="onClick" :title="title">{{ title }}</a> <a class="outline-link" :href="link" @click="onClick" :title="title">{{ title }}</a>
<template v-if="children?.length"> <template v-if="children?.length">
@ -31,18 +31,20 @@ function onClick({ target: el }: Event) {
} }
.nested { .nested {
padding-right: 16px;
padding-left: 16px; padding-left: 16px;
} }
.outline-link { .outline-link {
display: block; display: block;
line-height: 28px; line-height: 32px;
font-size: 14px;
font-weight: 400;
color: var(--vp-c-text-2); color: var(--vp-c-text-2);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
transition: color 0.5s; transition: color 0.5s;
font-weight: 400;
} }
.outline-link:hover, .outline-link:hover,

@ -1,9 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useWindowScroll } from '@vueuse/core' import { useWindowScroll } from '@vueuse/core'
import { onContentUpdated } from 'vitepress' import { onContentUpdated } from 'vitepress'
import { computed, onMounted, ref, shallowRef } from 'vue' import { computed, onMounted, ref } from 'vue'
import { useData } from '../composables/data' import { useData } from '../composables/data'
import { getHeaders, type MenuItem } from '../composables/outline' import { useLocalNav } from '../composables/local-nav'
import { getHeaders } from '../composables/outline'
import { useSidebar } from '../composables/sidebar' import { useSidebar } from '../composables/sidebar'
import VPLocalNavOutlineDropdown from './VPLocalNavOutlineDropdown.vue' import VPLocalNavOutlineDropdown from './VPLocalNavOutlineDropdown.vue'
import VPIconAlignLeft from './icons/VPIconAlignLeft.vue' import VPIconAlignLeft from './icons/VPIconAlignLeft.vue'
@ -18,9 +19,9 @@ defineEmits<{
const { theme, frontmatter } = useData() const { theme, frontmatter } = useData()
const { hasSidebar } = useSidebar() const { hasSidebar } = useSidebar()
const { headers } = useLocalNav()
const { y } = useWindowScroll() const { y } = useWindowScroll()
const headers = shallowRef<MenuItem[]>([])
const navHeight = ref(0) const navHeight = ref(0)
onMounted(() => { onMounted(() => {
@ -36,37 +37,44 @@ onContentUpdated(() => {
}) })
const empty = computed(() => { const empty = computed(() => {
return headers.value.length === 0 && !hasSidebar.value return headers.value.length === 0
})
const emptyAndNoSidebar = computed(() => {
return empty.value && !hasSidebar.value
}) })
const classes = computed(() => { const classes = computed(() => {
return { return {
VPLocalNav: true, VPLocalNav: true,
fixed: empty.value, 'has-sidebar': hasSidebar.value,
'reached-top': y.value >= navHeight.value empty: empty.value,
fixed: emptyAndNoSidebar.value
} }
}) })
</script> </script>
<template> <template>
<div <div
v-if="frontmatter.layout !== 'home' && (!empty || y >= navHeight)" v-if="frontmatter.layout !== 'home' && (!emptyAndNoSidebar || y >= navHeight)"
:class="classes" :class="classes"
> >
<button <div class="container">
v-if="hasSidebar" <button
class="menu" v-if="hasSidebar"
:aria-expanded="open" class="menu"
aria-controls="VPSidebarNav" :aria-expanded="open"
@click="$emit('open-menu')" aria-controls="VPSidebarNav"
> @click="$emit('open-menu')"
<VPIconAlignLeft class="menu-icon" /> >
<span class="menu-text"> <VPIconAlignLeft class="menu-icon" />
{{ theme.sidebarMenuLabel || 'Menu' }} <span class="menu-text">
</span> {{ theme.sidebarMenuLabel || 'Menu' }}
</button> </span>
</button>
<VPLocalNavOutlineDropdown :headers="headers" :navHeight="navHeight" />
<VPLocalNavOutlineDropdown :headers="headers" :navHeight="navHeight" />
</div>
</div> </div>
</template> </template>
@ -77,10 +85,6 @@ const classes = computed(() => {
/*rtl:ignore*/ /*rtl:ignore*/
left: 0; left: 0;
z-index: var(--vp-z-index-local-nav); z-index: var(--vp-z-index-local-nav);
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid var(--vp-c-gutter);
border-bottom: 1px solid var(--vp-c-gutter); border-bottom: 1px solid var(--vp-c-gutter);
padding-top: var(--vp-layout-top-height, 0px); padding-top: var(--vp-layout-top-height, 0px);
width: 100%; width: 100%;
@ -91,16 +95,38 @@ const classes = computed(() => {
position: fixed; position: fixed;
} }
.VPLocalNav.reached-top { @media (min-width: 960px) {
border-top-color: transparent; .VPLocalNav {
top: var(--vp-nav-height);
}
.VPLocalNav.has-sidebar {
padding-left: var(--vp-sidebar-width);
}
.VPLocalNav.empty {
display: none;
}
} }
@media (min-width: 960px) { @media (min-width: 1280px) {
.VPLocalNav { .VPLocalNav {
display: none; display: none;
} }
} }
@media (min-width: 1440px) {
.VPLocalNav.has-sidebar {
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
}
}
.container {
display: flex;
justify-content: space-between;
align-items: center;
}
.menu { .menu {
display: flex; display: flex;
align-items: center; align-items: center;
@ -123,6 +149,12 @@ const classes = computed(() => {
} }
} }
@media (min-width: 960px) {
.menu {
display: none;
}
}
.menu-icon { .menu-icon {
margin-right: 8px; margin-right: 8px;
width: 16px; width: 16px;

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { onClickOutside, onKeyStroke } from '@vueuse/core'
import { onContentUpdated } from 'vitepress' import { onContentUpdated } from 'vitepress'
import { nextTick, ref } from 'vue' import { nextTick, ref } from 'vue'
import { useData } from '../composables/data' import { useData } from '../composables/data'
@ -14,8 +15,17 @@ const props = defineProps<{
const { theme } = useData() const { theme } = useData()
const open = ref(false) const open = ref(false)
const vh = ref(0) const vh = ref(0)
const main = ref<HTMLDivElement>()
const items = ref<HTMLDivElement>() const items = ref<HTMLDivElement>()
onClickOutside(main, () => {
open.value = false
})
onKeyStroke('Escape', () => {
open.value = false
})
onContentUpdated(() => { onContentUpdated(() => {
open.value = false open.value = false
}) })
@ -44,7 +54,11 @@ function scrollToTop() {
</script> </script>
<template> <template>
<div class="VPLocalNavOutlineDropdown" :style="{ '--vp-vh': vh + 'px' }"> <div
class="VPLocalNavOutlineDropdown"
:style="{ '--vp-vh': vh + 'px' }"
ref="main"
>
<button @click="toggle" :class="{ open }" v-if="headers.length > 0"> <button @click="toggle" :class="{ open }" v-if="headers.length > 0">
{{ resolveTitle(theme) }} {{ resolveTitle(theme) }}
<VPIconChevronRight class="icon" /> <VPIconChevronRight class="icon" />
@ -53,11 +67,7 @@ function scrollToTop() {
{{ theme.returnToTopLabel || 'Return to top' }} {{ theme.returnToTopLabel || 'Return to top' }}
</button> </button>
<Transition name="flyout"> <Transition name="flyout">
<div v-if="open" <div v-if="open" ref="items" class="items" @click="onItemClick">
ref="items"
class="items"
@click="onItemClick"
>
<div class="header"> <div class="header">
<a class="top-link" href="#" @click="scrollToTop"> <a class="top-link" href="#" @click="scrollToTop">
{{ theme.returnToTopLabel || 'Return to top' }} {{ theme.returnToTopLabel || 'Return to top' }}
@ -76,6 +86,12 @@ function scrollToTop() {
padding: 12px 20px 11px; padding: 12px 20px 11px;
} }
@media (min-width: 960px) {
.VPLocalNavOutlineDropdown {
padding: 12px 36px 11px;
}
}
.VPLocalNavOutlineDropdown button { .VPLocalNavOutlineDropdown button {
display: block; display: block;
font-size: 12px; font-size: 12px;
@ -95,6 +111,12 @@ function scrollToTop() {
color: var(--vp-c-text-1); color: var(--vp-c-text-1);
} }
@media (min-width: 960px) {
.VPLocalNavOutlineDropdown button {
font-size: 14px;
}
}
.icon { .icon {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
@ -104,18 +126,13 @@ function scrollToTop() {
fill: currentColor; fill: currentColor;
} }
:deep(.outline-link) {
font-size: 14px;
padding: 2px 0;
}
.open > .icon { .open > .icon {
transform: rotate(90deg); transform: rotate(90deg);
} }
.items { .items {
position: absolute; position: absolute;
top: 64px; top: 40px;
right: 16px; right: 16px;
left: 16px; left: 16px;
display: grid; display: grid;
@ -128,6 +145,14 @@ function scrollToTop() {
box-shadow: var(--vp-shadow-3); box-shadow: var(--vp-shadow-3);
} }
@media (min-width: 960px) {
.items {
right: auto;
left: calc(var(--vp-sidebar-width) + 32px);
width: 320px;
}
}
.header { .header {
background-color: var(--vp-c-bg-soft); background-color: var(--vp-c-bg-soft);
} }
@ -147,11 +172,11 @@ function scrollToTop() {
} }
.flyout-enter-active { .flyout-enter-active {
transition: all .2s ease-out; transition: all 0.2s ease-out;
} }
.flyout-leave-active { .flyout-leave-active {
transition: all .15s ease-in; transition: all 0.15s ease-in;
} }
.flyout-enter-from, .flyout-enter-from,

@ -2,6 +2,7 @@
import { useWindowScroll } from '@vueuse/core' import { useWindowScroll } from '@vueuse/core'
import { ref, watchPostEffect } from 'vue' import { ref, watchPostEffect } from 'vue'
import { useData } from '../composables/data' import { useData } from '../composables/data'
import { useLocalNav } from '../composables/local-nav'
import { useSidebar } from '../composables/sidebar' import { useSidebar } from '../composables/sidebar'
import VPNavBarAppearance from './VPNavBarAppearance.vue' import VPNavBarAppearance from './VPNavBarAppearance.vue'
import VPNavBarExtra from './VPNavBarExtra.vue' import VPNavBarExtra from './VPNavBarExtra.vue'
@ -22,6 +23,7 @@ defineEmits<{
const { y } = useWindowScroll() const { y } = useWindowScroll()
const { hasSidebar } = useSidebar() const { hasSidebar } = useSidebar()
const { hasLocalNav } = useLocalNav()
const { frontmatter } = useData() const { frontmatter } = useData()
const classes = ref<Record<string, boolean>>({}) const classes = ref<Record<string, boolean>>({})
@ -29,6 +31,7 @@ const classes = ref<Record<string, boolean>>({})
watchPostEffect(() => { watchPostEffect(() => {
classes.value = { classes.value = {
'has-sidebar': hasSidebar.value, 'has-sidebar': hasSidebar.value,
'has-local-nav': hasLocalNav.value,
top: frontmatter.value.layout === 'home' && y.value === 0, top: frontmatter.value.layout === 'home' && y.value === 0,
} }
}) })
@ -36,59 +39,76 @@ watchPostEffect(() => {
<template> <template>
<div class="VPNavBar" :class="classes"> <div class="VPNavBar" :class="classes">
<div class="container"> <div class="wrapper">
<div class="title"> <div class="container">
<VPNavBarTitle> <div class="title">
<template #nav-bar-title-before><slot name="nav-bar-title-before" /></template> <VPNavBarTitle>
<template #nav-bar-title-after><slot name="nav-bar-title-after" /></template> <template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
</VPNavBarTitle> <template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
</div> </VPNavBarTitle>
</div>
<div class="content"> <div class="content">
<div class="curtain" /> <div class="content-body">
<div class="content-body"> <slot name="nav-bar-content-before" />
<slot name="nav-bar-content-before" /> <VPNavBarSearch class="search" />
<VPNavBarSearch class="search" /> <VPNavBarMenu class="menu" />
<VPNavBarMenu class="menu" /> <VPNavBarTranslations class="translations" />
<VPNavBarTranslations class="translations" /> <VPNavBarAppearance class="appearance" />
<VPNavBarAppearance class="appearance" /> <VPNavBarSocialLinks class="social-links" />
<VPNavBarSocialLinks class="social-links" /> <VPNavBarExtra class="extra" />
<VPNavBarExtra class="extra" /> <slot name="nav-bar-content-after" />
<slot name="nav-bar-content-after" /> <VPNavBarHamburger class="hamburger" :active="isScreenOpen" @click="$emit('toggle-screen')" />
<VPNavBarHamburger class="hamburger" :active="isScreenOpen" @click="$emit('toggle-screen')" /> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="divider">
<div class="divider-line" />
</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.VPNavBar { .VPNavBar {
position: relative; position: relative;
border-bottom: 1px solid transparent;
padding: 0 8px 0 24px;
height: var(--vp-nav-height); height: var(--vp-nav-height);
pointer-events: none; pointer-events: none;
white-space: nowrap; white-space: nowrap;
transition: background-color 0.5s;
} }
@media (min-width: 768px) { .VPNavBar.has-local-nav {
.VPNavBar { background-color: var(--vp-nav-bg-color);
padding: 0 32px;
}
} }
@media (min-width: 960px) { @media (min-width: 960px) {
.VPNavBar.has-sidebar { .VPNavBar.has-local-nav {
padding: 0; background-color: transparent;
} }
.VPNavBar:not(.has-sidebar):not(.top) { .VPNavBar:not(.has-sidebar):not(.top) {
border-bottom-color: var(--vp-c-gutter);
background-color: var(--vp-nav-bg-color); 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) {
.VPNavBar.has-sidebar .wrapper {
padding: 0;
}
}
.container { .container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -163,15 +183,19 @@ watchPostEffect(() => {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
height: calc(var(--vp-nav-height) - 1px); height: var(--vp-nav-height);
transition: background-color 0.5s; transition: background-color 0.5s;
} }
@media (min-width: 960px) { @media (min-width: 960px) {
.VPNavBar:not(.top) .content-body{ .VPNavBar:not(.top) .content-body {
position: relative; position: relative;
background-color: var(--vp-nav-bg-color); background-color: var(--vp-nav-bg-color);
} }
.VPNavBar:not(.has-sidebar):not(.top) .content-body {
background-color: transparent;
}
} }
@media (max-width: 767px) { @media (max-width: 767px) {
@ -206,27 +230,40 @@ watchPostEffect(() => {
margin-right: -8px; margin-right: -8px;
} }
.divider {
width: 100%;
height: 1px;
}
@media (min-width: 960px) { @media (min-width: 960px) {
.VPNavBar.has-sidebar .curtain { .VPNavBar.has-sidebar .divider {
position: absolute; padding-left: var(--vp-sidebar-width);
right: 0;
bottom: -31px;
width: calc(100% - var(--vp-sidebar-width));
height: 32px;
} }
}
.VPNavBar.has-sidebar .curtain::before { @media (min-width: 1440px) {
display: block; .VPNavBar.has-sidebar .divider {
width: 100%; padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
height: 32px;
background: linear-gradient(var(--vp-c-bg), transparent 70%);
content: "";
} }
} }
@media (min-width: 1440px) { .divider-line {
.VPNavBar.has-sidebar .curtain { width: 100%;
width: calc(100% - ((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))); height: 1px;
transition: background-color 0.5s;
}
.VPNavBar.has-local-nav .divider-line {
background-color: var(--vp-c-gutter);
}
@media (min-width: 960px) {
.VPNavBar:not(.top) .divider-line {
background-color: var(--vp-c-gutter);
}
.VPNavBar:not(.has-sidebar):not(.top) .divider {
background-color: var(--vp-c-gutter);
} }
} }
</style> </style>

@ -87,7 +87,6 @@ watch(
@media (min-width: 960px) { @media (min-width: 960px) {
.VPSidebar { .VPSidebar {
z-index: 1;
padding-top: var(--vp-nav-height); padding-top: var(--vp-nav-height);
width: var(--vp-sidebar-width); width: var(--vp-sidebar-width);
max-width: 100%; max-width: 100%;

@ -0,0 +1,24 @@
import { onContentUpdated } from 'vitepress'
import { type DefaultTheme } from 'vitepress/theme'
import { computed, shallowRef } from 'vue'
import { getHeaders, type MenuItem } from '../composables/outline'
import { useData } from './data'
export function useLocalNav(): DefaultTheme.DocLocalNav {
const { theme, frontmatter } = useData()
const headers = shallowRef<MenuItem[]>([])
const hasLocalNav = computed(() => {
return headers.value.length > 0
})
onContentUpdated(() => {
headers.value = getHeaders(frontmatter.value.outline ?? theme.value.outline)
})
return {
headers,
hasLocalNav
}
}

@ -181,7 +181,7 @@ export function useActiveAnchor(
if (activeLink) { if (activeLink) {
activeLink.classList.add('active') activeLink.classList.add('active')
marker.value.style.top = activeLink.offsetTop + 33 + 'px' marker.value.style.top = activeLink.offsetTop + 39 + 'px'
marker.value.style.opacity = '1' marker.value.style.opacity = '1'
} else { } else {
marker.value.style.top = '33px' marker.value.style.top = '33px'

@ -264,6 +264,12 @@
--vp-z-index-sidebar: 60; --vp-z-index-sidebar: 60;
} }
@media (min-width: 960px) {
:root {
--vp-z-index-sidebar: 25;
}
}
/** /**
* Icons * Icons
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */

@ -27,6 +27,7 @@ export { default as VPTeamPageSection } from './components/VPTeamPageSection.vue
export { default as VPTeamMembers } from './components/VPTeamMembers.vue' export { default as VPTeamMembers } from './components/VPTeamMembers.vue'
export { useSidebar } from './composables/sidebar' export { useSidebar } from './composables/sidebar'
export { useLocalNav } from './composables/local-nav'
const theme: Theme = { const theme: Theme = {
Layout, Layout,

@ -252,7 +252,7 @@ export async function resolveSiteData(
appearance: userConfig.appearance ?? true, appearance: userConfig.appearance ?? true,
themeConfig: userConfig.themeConfig || {}, themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {}, locales: userConfig.locales || {},
scrollOffset: userConfig.scrollOffset ?? 90, scrollOffset: userConfig.scrollOffset ?? 134,
cleanUrls: !!userConfig.cleanUrls, cleanUrls: !!userConfig.cleanUrls,
contentProps: userConfig.contentProps contentProps: userConfig.contentProps
} }

1
theme.d.ts vendored

@ -13,6 +13,7 @@ declare const theme: {
export default theme export default theme
export declare const useSidebar: () => DefaultTheme.DocSidebar export declare const useSidebar: () => DefaultTheme.DocSidebar
export declare const useLocalNav: () => DefaultTheme.DocLocalNav
// TODO: add props for these // TODO: add props for these
export declare const VPButton: DefineComponent export declare const VPButton: DefineComponent

@ -358,6 +358,25 @@ export namespace DefaultTheme {
actionText?: string actionText?: string
} }
// local nav -----------------------------------------------------------------
/**
* ReturnType of `useLocalNav`.
*/
export interface DocLocalNav {
/**
* The outline headers of the current page.
*/
headers: ShallowRef<MenuItem[]>
/**
* Whether the current page has a local nav. Local nav is shown when the
* "outline" is present in the page. However, note that the actual
* local nav visibility depends on the screen width as well.
*/
hasLocalNav: ComputedRef<boolean>
}
// outline ------------------------------------------------------------------- // outline -------------------------------------------------------------------
export interface Outline { export interface Outline {

Loading…
Cancel
Save