diff --git a/beta/package.json b/beta/package.json
index f38255fc..ee9d7798 100644
--- a/beta/package.json
+++ b/beta/package.json
@@ -37,8 +37,7 @@
"parse-numeric-range": "^1.2.0",
"react": "0.0.0-experimental-82c64e1a4-20220520",
"react-collapsed": "3.1.0",
- "react-dom": "0.0.0-experimental-82c64e1a4-20220520",
- "scroll-into-view-if-needed": "^2.2.25"
+ "react-dom": "0.0.0-experimental-82c64e1a4-20220520"
},
"devDependencies": {
"@babel/core": "^7.12.9",
diff --git a/beta/src/components/Layout/Nav/MobileNav.tsx b/beta/src/components/Layout/Nav/MobileNav.tsx
deleted file mode 100644
index 351ae879..00000000
--- a/beta/src/components/Layout/Nav/MobileNav.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) Facebook, Inc. and its affiliates.
- */
-
-import * as React from 'react';
-import cn from 'classnames';
-import {RouteItem} from 'components/Layout/useRouteMeta';
-import {useRouter} from 'next/router';
-import {useActiveSection} from 'hooks/useActiveSection';
-import {SidebarRouteTree} from '../Sidebar';
-import sidebarHome from '../../../sidebarHome.json';
-import sidebarLearn from '../../../sidebarLearn.json';
-import sidebarReference from '../../../sidebarReference.json';
-
-export function MobileNav() {
- // This is where we actually are according to the router.
- const section = useActiveSection();
-
- // Let the user switch tabs there and back without navigating.
- // Seed the tab state from the router, but keep it independent.
- const [tab, setTab] = React.useState(section);
-
- let tree = null;
- switch (tab) {
- case 'home':
- tree = sidebarHome.routes[0];
- break;
- case 'learn':
- tree = sidebarLearn.routes[0];
- break;
- case 'apis':
- tree = sidebarReference.routes[0];
- break;
- }
-
- return (
- <>
-
- setTab('home')}>
- Home
-
- setTab('learn')}>
- Learn
-
- setTab('apis')}>
- API
-
-
- {/* No fallback UI so need to be careful not to suspend directly inside. */}
-
-
-
- >
- );
-}
-
-function TabButton({
- children,
- onClick,
- isActive,
-}: {
- children: any;
- onClick: (event: React.MouseEvent) => void;
- isActive: boolean;
-}) {
- const classes = cn(
- 'inline-flex items-center w-full border-b-2 justify-center text-base leading-9 px-3 py-0.5 hover:text-link hover:gray-5',
- {
- 'text-link dark:text-link-dark dark:border-link-dark border-link font-bold':
- isActive,
- 'border-transparent': !isActive,
- }
- );
- return (
-
- );
-}
diff --git a/beta/src/components/Layout/Nav/Nav.tsx b/beta/src/components/Layout/Nav/Nav.tsx
index 30b6a06e..e682dfff 100644
--- a/beta/src/components/Layout/Nav/Nav.tsx
+++ b/beta/src/components/Layout/Nav/Nav.tsx
@@ -6,16 +6,25 @@ import * as React from 'react';
import cn from 'classnames';
import NextLink from 'next/link';
import {useRouter} from 'next/router';
+import {
+ clearAllBodyScrollLocks,
+ disableBodyScroll,
+ enableBodyScroll,
+} from 'body-scroll-lock';
import {IconClose} from 'components/Icon/IconClose';
import {IconHamburger} from 'components/Icon/IconHamburger';
import {Search} from 'components/Search';
-import {MenuContext} from 'components/useMenu';
import {useActiveSection} from 'hooks/useActiveSection';
-
import {Logo} from '../../Logo';
import {Feedback} from '../Feedback';
import NavLink from './NavLink';
+import {SidebarContext} from 'components/Layout/useRouteMeta';
+import {SidebarRouteTree} from '../Sidebar/SidebarRouteTree';
+import type {RouteItem} from '../useRouteMeta';
+import sidebarHome from '../../../sidebarHome.json';
+import sidebarLearn from '../../../sidebarLearn.json';
+import sidebarReference from '../../../sidebarReference.json';
declare global {
interface Window {
@@ -88,12 +97,80 @@ const lightIcon = (
);
export default function Nav() {
- const {isOpen, toggleOpen} = React.useContext(MenuContext);
+ const [isOpen, setIsOpen] = React.useState(false);
const [showFeedback, setShowFeedback] = React.useState(false);
+ const menuRef = React.useRef(null);
const feedbackAutohideRef = React.useRef(null);
const section = useActiveSection();
+ const {asPath} = useRouter();
const feedbackPopupRef = React.useRef(null);
+ // In desktop mode, use the route tree for current route.
+ let routeTree: RouteItem = React.useContext(SidebarContext);
+ // In mobile mode, let the user switch tabs there and back without navigating.
+ // Seed the tab state from the router, but keep it independent.
+ const [tab, setTab] = React.useState(section);
+ const [prevSection, setPrevSection] = React.useState(section);
+ if (prevSection !== section) {
+ setPrevSection(section);
+ setTab(section);
+ }
+ if (isOpen) {
+ switch (tab) {
+ case 'home':
+ routeTree = sidebarHome as RouteItem;
+ break;
+ case 'learn':
+ routeTree = sidebarLearn as RouteItem;
+ break;
+ case 'apis':
+ routeTree = sidebarReference as RouteItem;
+ break;
+ }
+ }
+ // HACK. Fix up the data structures instead.
+ if ((routeTree as any).routes.length === 1) {
+ routeTree = (routeTree as any).routes[0];
+ }
+
+ // While the overlay is open, disable body scroll.
+ React.useEffect(() => {
+ if (isOpen) {
+ const preferredScrollParent = menuRef.current!;
+ disableBodyScroll(preferredScrollParent);
+ return () => enableBodyScroll(preferredScrollParent);
+ } else {
+ return undefined;
+ }
+ }, [isOpen]);
+
+ // Close the overlay on any navigation.
+ React.useEffect(() => {
+ setIsOpen(false);
+ }, [asPath]);
+
+ // Also close the overlay if the window gets resized past mobile layout.
+ // (This is also important because we don't want to keep the body locked!)
+ React.useEffect(() => {
+ const media = window.matchMedia(`(max-width: 1023px)`);
+ function closeIfNeeded() {
+ if (!media.matches) {
+ setIsOpen(false);
+ }
+ }
+ closeIfNeeded();
+ media.addEventListener('change', closeIfNeeded);
+ return () => {
+ media.removeEventListener('change', closeIfNeeded);
+ };
+ }, []);
+
+ function handleFeedback() {
+ clearTimeout(feedbackAutohideRef.current);
+ setShowFeedback(!showFeedback);
+ }
+
+ // Hide the Feedback widget on any click outside.
React.useEffect(() => {
if (!showFeedback) {
return;
@@ -113,121 +190,186 @@ export default function Nav() {
capture: true,
});
}, [showFeedback]);
-
- function handleFeedback() {
- clearTimeout(feedbackAutohideRef.current);
- setShowFeedback(!showFeedback);
- }
-
return (
-