diff --git a/src/client/app/index.ts b/src/client/app/index.ts index 023b93ae..e653ef96 100644 --- a/src/client/app/index.ts +++ b/src/client/app/index.ts @@ -17,6 +17,7 @@ import { usePrefetch } from './composables/preFetch' import { dataSymbol, initData, siteDataRef, useData } from './data' import { RouterSymbol, createRouter, scrollTo, type Router } from './router' import { inBrowser, pathToFile } from './utils' +import { provideInert } from './inert' function resolveThemeExtends(theme: typeof RawTheme): typeof RawTheme { if (theme.extends) { @@ -73,6 +74,8 @@ export async function createApp() { const data = initData(router.route) app.provide(dataSymbol, data) + provideInert(app) + // install global components app.component('Content', Content) app.component('ClientOnly', ClientOnly) diff --git a/src/client/app/inert.ts b/src/client/app/inert.ts new file mode 100644 index 00000000..1e041031 --- /dev/null +++ b/src/client/app/inert.ts @@ -0,0 +1,60 @@ +import { + type App, + computed, + inject, + reactive, + type UnwrapNestedRefs +} from 'vue' + +const inertSymbol = Symbol() +const inertStateSymbol = Symbol() + +export interface Inert { + isSidebarOpen: boolean + isScreenOpen: boolean + isSidebarEnabled: boolean + onAfterRouteChanged: () => void +} + +export interface InertState { + inertSkipLink: boolean + inertNav: boolean + inertLocalNav: boolean + inertSidebar: boolean + inertContent: boolean + inertFooter: boolean +} + +export function useInert() { + return inject>(inertSymbol) +} + +export function useInertState() { + return inject>(inertStateSymbol) +} + +export function provideInert(app: App) { + const inert = reactive({ + isSidebarOpen: false, + isScreenOpen: false, + isSidebarEnabled: false, + onAfterRouteChanged() { + inert.isSidebarOpen = false + inert.isScreenOpen = false + } + }) + const inertState = reactive({ + inertSkipLink: computed(() => inert.isSidebarOpen || inert.isScreenOpen), + inertNav: computed(() => inert.isSidebarOpen), + inertLocalNav: computed(() => inert.isSidebarOpen || inert.isScreenOpen), + inertSidebar: computed( + () => + !inert.isSidebarEnabled && (!inert.isSidebarOpen || inert.isScreenOpen) + ), + inertContent: computed(() => inert.isSidebarOpen || inert.isScreenOpen), + inertFooter: computed(() => inert.isSidebarOpen || inert.isScreenOpen) + }) + + app.provide(inertSymbol, inert) + app.provide(inertStateSymbol, inertState) +} diff --git a/src/client/index.ts b/src/client/index.ts index 072adba3..398dc89a 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -3,6 +3,7 @@ // generic types export type { VitePressData } from './app/data' +export type { Inert, InertState } from './app/inert' export type { Route, Router } from './app/router' // theme types @@ -13,6 +14,7 @@ export type { HeadConfig, Header, PageData, SiteData } from '../../types/shared' // composables export { useData, dataSymbol } from './app/data' +export { useInert, useInertState } from './app/inert' export { useRoute, useRouter } from './app/router' // utilities diff --git a/src/client/theme-default/Layout.vue b/src/client/theme-default/Layout.vue index 0ecca7ec..3ff6adad 100644 --- a/src/client/theme-default/Layout.vue +++ b/src/client/theme-default/Layout.vue @@ -1,5 +1,5 @@ @@ -155,11 +166,12 @@ function scrollToTop() { .header { background-color: var(--vp-c-bg-soft); + padding: 2px 16px; } .top-link { display: block; - padding: 0 16px; + padding: 0; line-height: 48px; font-size: 14px; font-weight: 500; diff --git a/src/client/theme-default/components/VPSkipLink.vue b/src/client/theme-default/components/VPSkipLink.vue index 4159e64e..21155024 100644 --- a/src/client/theme-default/components/VPSkipLink.vue +++ b/src/client/theme-default/components/VPSkipLink.vue @@ -27,14 +27,16 @@ function focusOnTargetAnchor({ target }: Event) {