Skip to content

Commit

Permalink
[v3] consider navbar height for the anchor intersection observer (#3249)
Browse files Browse the repository at this point in the history
* [v3] consider navbar height for the anchor intersection observer

* fix root margin must be specified in pixels or percent

* polish

---------

Co-authored-by: Dimitri POSTOLOV <dmytropostolov@gmail.com>
  • Loading branch information
87xie and dimaMachina authored Sep 15, 2024
1 parent 0df30b1 commit cee94a6
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/red-squids-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'nextra-theme-docs': patch
---

Consider the navbar height when setting the root margin for the active anchor intersection observer
2 changes: 1 addition & 1 deletion packages/nextra-theme-docs/src/components/head.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function Head(): ReactElement {
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
/>
<style>{`:root{--nextra-primary-hue:${lightHue}deg;--nextra-primary-saturation:${lightSaturation}%;--nextra-navbar-height:4rem;--nextra-menu-height:3.75rem;--nextra-banner-height:2.5rem;--nextra-bg:${bgColor.light};}.dark{--nextra-primary-hue:${darkHue}deg;--nextra-primary-saturation:${darkSaturation}%;--nextra-bg:${bgColor.dark};}`}</style>
<style>{`:root{--nextra-primary-hue:${lightHue}deg;--nextra-primary-saturation:${lightSaturation}%;--nextra-navbar-height:64px;--nextra-menu-height:3.75rem;--nextra-banner-height:2.5rem;--nextra-bg:${bgColor.light};}.dark{--nextra-primary-hue:${darkHue}deg;--nextra-primary-saturation:${darkSaturation}%;--nextra-bg:${bgColor.dark};}`}</style>
{head}
</NextHead>
)
Expand Down
2 changes: 0 additions & 2 deletions packages/nextra-theme-docs/src/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import { getGitIssueUrl, useGitEditUrl } from './utils'

export const DEFAULT_LOCALE = 'en-US'

export const IS_BROWSER = typeof window !== 'undefined'

export type DocsThemeConfig = z.infer<typeof themeSchema>
export type PartialDocsThemeConfig = z.infer<typeof publicThemeSchema>

Expand Down
23 changes: 15 additions & 8 deletions packages/nextra-theme-docs/src/contexts/active-anchor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Dispatch, ReactElement, ReactNode, SetStateAction } from 'react'
import { createContext, useContext, useRef, useState } from 'react'
import { IS_BROWSER } from '../constants'
import { createContext, useContext, useEffect, useRef, useState } from 'react'

type ActiveAnchor = Record<
string,
Expand All @@ -12,12 +11,12 @@ type ActiveAnchor = Record<
}
>

const ActiveAnchorContext = createContext<ActiveAnchor>({})
const ActiveAnchorContext = createContext<ActiveAnchor>(null!)
ActiveAnchorContext.displayName = 'ActiveAnchor'

const SetActiveAnchorContext = createContext<
Dispatch<SetStateAction<ActiveAnchor>>
>(v => v)
>(null!)
SetActiveAnchorContext.displayName = 'SetActiveAnchor'

const IntersectionObserverContext = createContext<IntersectionObserver | null>(
Expand All @@ -42,8 +41,12 @@ export const ActiveAnchorProvider = ({
children: ReactNode
}): ReactElement => {
const [activeAnchor, setActiveAnchor] = useState<ActiveAnchor>({})
const observerRef = useRef<IntersectionObserver | null>(null)
if (IS_BROWSER && !observerRef.current) {
const observerRef = useRef<IntersectionObserver>(null!)

useEffect(() => {
const navbarHeight = getComputedStyle(document.body).getPropertyValue(
'--nextra-navbar-height'
)
observerRef.current = new IntersectionObserver(
entries => {
setActiveAnchor(f => {
Expand Down Expand Up @@ -91,11 +94,15 @@ export const ActiveAnchorProvider = ({
})
},
{
rootMargin: '0px 0px -50%',
rootMargin: `-${navbarHeight} 0px -50%`,
threshold: [0, 1]
}
)
}

return () => {
observerRef.current.disconnect()
}
}, [])
return (
<ActiveAnchorContext.Provider value={activeAnchor}>
<SetActiveAnchorContext.Provider value={setActiveAnchor}>
Expand Down
7 changes: 3 additions & 4 deletions packages/nextra-theme-docs/src/mdx-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,13 @@ const createHeading = (
const obRef = useRef<HTMLAnchorElement>(null)

useEffect(() => {
if (!id) return
const heading = obRef.current
if (!heading) return
if (!id || !observer || !heading) return
observer.observe(heading)
slugs.set(heading, [id, (context.index += 1)])
observer?.observe(heading)

return () => {
observer?.disconnect()
observer.disconnect()
slugs.delete(heading)
setActiveAnchor(f => {
const ret = { ...f }
Expand Down

0 comments on commit cee94a6

Please sign in to comment.