feat: add again inert content on narrow for narrow screens

userquin/feat-add-inert-content-again
userquin 2 years ago
parent d1ff29431f
commit 0ae4ef0a2f

@ -17,6 +17,7 @@ import { usePrefetch } from './composables/preFetch'
import { dataSymbol, initData, siteDataRef, useData } from './data' import { dataSymbol, initData, siteDataRef, useData } from './data'
import { RouterSymbol, createRouter, scrollTo, type Router } from './router' import { RouterSymbol, createRouter, scrollTo, type Router } from './router'
import { inBrowser, pathToFile } from './utils' import { inBrowser, pathToFile } from './utils'
import { provideInert } from './inert'
function resolveThemeExtends(theme: typeof RawTheme): typeof RawTheme { function resolveThemeExtends(theme: typeof RawTheme): typeof RawTheme {
if (theme.extends) { if (theme.extends) {
@ -73,6 +74,8 @@ export async function createApp() {
const data = initData(router.route) const data = initData(router.route)
app.provide(dataSymbol, data) app.provide(dataSymbol, data)
provideInert(app)
// install global components // install global components
app.component('Content', Content) app.component('Content', Content)
app.component('ClientOnly', ClientOnly) app.component('ClientOnly', ClientOnly)

@ -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<UnwrapNestedRefs<Inert>>(inertSymbol)
}
export function useInertState() {
return inject<UnwrapNestedRefs<InertState>>(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)
}

@ -3,6 +3,7 @@
// generic types // generic types
export type { VitePressData } from './app/data' export type { VitePressData } from './app/data'
export type { Inert, InertState } from './app/inert'
export type { Route, Router } from './app/router' export type { Route, Router } from './app/router'
// theme types // theme types
@ -13,6 +14,7 @@ export type { HeadConfig, Header, PageData, SiteData } from '../../types/shared'
// composables // composables
export { useData, dataSymbol } from './app/data' export { useData, dataSymbol } from './app/data'
export { useInert, useInertState } from './app/inert'
export { useRoute, useRouter } from './app/router' export { useRoute, useRouter } from './app/router'
// utilities // utilities

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useRoute } from 'vitepress' import { useInertState, useRoute } from 'vitepress'
import { computed, provide, useSlots, watch } from 'vue' import { computed, provide, useSlots, watch } from 'vue'
import VPBackdrop from './components/VPBackdrop.vue' import VPBackdrop from './components/VPBackdrop.vue'
import VPContent from './components/VPContent.vue' import VPContent from './components/VPContent.vue'
@ -17,6 +17,7 @@ const {
close: closeSidebar close: closeSidebar
} = useSidebar() } = useSidebar()
const inert = useInertState()
const route = useRoute() const route = useRoute()
watch(() => route.path, closeSidebar) watch(() => route.path, closeSidebar)
@ -33,9 +34,9 @@ provide('hero-image-slot-exists', heroImageSlotExists)
<template> <template>
<div v-if="frontmatter.layout !== false" class="Layout" :class="frontmatter.pageClass" > <div v-if="frontmatter.layout !== false" class="Layout" :class="frontmatter.pageClass" >
<slot name="layout-top" /> <slot name="layout-top" />
<VPSkipLink /> <VPSkipLink :inert="inert?.inertSkipLink" />
<VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" /> <VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" />
<VPNav> <VPNav :inert="inert?.inertNav">
<template #nav-bar-title-before><slot name="nav-bar-title-before" /></template> <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-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-before><slot name="nav-bar-content-before" /></template>
@ -43,14 +44,14 @@ provide('hero-image-slot-exists', heroImageSlotExists)
<template #nav-screen-content-before><slot name="nav-screen-content-before" /></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> <template #nav-screen-content-after><slot name="nav-screen-content-after" /></template>
</VPNav> </VPNav>
<VPLocalNav :open="isSidebarOpen" @open-menu="openSidebar" /> <VPLocalNav :inert="inert?.inertLocalNav" :open="isSidebarOpen" @open-menu="openSidebar" />
<VPSidebar :open="isSidebarOpen"> <VPSidebar :inert="inert?.inertSidebar" :open="isSidebarOpen">
<template #sidebar-nav-before><slot name="sidebar-nav-before" /></template> <template #sidebar-nav-before><slot name="sidebar-nav-before" /></template>
<template #sidebar-nav-after><slot name="sidebar-nav-after" /></template> <template #sidebar-nav-after><slot name="sidebar-nav-after" /></template>
</VPSidebar> </VPSidebar>
<VPContent> <VPContent :inert="inert?.inertContent">
<template #page-top><slot name="page-top" /></template> <template #page-top><slot name="page-top" /></template>
<template #page-bottom><slot name="page-bottom" /></template> <template #page-bottom><slot name="page-bottom" /></template>
@ -76,7 +77,7 @@ provide('hero-image-slot-exists', heroImageSlotExists)
<template #aside-ads-after><slot name="aside-ads-after" /></template> <template #aside-ads-after><slot name="aside-ads-after" /></template>
</VPContent> </VPContent>
<VPFooter /> <VPFooter :inert="inert?.inertFooter" />
<slot name="layout-bottom" /> <slot name="layout-bottom" />
</div> </div>
<Content v-else /> <Content v-else />

@ -1,37 +1,50 @@
<script setup lang="ts"> <script setup lang="ts">
import { onClickOutside, onKeyStroke } from '@vueuse/core' import { 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'
import { resolveTitle, type MenuItem } from '../composables/outline' import { resolveTitle, type MenuItem } from '../composables/outline'
import VPDocOutlineItem from './VPDocOutlineItem.vue' import VPDocOutlineItem from './VPDocOutlineItem.vue'
import VPIconChevronRight from './icons/VPIconChevronRight.vue' import VPIconChevronRight from './icons/VPIconChevronRight.vue'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
const props = defineProps<{ const props = defineProps<{
headers: MenuItem[] headers: MenuItem[]
navHeight: number navHeight: number
}>() }>()
const { theme } = useData()
const open = ref(false)
const vh = ref(0)
const main = ref<HTMLDivElement>() const main = ref<HTMLDivElement>()
const items = ref<HTMLDivElement>() const items = ref<HTMLDivElement>()
onClickOutside(main, () => { const open = ref(false)
const { theme } = useData()
const { activate, deactivate } = useFocusTrap(items, {
immediate: true,
allowOutsideClick: true,
clickOutsideDeactivates: true,
escapeDeactivates: true,
delayInitialFocus: false,
onDeactivate: () => {
open.value = false open.value = false
},
}) })
const vh = ref(0)
onKeyStroke('Escape', () => { onKeyStroke('Escape', () => {
open.value = false deactivate()
}) })
onContentUpdated(() => { onContentUpdated(deactivate)
open.value = false
})
function toggle() { function toggle() {
open.value = !open.value if (open.value) {
deactivate()
} else {
open.value = true
nextTick(() => activate())
}
vh.value = window.innerHeight + Math.min(window.scrollY - props.navHeight, 0) vh.value = window.innerHeight + Math.min(window.scrollY - props.navHeight, 0)
} }
@ -41,14 +54,12 @@ function onItemClick(e: Event) {
if (items.value) { if (items.value) {
items.value.style.transition = 'none' items.value.style.transition = 'none'
} }
nextTick(() => { nextTick(() => deactivate())
open.value = false
})
} }
} }
function scrollToTop() { function scrollToTop() {
open.value = false deactivate()
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
} }
</script> </script>
@ -155,11 +166,12 @@ function scrollToTop() {
.header { .header {
background-color: var(--vp-c-bg-soft); background-color: var(--vp-c-bg-soft);
padding: 2px 16px;
} }
.top-link { .top-link {
display: block; display: block;
padding: 0 16px; padding: 0;
line-height: 48px; line-height: 48px;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;

@ -27,6 +27,7 @@ function focusOnTargetAnchor({ target }: Event) {
</script> </script>
<template> <template>
<div>
<span ref="backToTop" tabindex="-1" /> <span ref="backToTop" tabindex="-1" />
<a <a
href="#VPContent" href="#VPContent"
@ -35,6 +36,7 @@ function focusOnTargetAnchor({ target }: Event) {
> >
Skip to content Skip to content
</a> </a>
</div>
</template> </template>
<style scoped> <style scoped>

@ -1,16 +1,17 @@
import { ref, watch } from 'vue' import { computed, watch } from 'vue'
import { useRoute } from 'vitepress' import { useInert, useRoute } from 'vitepress'
export function useNav() { export function useNav() {
const isScreenOpen = ref(false) const inert = useInert()!
const isScreenOpen = computed(() => inert.isScreenOpen)
function openScreen() { function openScreen() {
isScreenOpen.value = true inert.isScreenOpen = true
window.addEventListener('resize', closeScreenOnTabletWindow) window.addEventListener('resize', closeScreenOnTabletWindow)
} }
function closeScreen() { function closeScreen() {
isScreenOpen.value = false inert.isScreenOpen = false
window.removeEventListener('resize', closeScreenOnTabletWindow) window.removeEventListener('resize', closeScreenOnTabletWindow)
} }
@ -19,7 +20,7 @@ export function useNav() {
} }
/** /**
* Close screen when the user resizes the window wider than tablet size. * Close the screen when the user resizes the window wider than tablet size.
*/ */
function closeScreenOnTabletWindow() { function closeScreenOnTabletWindow() {
window.outerWidth >= 768 && closeScreen() window.outerWidth >= 768 && closeScreen()

@ -18,6 +18,7 @@ import {
getSidebarGroups getSidebarGroups
} from '../support/sidebar' } from '../support/sidebar'
import { useData } from './data' import { useData } from './data'
import { useInert } from 'vitepress'
export interface SidebarControl { export interface SidebarControl {
collapsed: Ref<boolean> collapsed: Ref<boolean>
@ -33,7 +34,8 @@ export function useSidebar() {
const { frontmatter, page, theme } = useData() const { frontmatter, page, theme } = useData()
const is960 = useMediaQuery('(min-width: 960px)') const is960 = useMediaQuery('(min-width: 960px)')
const isOpen = ref(false) const inert = useInert()!
const isOpen = computed(() => inert.isSidebarOpen)
const _sidebar = computed(() => { const _sidebar = computed(() => {
const sidebarConfig = theme.value.sidebar const sidebarConfig = theme.value.sidebar
@ -77,11 +79,11 @@ export function useSidebar() {
}) })
function open() { function open() {
isOpen.value = true inert.isSidebarOpen = true
} }
function close() { function close() {
isOpen.value = false inert.isSidebarOpen = false
} }
function toggle() { function toggle() {

@ -157,7 +157,11 @@ export async function createVitePressPlugin(
}, },
optimizeDeps: { optimizeDeps: {
// force include vue to avoid duplicated copies when linked + optimized // force include vue to avoid duplicated copies when linked + optimized
include: ['vue', 'vitepress > @vue/devtools-api'], include: [
'vue',
'vitepress > @vue/devtools-api',
'vitepress > @vueuse/integrations/useFocusTrap'
],
exclude: ['@docsearch/js', 'vitepress'] exclude: ['@docsearch/js', 'vitepress']
}, },
server: { server: {

@ -168,7 +168,6 @@ export async function localSearchPlugin(
config: () => ({ config: () => ({
optimizeDeps: { optimizeDeps: {
include: [ include: [
'vitepress > @vueuse/integrations/useFocusTrap',
'vitepress > mark.js/src/vanilla.js', 'vitepress > mark.js/src/vanilla.js',
'vitepress > minisearch' 'vitepress > minisearch'
] ]

Loading…
Cancel
Save