fix(theme): render sidebar active state during ssr

dcb7a755 turned hasActiveLink from a computed into a ref fed only by a
non-immediate watch and onMounted, so is-active/has-active stopped
rendering in static HTML. This broke pre-hydration sidebar highlighting
and the docsearch crawler's lvl0 selector (section.has-active div h2),
which has indexed every record as the defaultValue since then.

Run the update once during setup with the hash check skipped: the server
and the client's initial render compute identical path-only state, so
hydration stays clean; the watch and onMounted refine with the real hash
after mount.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/5391/head
Divyansh Singh 3 weeks ago
parent b0da3845f8
commit 005aa5c3e8

@ -83,12 +83,14 @@ export function useSidebarItemControl(
const isActiveLink = ref(false)
const hasActiveLink = ref(false)
function updateActiveLink(): void {
function updateActiveLink(skipHashCheck = false): void {
if (item.value.link) {
isActiveLink.value = isActive(
route.data.relativePath,
route.hash,
item.value.link
item.value.link,
false,
skipHashCheck
)
} else {
isActiveLink.value = false
@ -105,15 +107,21 @@ export function useSidebarItemControl(
hasActiveLink.value = containsActiveLink(
route.data.relativePath,
route.hash,
item.value.items
item.value.items,
skipHashCheck
)
if (hasActiveLink.value) {
nextTick(() => (collapsed.value = false))
}
}
watch([item, route], updateActiveLink)
onMounted(updateActiveLink)
// runs during setup so active classes render in SSR output too; the hash
// isn't known on the server (and may differ at hydration), so it's skipped
// until mounted
updateActiveLink(true)
watch([item, route], () => updateActiveLink())
onMounted(() => updateActiveLink())
const hasChildren = computed(() => {
return !!(item.value.items && item.value.items.length)

@ -100,16 +100,17 @@ export function getFlatSideBarLinks(sidebar: SidebarItem[]): SidebarLink[] {
export function hasActiveLink(
path: string,
hash: string,
items: SidebarItem | SidebarItem[]
items: SidebarItem | SidebarItem[],
skipHashCheck = false
): boolean {
if (Array.isArray(items)) {
return items.some((item) => hasActiveLink(path, hash, item))
return items.some((item) => hasActiveLink(path, hash, item, skipHashCheck))
}
if (items.link && isActive(path, hash, items.link)) {
if (items.link && isActive(path, hash, items.link, false, skipHashCheck)) {
return true
}
if (items.items) {
return hasActiveLink(path, hash, items.items)
return hasActiveLink(path, hash, items.items, skipHashCheck)
}
return false
}

Loading…
Cancel
Save