feat(config, theme): add clientOnly marker

pull/4663/head
Yuxuan Zhang 6 months ago
parent 0267dcafa2
commit 82e59e763d
No known key found for this signature in database
GPG Key ID: 6910B04F3351EF7D

@ -1,7 +1,18 @@
import { defineComponent, onMounted, ref } from 'vue'
export const ClientOnly = defineComponent({
setup(_, { slots }) {
props: {
isClientOnly: {
type: Boolean,
default: true
}
},
setup({ isClientOnly }, { slots }) {
if (isClientOnly) console.log({ isClientOnly, slots })
// Programmatically determine if this component should be
// client-only based on the presence of the isClientOnly attribute.
if (!isClientOnly) return () => slots.default?.() || null
const show = ref(false)
onMounted(() => {

@ -2,11 +2,13 @@
import { useData } from '../composables/data'
import VPNavBarMenuLink from './VPNavBarMenuLink.vue'
import VPNavBarMenuGroup from './VPNavBarMenuGroup.vue'
import { isClientOnly } from '../../shared'
const { theme } = useData()
</script>
<template>
<ClientOnly :isClientOnly="isClientOnly(theme.nav)">
<nav
v-if="theme.nav"
aria-labelledby="main-nav-aria-label"
@ -25,6 +27,7 @@ const { theme } = useData()
<VPNavBarMenuGroup v-else :item="item" />
</template>
</nav>
</ClientOnly>
</template>
<style scoped>

@ -4,8 +4,9 @@ import { inBrowser } from 'vitepress'
import { ref, watch } from 'vue'
import { useSidebar } from '../composables/sidebar'
import VPSidebarGroup from './VPSidebarGroup.vue'
import { isClientOnly } from '../../shared'
const { sidebarGroups, hasSidebar } = useSidebar()
const { sidebar, sidebarGroups, hasSidebar, isSidebarClientOnly } = useSidebar()
const props = defineProps<{
open: boolean
@ -38,27 +39,18 @@ watch(
</script>
<template>
<aside
v-if="hasSidebar"
class="VPSidebar"
:class="{ open }"
ref="navEl"
@click.stop
>
<aside v-if="hasSidebar" class="VPSidebar" :class="{ open }" ref="navEl" @click.stop>
<div class="curtain" />
<nav
class="nav"
id="VPSidebarNav"
aria-labelledby="sidebar-aria-label"
tabindex="-1"
>
<nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1">
<span class="visually-hidden" id="sidebar-aria-label">
Sidebar Navigation
</span>
<slot name="sidebar-nav-before" />
<ClientOnly :is-client-only="isSidebarClientOnly || isClientOnly(sidebar) || isClientOnly(sidebarGroups)">
<VPSidebarGroup :items="sidebarGroups" :key="key" />
</ClientOnly>
<slot name="sidebar-nav-after" />
</nav>
</aside>

@ -2,6 +2,7 @@
import type { DefaultTheme } from 'vitepress/theme'
import { onBeforeUnmount, onMounted, ref } from 'vue'
import VPSidebarItem from './VPSidebarItem.vue'
import { isClientOnly } from '../../shared'
defineProps<{
items: DefaultTheme.SidebarItem[]
@ -33,7 +34,9 @@ onBeforeUnmount(() => {
class="group"
:class="{ 'no-transition': disableTransition }"
>
<ClientOnly :isClientOnly="isClientOnly(item)">
<VPSidebarItem :item="item" :depth="0" />
</ClientOnly>
</div>
</template>

@ -3,6 +3,7 @@ import type { DefaultTheme } from 'vitepress/theme'
import { computed } from 'vue'
import { useSidebarControl } from '../composables/sidebar'
import VPLink from './VPLink.vue'
import { isClientOnly } from '../../shared'
const props = defineProps<{
item: DefaultTheme.SidebarItem
@ -55,56 +56,32 @@ function onCaretClick() {
</script>
<template>
<ClientOnly :isClientOnly="isClientOnly(item)">
<component :is="sectionTag" class="VPSidebarItem" :class="classes">
<div
v-if="item.text"
class="item"
:role="itemRole"
v-on="
item.items
<div v-if="item.text" class="item" :role="itemRole" v-on="item.items
? { click: onItemInteraction, keydown: onItemInteraction }
: {}
"
:tabindex="item.items && 0"
>
" :tabindex="item.items && 0">
<div class="indicator" />
<VPLink
v-if="item.link"
:tag="linkTag"
class="link"
:href="item.link"
:rel="item.rel"
:target="item.target"
>
<VPLink v-if="item.link" :tag="linkTag" class="link" :href="item.link" :rel="item.rel" :target="item.target">
<component :is="textTag" class="text" v-html="item.text" />
</VPLink>
<component v-else :is="textTag" class="text" v-html="item.text" />
<div
v-if="item.collapsed != null && item.items && item.items.length"
class="caret"
role="button"
aria-label="toggle section"
@click="onCaretClick"
@keydown.enter="onCaretClick"
tabindex="0"
>
<div v-if="item.collapsed != null && item.items && item.items.length" class="caret" role="button"
aria-label="toggle section" @click="onCaretClick" @keydown.enter="onCaretClick" tabindex="0">
<span class="vpi-chevron-right caret-icon" />
</div>
</div>
<div v-if="item.items && item.items.length" class="items">
<template v-if="depth < 5">
<VPSidebarItem
v-for="i in item.items"
:key="i.text"
:item="i"
:depth="depth + 1"
/>
<VPSidebarItem v-for="i in item.items" :key="i.text" :item="i" :depth="depth + 1" />
</template>
</div>
</component>
</ClientOnly>
</template>
<style scoped>
@ -233,7 +210,9 @@ function onCaretClick() {
}
.VPSidebarItem.collapsed .caret-icon {
transform: rotate(0)/*rtl:rotate(180deg)*/;
transform: rotate(0)
/*rtl:rotate(180deg)*/
;
}
.VPSidebarItem.level-1 .items,

@ -11,7 +11,7 @@ import {
type ComputedRef,
type Ref
} from 'vue'
import { isActive } from '../../shared'
import { isActive, isClientOnly } from '../../shared'
import {
hasActiveLink as containsActiveLink,
getSidebar,
@ -96,6 +96,7 @@ export function useSidebar() {
hasAside,
leftAside,
isSidebarEnabled,
isSidebarClientOnly: isClientOnly(theme.value.sidebar),
open,
close,
toggle

@ -17,6 +17,7 @@ import type { RawConfigExports, SiteConfig, UserConfig } from './siteConfig'
export { resolvePages } from './plugins/dynamicRoutesPlugin'
export * from './siteConfig'
export { clientOnly, isClientOnly } from './shared'
const debug = _debug('vitepress:config')

@ -230,3 +230,14 @@ export function escapeHtml(str: string): string {
.replace(/"/g, '&quot;')
.replace(/&(?![\w#]+;)/g, '&amp;')
}
const CLIENT_ONLY = '[VP_CLIENT_ONLY]'
export function clientOnly<T extends object>(obj: T): T {
;(obj as any)[CLIENT_ONLY] = true
return obj
}
export function isClientOnly<T = any>(obj: T) {
return Boolean((obj as any)?.[CLIENT_ONLY])
}

Loading…
Cancel
Save