22 * Copyright (c) Facebook, Inc. and its affiliates.
33 */
44
5- import {
6- useState ,
7- useRef ,
8- useCallback ,
9- useEffect ,
10- startTransition ,
11- Suspense ,
12- } from 'react' ;
13- import Image from 'next/image' ;
14- import * as React from 'react' ;
155import cn from 'classnames' ;
6+ import Image from 'next/image' ;
167import NextLink from 'next/link' ;
17- import { useRouter } from 'next/router ' ;
18- import { disableBodyScroll , enableBodyScroll } from 'body-scroll-lock ' ;
8+ import * as React from 'react ' ;
9+ import { Suspense } from 'react ' ;
1910
2011import { IconClose } from 'components/Icon/IconClose' ;
2112import { IconHamburger } from 'components/Icon/IconHamburger' ;
2213import { IconSearch } from 'components/Icon/IconSearch' ;
14+ import NavItem from 'components/NavItem' ;
2315import { Search } from 'components/Search' ;
16+ import { useMenuAndScroll } from 'hooks' ;
17+ import { siteConfig } from 'siteConfig' ;
2418import { Logo } from '../../Logo' ;
2519import { Feedback } from '../Feedback' ;
2620import { SidebarRouteTree } from '../Sidebar' ;
2721import type { RouteItem } from '../getRouteMeta' ;
28- import { siteConfig } from 'siteConfig' ;
2922import BrandMenu from './BrandMenu' ;
3023
3124declare global {
@@ -120,23 +113,6 @@ function Link({
120113 ) ;
121114}
122115
123- function NavItem ( { url, isActive, children} : any ) {
124- return (
125- < div className = "flex flex-auto sm:flex-1" >
126- < Link
127- href = { url }
128- className = { cn (
129- 'active:scale-95 transition-transform w-full text-center outline-link py-1.5 px-1.5 xs:px-3 sm:px-4 rounded-full capitalize whitespace-nowrap' ,
130- ! isActive && 'hover:bg-primary/5 hover:dark:bg-primary-dark/5' ,
131- isActive &&
132- 'bg-highlight dark:bg-highlight-dark text-link dark:text-link-dark'
133- ) } >
134- { children }
135- </ Link >
136- </ div >
137- ) ;
138- }
139-
140116function Kbd ( props : { children ?: React . ReactNode ; wide ?: boolean } ) {
141117 const { wide, ...rest } = props ;
142118 const width = wide ? 'w-10' : 'w-5' ;
@@ -158,77 +134,16 @@ export default function TopNav({
158134 breadcrumbs : RouteItem [ ] ;
159135 section : 'learn' | 'reference' | 'community' | 'blog' | 'home' | 'unknown' ;
160136} ) {
161- const [ isMenuOpen , setIsMenuOpen ] = useState ( false ) ;
162- const [ showSearch , setShowSearch ] = useState ( false ) ;
163- const [ isScrolled , setIsScrolled ] = useState ( false ) ;
164- const scrollParentRef = useRef < HTMLDivElement > ( null ) ;
165- const { asPath} = useRouter ( ) ;
166-
167- // HACK. Fix up the data structures instead.
168- if ( ( routeTree as any ) . routes . length === 1 ) {
169- routeTree = ( routeTree as any ) . routes [ 0 ] ;
170- }
171-
172- // While the overlay is open, disable body scroll.
173- useEffect ( ( ) => {
174- if ( isMenuOpen ) {
175- const preferredScrollParent = scrollParentRef . current ! ;
176- disableBodyScroll ( preferredScrollParent ) ;
177- return ( ) => enableBodyScroll ( preferredScrollParent ) ;
178- } else {
179- return undefined ;
180- }
181- } , [ isMenuOpen ] ) ;
182-
183- // Close the overlay on any navigation.
184- useEffect ( ( ) => {
185- setIsMenuOpen ( false ) ;
186- } , [ asPath ] ) ;
187-
188- // Also close the overlay if the window gets resized past mobile layout.
189- // (This is also important because we don't want to keep the body locked!)
190- useEffect ( ( ) => {
191- const media = window . matchMedia ( `(max-width: 1023px)` ) ;
192-
193- function closeIfNeeded ( ) {
194- if ( ! media . matches ) {
195- setIsMenuOpen ( false ) ;
196- }
197- }
198-
199- closeIfNeeded ( ) ;
200- media . addEventListener ( 'change' , closeIfNeeded ) ;
201- return ( ) => {
202- media . removeEventListener ( 'change' , closeIfNeeded ) ;
203- } ;
204- } , [ ] ) ;
205-
206- const scrollDetectorRef = useRef ( null ) ;
207- useEffect ( ( ) => {
208- const observer = new IntersectionObserver (
209- ( entries ) => {
210- entries . forEach ( ( entry ) => {
211- setIsScrolled ( ! entry . isIntersecting ) ;
212- } ) ;
213- } ,
214- {
215- root : null ,
216- rootMargin : `0px 0px` ,
217- threshold : 0 ,
218- }
219- ) ;
220- observer . observe ( scrollDetectorRef . current ! ) ;
221- return ( ) => observer . disconnect ( ) ;
222- } , [ ] ) ;
223-
224- const onOpenSearch = useCallback ( ( ) => {
225- startTransition ( ( ) => {
226- setShowSearch ( true ) ;
227- } ) ;
228- } , [ ] ) ;
229- const onCloseSearch = useCallback ( ( ) => {
230- setShowSearch ( false ) ;
231- } , [ ] ) ;
137+ const {
138+ isMenuOpen,
139+ setIsMenuOpen,
140+ showSearch,
141+ isScrolled,
142+ scrollParentRef,
143+ scrollDetectorRef,
144+ onOpenSearch,
145+ onCloseSearch,
146+ } = useMenuAndScroll ( routeTree ) ;
232147
233148 return (
234149 < >
0 commit comments