mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-27 03:08:06 +00:00
[beta] RFC - homepage and top nav
This commit is contained in:
@@ -12,34 +12,40 @@ import {IconTwitter} from 'components/Icon/IconTwitter';
|
||||
import {IconGitHub} from 'components/Icon/IconGitHub';
|
||||
import {IconNavArrow} from 'components/Icon/IconNavArrow';
|
||||
|
||||
export function Footer() {
|
||||
interface FooterProps {
|
||||
hideSurvey?: boolean;
|
||||
}
|
||||
|
||||
export function Footer({hideSurvey = false}: FooterProps) {
|
||||
const socialLinkClasses = 'hover:text-primary dark:text-primary-dark';
|
||||
return (
|
||||
<>
|
||||
<div className="self-stretch w-full">
|
||||
<div className="mx-auto w-full px-5 sm:px-12 md:px-12 pt-10 md:pt-12 lg:pt-10">
|
||||
<hr className="max-w-7xl mx-auto border-border dark:border-border-dark" />
|
||||
<div className="flex flex-col items-center m-4 p-4">
|
||||
<p className="font-bold text-primary dark:text-primary-dark text-lg mb-4">
|
||||
How do you like these docs?
|
||||
</p>
|
||||
<div>
|
||||
<ButtonLink
|
||||
href="https://www.surveymonkey.co.uk/r/PYRPF3X"
|
||||
className="mt-1"
|
||||
type="primary"
|
||||
size="md"
|
||||
target="_blank">
|
||||
Take our survey!
|
||||
<IconNavArrow
|
||||
displayDirection="right"
|
||||
className="inline ml-1"
|
||||
/>
|
||||
</ButtonLink>
|
||||
{!hideSurvey && (
|
||||
<div className="mx-auto w-full px-5 sm:px-12 md:px-12 pt-10 md:pt-12 lg:pt-10">
|
||||
<hr className="max-w-7xl mx-auto border-border dark:border-border-dark" />
|
||||
<div className="flex flex-col items-center m-4 p-4">
|
||||
<p className="font-bold text-primary dark:text-primary-dark text-lg mb-4">
|
||||
How do you like these docs?
|
||||
</p>
|
||||
<div>
|
||||
<ButtonLink
|
||||
href="https://www.surveymonkey.co.uk/r/PYRPF3X"
|
||||
className="mt-1"
|
||||
type="primary"
|
||||
size="md"
|
||||
target="_blank">
|
||||
Take our survey!
|
||||
<IconNavArrow
|
||||
displayDirection="right"
|
||||
className="inline ml-1"
|
||||
/>
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="max-w-7xl mx-auto border-border dark:border-border-dark" />
|
||||
</div>
|
||||
)}
|
||||
<hr className="max-w-7xl mx-auto border-border dark:border-border-dark" />
|
||||
<footer className="text-secondary dark:text-secondary-dark py-12 px-5 sm:px-12 md:px-12 sm:py-12 md:py-16 lg:py-14">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-5 gap-x-12 gap-y-8 max-w-7xl mx-auto ">
|
||||
<ExternalLink
|
||||
|
||||
81
beta/src/components/Layout/HomePage.tsx
Normal file
81
beta/src/components/Layout/HomePage.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
import {Suspense} from 'react';
|
||||
import * as React from 'react';
|
||||
import {useRouter} from 'next/router';
|
||||
import {Footer} from './Footer';
|
||||
import SocialBanner from '../SocialBanner';
|
||||
import {Seo} from 'components/Seo';
|
||||
import {getRouteMeta} from './getRouteMeta';
|
||||
import type {RouteItem} from 'components/Layout/getRouteMeta';
|
||||
import ButtonLink from '../ButtonLink';
|
||||
import {IconNavArrow} from '../Icon/IconNavArrow';
|
||||
import {Search} from '../Search';
|
||||
import {TopNav} from './TopNav';
|
||||
import(/* webpackPrefetch: true */ '../MDX/CodeBlock/CodeBlock');
|
||||
|
||||
interface PageProps {
|
||||
routeTree: RouteItem;
|
||||
meta: {title?: string; description?: string};
|
||||
}
|
||||
|
||||
export function HomePage({routeTree, meta}: PageProps) {
|
||||
const {asPath} = useRouter();
|
||||
const cleanedPath = asPath.split(/[\?\#]/)[0];
|
||||
const {route, nextRoute, prevRoute, breadcrumbs} = getRouteMeta(
|
||||
cleanedPath,
|
||||
routeTree
|
||||
);
|
||||
const title = meta.title || route?.title || '';
|
||||
return (
|
||||
<>
|
||||
<SocialBanner />
|
||||
<TopNav routeTree={routeTree} breadcrumbs={breadcrumbs} />
|
||||
{/* No fallback UI so need to be careful not to suspend directly inside. */}
|
||||
<Suspense fallback={null}>
|
||||
<main className="min-w-0">
|
||||
<div className="lg:hidden h-16 mb-2" />
|
||||
<article className="break-words" key={asPath}>
|
||||
<div className="pl-0">
|
||||
<Seo title={title} isHomePage={true} />
|
||||
<div className="px-5 sm:px-12">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="flex flex-col justify-center items-center m-16">
|
||||
<h1 className="text-6xl text-center flex wrap font-bold leading-tight text-primary dark:text-primary-dark">
|
||||
The library for user interfaces
|
||||
</h1>
|
||||
<h2 className="text-xl mt-2 text-center flex wrap leading-tight text-primary dark:text-primary-dark">
|
||||
Neque porro quisquam est qui dolorem ipsum quia dolor sit
|
||||
amet, consectetur, adipisci velit...
|
||||
</h2>
|
||||
<div className="flex-col-reverse md:flex-row flex mt-4 md:mt-16">
|
||||
<div>
|
||||
<ButtonLink
|
||||
href="/learn"
|
||||
className="w-full mt-4 md:mt-0"
|
||||
type="primary"
|
||||
size="md">
|
||||
Learn React
|
||||
<IconNavArrow
|
||||
displayDirection="right"
|
||||
className="inline ml-1"
|
||||
/>
|
||||
</ButtonLink>
|
||||
</div>
|
||||
<div className=" w-72 md:ml-4">
|
||||
<Search fullsize />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<Footer hideSurvey />
|
||||
</main>
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import cn from 'classnames';
|
||||
import {ExternalLink} from 'components/ExternalLink';
|
||||
import NextLink from 'next/link';
|
||||
|
||||
interface NavLinkProps {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export default function NavLink({href, children, isActive}: NavLinkProps) {
|
||||
const classes = cn(
|
||||
{
|
||||
'text-link border-link dark:text-link-dark dark:border-link-dark font-bold':
|
||||
isActive,
|
||||
},
|
||||
{'border-transparent': !isActive},
|
||||
'inline-flex w-full items-center border-b-2 justify-center text-base leading-9 px-3 py-0.5 hover:text-link dark:hover:text-link-dark whitespace-nowrap'
|
||||
);
|
||||
|
||||
if (href.startsWith('https://')) {
|
||||
return (
|
||||
<ExternalLink href={href} className={classes}>
|
||||
{children}
|
||||
</ExternalLink>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NextLink href={href}>
|
||||
<a className={classes}>{children}</a>
|
||||
</NextLink>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
import {Suspense} from 'react';
|
||||
import * as React from 'react';
|
||||
import {useRouter} from 'next/router';
|
||||
import {Nav} from './Nav';
|
||||
import {SidebarNav} from './SidebarNav';
|
||||
import {Footer} from './Footer';
|
||||
import {Toc} from './Toc';
|
||||
import SocialBanner from '../SocialBanner';
|
||||
@@ -14,10 +14,10 @@ import {Seo} from 'components/Seo';
|
||||
import PageHeading from 'components/PageHeading';
|
||||
import {getRouteMeta} from './getRouteMeta';
|
||||
import {TocContext} from '../MDX/TocContext';
|
||||
import sidebarLearn from '../../sidebarLearn.json';
|
||||
import sidebarReference from '../../sidebarReference.json';
|
||||
import type {TocItem} from 'components/MDX/TocContext';
|
||||
import type {RouteItem} from 'components/Layout/getRouteMeta';
|
||||
import {HomePage} from './HomePage';
|
||||
import {TopNav} from './TopNav';
|
||||
|
||||
import(/* webpackPrefetch: true */ '../MDX/CodeBlock/CodeBlock');
|
||||
|
||||
@@ -39,21 +39,21 @@ export function Page({children, toc, routeTree, meta, section}: PageProps) {
|
||||
const title = meta.title || route?.title || '';
|
||||
const description = meta.description || route?.description || '';
|
||||
const isHomePage = cleanedPath === '/';
|
||||
|
||||
if (isHomePage) {
|
||||
return <HomePage routeTree={routeTree} meta={meta} />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<SocialBanner />
|
||||
<TopNav routeTree={routeTree} breadcrumbs={breadcrumbs} />
|
||||
<div className="grid grid-cols-only-content lg:grid-cols-sidebar-content 2xl:grid-cols-sidebar-content-toc">
|
||||
<div className="fixed lg:sticky top-0 left-0 right-0 py-0 shadow lg:shadow-none z-50">
|
||||
<Nav
|
||||
routeTree={routeTree}
|
||||
breadcrumbs={breadcrumbs}
|
||||
section={section}
|
||||
/>
|
||||
<SidebarNav routeTree={routeTree} breadcrumbs={breadcrumbs} />
|
||||
</div>
|
||||
{/* No fallback UI so need to be careful not to suspend directly inside. */}
|
||||
<Suspense fallback={null}>
|
||||
<main className="min-w-0">
|
||||
<div className="lg:hidden h-16 mb-2" />
|
||||
<article className="break-words" key={asPath}>
|
||||
<div className="pl-0">
|
||||
<Seo title={title} isHomePage={isHomePage} />
|
||||
|
||||
65
beta/src/components/Layout/SidebarNav/SidebarNav.tsx
Normal file
65
beta/src/components/Layout/SidebarNav/SidebarNav.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
import {Suspense} from 'react';
|
||||
import * as React from 'react';
|
||||
import cn from 'classnames';
|
||||
import {Search} from 'components/Search';
|
||||
import {Feedback} from '../Feedback';
|
||||
import {SidebarRouteTree} from '../Sidebar/SidebarRouteTree';
|
||||
import type {RouteItem} from '../getRouteMeta';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__theme: string;
|
||||
__setPreferredTheme: (theme: string) => void;
|
||||
}
|
||||
}
|
||||
|
||||
export default function SidebarNav({
|
||||
routeTree,
|
||||
breadcrumbs,
|
||||
}: {
|
||||
routeTree: RouteItem;
|
||||
breadcrumbs: RouteItem[];
|
||||
}) {
|
||||
// HACK. Fix up the data structures instead.
|
||||
if ((routeTree as any).routes.length === 1) {
|
||||
routeTree = (routeTree as any).routes[0];
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('sticky top-0 lg:bottom-0 lg:h-screen flex flex-col')}>
|
||||
<nav className="items-center w-full flex lg:block justify-between bg-wash dark:bg-wash-dark pt-0 lg:pt-4 pr-5 lg:px-5 z-50">
|
||||
<div className="hidden lg:block sm:pt-10 lg:pt-0">
|
||||
<Search />
|
||||
</div>
|
||||
</nav>
|
||||
<div className="overflow-y-scroll no-bg-scrollbar lg:w-[336px] grow bg-wash dark:bg-wash-dark">
|
||||
<aside
|
||||
className={cn(
|
||||
`lg:grow lg:flex flex-col w-full pb-8 lg:pb-0 lg:max-w-xs z-10 hidden lg:block`
|
||||
)}>
|
||||
<nav
|
||||
role="navigation"
|
||||
style={{'--bg-opacity': '.2'} as React.CSSProperties} // Need to cast here because CSS vars aren't considered valid in TS types (cuz they could be anything)
|
||||
className="w-full lg:h-auto grow pr-0 lg:pr-5 pt-6 lg:py-6 md:pt-4 lg:pt-4 scrolling-touch scrolling-gpu">
|
||||
{/* No fallback UI so need to be careful not to suspend directly inside. */}
|
||||
<Suspense fallback={null}>
|
||||
<SidebarRouteTree
|
||||
routeTree={routeTree}
|
||||
breadcrumbs={breadcrumbs}
|
||||
isForceExpanded={false}
|
||||
/>
|
||||
</Suspense>
|
||||
<div className="h-20" />
|
||||
</nav>
|
||||
<div className="fixed bottom-0 hidden lg:block">
|
||||
<Feedback />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
5
beta/src/components/Layout/SidebarNav/index.tsx
Normal file
5
beta/src/components/Layout/SidebarNav/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
export {default as SidebarNav} from './SidebarNav';
|
||||
@@ -14,11 +14,9 @@ import {IconHamburger} from 'components/Icon/IconHamburger';
|
||||
import {Search} from 'components/Search';
|
||||
import {Logo} from '../../Logo';
|
||||
import {Feedback} from '../Feedback';
|
||||
import NavLink from './NavLink';
|
||||
import {SidebarRouteTree} from '../Sidebar/SidebarRouteTree';
|
||||
import type {RouteItem} from '../getRouteMeta';
|
||||
import sidebarLearn from '../../../sidebarLearn.json';
|
||||
import sidebarReference from '../../../sidebarReference.json';
|
||||
import {SidebarLink} from '../Sidebar';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -90,14 +88,37 @@ const lightIcon = (
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default function Nav({
|
||||
const githubIcon = (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24">
|
||||
<g fill="currentColor">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
function Link({href, children, ...props}: JSX.IntrinsicElements['a']) {
|
||||
return (
|
||||
<NextLink href={`${href}`}>
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
|
||||
<a
|
||||
className="inline text-primary dark:text-primary-dark hover:text-link hover:dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal"
|
||||
{...props}>
|
||||
{children}
|
||||
</a>
|
||||
</NextLink>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TopNav({
|
||||
routeTree,
|
||||
breadcrumbs,
|
||||
section,
|
||||
}: {
|
||||
routeTree: RouteItem;
|
||||
breadcrumbs: RouteItem[];
|
||||
section: 'learn' | 'reference' | 'home';
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [showFeedback, setShowFeedback] = useState(false);
|
||||
@@ -106,25 +127,6 @@ export default function Nav({
|
||||
const {asPath} = useRouter();
|
||||
const feedbackPopupRef = useRef<null | HTMLDivElement>(null);
|
||||
|
||||
// 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] = useState(section);
|
||||
const [prevSection, setPrevSection] = useState(section);
|
||||
if (prevSection !== section) {
|
||||
setPrevSection(section);
|
||||
setTab(section);
|
||||
}
|
||||
if (isOpen) {
|
||||
switch (tab) {
|
||||
case 'home':
|
||||
case 'learn':
|
||||
routeTree = sidebarLearn as RouteItem;
|
||||
break;
|
||||
case 'reference':
|
||||
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];
|
||||
@@ -150,11 +152,13 @@ export default function Nav({
|
||||
// (This is also important because we don't want to keep the body locked!)
|
||||
useEffect(() => {
|
||||
const media = window.matchMedia(`(max-width: 1023px)`);
|
||||
|
||||
function closeIfNeeded() {
|
||||
if (!media.matches) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
closeIfNeeded();
|
||||
media.addEventListener('change', closeIfNeeded);
|
||||
return () => {
|
||||
@@ -172,6 +176,7 @@ export default function Nav({
|
||||
if (!showFeedback) {
|
||||
return;
|
||||
}
|
||||
|
||||
function handleDocumentClickCapture(e: MouseEvent) {
|
||||
if (!feedbackPopupRef.current!.contains(e.target as any)) {
|
||||
e.stopPropagation();
|
||||
@@ -179,6 +184,7 @@ export default function Nav({
|
||||
setShowFeedback(false);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', handleDocumentClickCapture, {
|
||||
capture: true,
|
||||
});
|
||||
@@ -188,19 +194,13 @@ export default function Nav({
|
||||
});
|
||||
}, [showFeedback]);
|
||||
|
||||
function selectTab(nextTab: 'learn' | 'reference') {
|
||||
setTab(nextTab);
|
||||
scrollParentRef.current!.scrollTop = 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'sticky top-0 lg:bottom-0 lg:h-screen flex flex-col',
|
||||
isOpen && 'h-screen'
|
||||
isOpen && 'h-screen sticky top-0 lg:bottom-0 lg:h-screen flex flex-col'
|
||||
)}>
|
||||
<nav className="items-center w-full flex lg:block justify-between bg-wash dark:bg-wash-dark pt-0 lg:pt-4 pr-5 lg:px-5 z-50">
|
||||
<div className="xl:w-full xl:max-w-xs flex items-center">
|
||||
<nav className="items-center w-full flex md:block justify-between bg-wash dark:bg-wash-dark pt-4 pr-5 md:px-5 z-50">
|
||||
<div className="w-full flex items-center text-l">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Menu"
|
||||
@@ -211,7 +211,7 @@ export default function Nav({
|
||||
{isOpen ? <IconClose /> : <IconHamburger />}
|
||||
</button>
|
||||
<NextLink href="/">
|
||||
<a className="inline-flex text-l font-normal items-center text-primary dark:text-primary-dark py-1 mr-0 sm:mr-3 whitespace-nowrap">
|
||||
<a className="inline-flex text-l font-normal items-center text-primary dark:text-primary-dark py-1 mr-3 whitespace-nowrap">
|
||||
<Logo className="text-sm mr-2 w-8 h-8 text-link dark:text-link-dark" />
|
||||
React Docs
|
||||
</a>
|
||||
@@ -221,167 +221,184 @@ export default function Nav({
|
||||
Beta
|
||||
</div>
|
||||
</div>
|
||||
<div className="block dark:hidden">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Use Dark Mode"
|
||||
onClick={() => {
|
||||
window.__setPreferredTheme('dark');
|
||||
}}
|
||||
className="hidden lg:flex items-center h-full pr-2">
|
||||
{darkIcon}
|
||||
</button>
|
||||
<div className="hidden lg:flex">
|
||||
<div className="block mr-4">
|
||||
<Link href="/learn">Learn</Link>
|
||||
</div>
|
||||
<div className="block mr-4">
|
||||
<Link href="/reference/react">Reference</Link>
|
||||
</div>
|
||||
<div className="block mr-4">
|
||||
<Link href="/community">Community</Link>
|
||||
</div>
|
||||
<div className="block mr-4">
|
||||
<Link href="/blog">Blog</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden dark:block">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Use Light Mode"
|
||||
onClick={() => {
|
||||
window.__setPreferredTheme('light');
|
||||
}}
|
||||
className="hidden lg:flex items-center h-full pr-2">
|
||||
{lightIcon}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{!isOpen && (
|
||||
<div className="hidden lg:block sm:pt-10 lg:pt-4">
|
||||
<div className="hidden md:flex sm:w-full lg:hidden">
|
||||
<Search />
|
||||
</div>
|
||||
)}
|
||||
<div className="px-0 pt-2 w-full 2xl:max-w-xs hidden lg:flex items-center self-center border-b-0 lg:border-b border-border dark:border-border-dark">
|
||||
<NavLink
|
||||
href="/learn"
|
||||
isActive={section === 'learn' || section === 'home'}>
|
||||
Learn
|
||||
</NavLink>
|
||||
<NavLink href="/reference/react" isActive={section === 'reference'}>
|
||||
Reference
|
||||
</NavLink>
|
||||
</div>
|
||||
<div className="flex my-4 h-10 mx-0 w-full lg:hidden justify-end lg:max-w-sm">
|
||||
<Search />
|
||||
<button
|
||||
aria-label="Give feedback"
|
||||
type="button"
|
||||
className={cn(
|
||||
'inline-flex lg:hidden items-center rounded-full px-1.5 ml-4 lg:ml-6 relative top-px',
|
||||
{
|
||||
'bg-card dark:bg-card-dark': showFeedback,
|
||||
}
|
||||
)}
|
||||
onClick={handleFeedback}>
|
||||
{feedbackIcon}
|
||||
</button>
|
||||
<div className="block dark:hidden">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Use Dark Mode"
|
||||
onClick={() => {
|
||||
window.__setPreferredTheme('dark');
|
||||
}}
|
||||
className="flex lg:hidden items-center p-1 h-full ml-4 lg:ml-6">
|
||||
{darkIcon}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
ref={feedbackPopupRef}
|
||||
className={cn(
|
||||
'fixed top-12 right-0',
|
||||
showFeedback ? 'block' : 'hidden'
|
||||
)}>
|
||||
<Feedback
|
||||
onSubmit={() => {
|
||||
clearTimeout(feedbackAutohideRef.current);
|
||||
feedbackAutohideRef.current = setTimeout(() => {
|
||||
setShowFeedback(false);
|
||||
}, 1000);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden dark:block">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Use Light Mode"
|
||||
onClick={() => {
|
||||
window.__setPreferredTheme('light');
|
||||
}}
|
||||
className="flex lg:hidden items-center p-1 h-full ml-4 lg:ml-6">
|
||||
{lightIcon}
|
||||
</button>
|
||||
<div className="flex w-full md:hidden"></div>
|
||||
<div className="flex items-center md:border-l border-gray-20 dark:border-gray-70 pl-4 ">
|
||||
<div className="block md:hidden">
|
||||
<Search />
|
||||
</div>
|
||||
<div className="block mr-4 lg:hidden">
|
||||
<button
|
||||
aria-label="Give feedback"
|
||||
type="button"
|
||||
className={cn(
|
||||
'inline-flex lg:hidden items-center rounded-full px-1.5 ml-4 lg:ml-6 relative top-px',
|
||||
{
|
||||
'bg-card dark:bg-card-dark': showFeedback,
|
||||
}
|
||||
)}
|
||||
onClick={handleFeedback}>
|
||||
{feedbackIcon}
|
||||
</button>
|
||||
<div
|
||||
ref={feedbackPopupRef}
|
||||
className={cn(
|
||||
'fixed top-12 right-0',
|
||||
showFeedback ? 'block' : 'hidden'
|
||||
)}>
|
||||
<Feedback
|
||||
onSubmit={() => {
|
||||
clearTimeout(feedbackAutohideRef.current);
|
||||
feedbackAutohideRef.current = setTimeout(() => {
|
||||
setShowFeedback(false);
|
||||
}, 1000);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="block mr-4 dark:hidden">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Use Dark Mode"
|
||||
onClick={() => {
|
||||
window.__setPreferredTheme('dark');
|
||||
}}
|
||||
className="flex items-center h-full pr-2">
|
||||
{darkIcon}
|
||||
</button>
|
||||
</div>
|
||||
<div className="hidden mr-4 dark:block">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Use Light Mode"
|
||||
onClick={() => {
|
||||
window.__setPreferredTheme('light');
|
||||
}}
|
||||
className="items-center h-full pr-2">
|
||||
{lightIcon}
|
||||
</button>
|
||||
</div>
|
||||
<div className="block">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Open on GitHub"
|
||||
onClick={() => {
|
||||
// todo
|
||||
}}
|
||||
className="items-center h-full pr-2">
|
||||
{githubIcon}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{isOpen && (
|
||||
<div className="bg-wash dark:bg-wash-dark px-5 flex justify-end border-b border-border dark:border-border-dark items-center self-center w-full z-10">
|
||||
<TabButton
|
||||
isActive={tab === 'learn' || tab === 'home'}
|
||||
onClick={() => selectTab('learn')}>
|
||||
Learn
|
||||
</TabButton>
|
||||
<TabButton
|
||||
isActive={tab === 'reference'}
|
||||
onClick={() => selectTab('reference')}>
|
||||
Reference
|
||||
</TabButton>
|
||||
<div
|
||||
ref={scrollParentRef}
|
||||
className="overflow-y-scroll no-bg-scrollbar lg:w-[336px] grow bg-wash dark:bg-wash-dark">
|
||||
<aside
|
||||
className={cn(
|
||||
`lg:grow lg:flex flex-col w-full pb-8 lg:pb-0 lg:max-w-xs z-10`,
|
||||
isOpen ? 'block z-40' : 'hidden lg:block'
|
||||
)}>
|
||||
<nav
|
||||
role="navigation"
|
||||
style={{'--bg-opacity': '.2'} as React.CSSProperties} // Need to cast here because CSS vars aren't considered valid in TS types (cuz they could be anything)
|
||||
className="w-full lg:h-auto grow pr-0 lg:pr-5 pt-6 lg:py-6 md:pt-4 lg:pt-4 scrolling-touch scrolling-gpu">
|
||||
{/* No fallback UI so need to be careful not to suspend directly inside. */}
|
||||
<Suspense fallback={null}>
|
||||
<div className="block md:hidden">
|
||||
<div className="block mr-4">
|
||||
<SidebarLink
|
||||
href="/learn"
|
||||
isPending={false}
|
||||
selected={false}
|
||||
level={0}
|
||||
title="Learn"
|
||||
wip={false}
|
||||
isExpanded={false}
|
||||
isBreadcrumb={false}
|
||||
hideArrow
|
||||
/>
|
||||
</div>
|
||||
<div className="block mr-4">
|
||||
<SidebarLink
|
||||
href="/reference/react"
|
||||
isPending={false}
|
||||
selected={false}
|
||||
level={0}
|
||||
title="Reference"
|
||||
wip={false}
|
||||
isExpanded={false}
|
||||
isBreadcrumb={false}
|
||||
hideArrow
|
||||
/>
|
||||
</div>
|
||||
<div className="block mr-4">
|
||||
<SidebarLink
|
||||
href="/community"
|
||||
isPending={false}
|
||||
selected={false}
|
||||
level={0}
|
||||
title="Community"
|
||||
wip={false}
|
||||
isExpanded={false}
|
||||
isBreadcrumb={false}
|
||||
hideArrow
|
||||
/>
|
||||
</div>
|
||||
<div className="block mr-4">
|
||||
<SidebarLink
|
||||
href="/blog"
|
||||
isPending={false}
|
||||
selected={false}
|
||||
level={0}
|
||||
title="Blog"
|
||||
wip={false}
|
||||
isExpanded={false}
|
||||
isBreadcrumb={false}
|
||||
hideArrow
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
role="separator"
|
||||
className="mt-4 mb-2 ml-5 border-b border-border dark:border-border-dark"
|
||||
/>
|
||||
</div>
|
||||
<SidebarRouteTree
|
||||
// Don't share state between the desktop and mobile versions.
|
||||
// This avoids unnecessary animations and visual flicker.
|
||||
key={isOpen ? 'mobile-overlay' : 'desktop-or-hidden'}
|
||||
routeTree={routeTree}
|
||||
breadcrumbs={breadcrumbs}
|
||||
isForceExpanded={isOpen}
|
||||
/>
|
||||
</Suspense>
|
||||
<div className="h-20" />
|
||||
</nav>
|
||||
<div className="fixed bottom-0 hidden lg:block">
|
||||
<Feedback />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
ref={scrollParentRef}
|
||||
className="overflow-y-scroll no-bg-scrollbar lg:w-[336px] grow bg-wash dark:bg-wash-dark">
|
||||
<aside
|
||||
className={cn(
|
||||
`lg:grow lg:flex flex-col w-full pb-8 lg:pb-0 lg:max-w-xs z-10`,
|
||||
isOpen ? 'block z-40' : 'hidden lg:block'
|
||||
)}>
|
||||
<nav
|
||||
role="navigation"
|
||||
style={{'--bg-opacity': '.2'} as React.CSSProperties} // Need to cast here because CSS vars aren't considered valid in TS types (cuz they could be anything)
|
||||
className="w-full lg:h-auto grow pr-0 lg:pr-5 pt-6 lg:py-6 md:pt-4 lg:pt-4 scrolling-touch scrolling-gpu">
|
||||
{/* No fallback UI so need to be careful not to suspend directly inside. */}
|
||||
<Suspense fallback={null}>
|
||||
<SidebarRouteTree
|
||||
// Don't share state between the desktop and mobile versions.
|
||||
// This avoids unnecessary animations and visual flicker.
|
||||
key={isOpen ? 'mobile-overlay' : 'desktop-or-hidden'}
|
||||
routeTree={routeTree}
|
||||
breadcrumbs={breadcrumbs}
|
||||
isForceExpanded={isOpen}
|
||||
/>
|
||||
</Suspense>
|
||||
<div className="h-20" />
|
||||
</nav>
|
||||
<div className="fixed bottom-0 hidden lg:block">
|
||||
<Feedback />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TabButton({
|
||||
children,
|
||||
onClick,
|
||||
isActive,
|
||||
}: {
|
||||
children: any;
|
||||
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
isActive: boolean;
|
||||
}) {
|
||||
const classes = cn(
|
||||
'inline-flex items-center w-full border-b-2 justify-center text-base leading-9 px-3 pb-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 (
|
||||
<button className={classes} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -2,4 +2,4 @@
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
export {default as Nav} from './Nav';
|
||||
export {default as TopNav} from './TopNav';
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
import {Logo} from 'components/Logo';
|
||||
import YouWillLearnCard from 'components/MDX/YouWillLearnCard';
|
||||
|
||||
function HomepageHero() {
|
||||
return (
|
||||
<>
|
||||
<div className="mt-8 lg:mt-10 mb-0 sm:mt-8 sm:mb-8 lg:mb-6 flex-col sm:flex-row flex grow items-start sm:items-center justify-start mx-auto max-w-4xl">
|
||||
<Logo className="text-link dark:text-link-dark w-20 sm:w-28 mr-4 mb-4 sm:mb-0 h-auto" />
|
||||
<div className="flex flex-wrap">
|
||||
<h1 className="text-5xl mr-4 -mt-1 flex wrap font-bold leading-tight text-primary dark:text-primary-dark">
|
||||
React Docs
|
||||
</h1>
|
||||
<div className="inline-flex self-center px-2 mt-1 bg-highlight dark:bg-highlight-dark w-auto rounded text-link dark:text-link-dark uppercase font-bold tracking-wide text-base whitespace-nowrap">
|
||||
Beta
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section className="my-8 sm:my-10 grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-4">
|
||||
<div className="flex flex-col justify-center">
|
||||
<YouWillLearnCard title="Quick Start" path="/learn">
|
||||
<p>
|
||||
Learn how to think in React with step-by-step explanations and
|
||||
interactive examples.
|
||||
</p>
|
||||
</YouWillLearnCard>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center">
|
||||
<YouWillLearnCard title="API Reference" path="/reference/react">
|
||||
<p>
|
||||
Look up the API of React Hooks, and see their shape with
|
||||
color-coded signatures.
|
||||
</p>
|
||||
</YouWillLearnCard>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default HomepageHero;
|
||||
@@ -12,7 +12,6 @@ import ConsoleBlock from './ConsoleBlock';
|
||||
import ExpandableCallout from './ExpandableCallout';
|
||||
import ExpandableExample from './ExpandableExample';
|
||||
import {H1, H2, H3, H4} from './Heading';
|
||||
import HomepageHero from './HomepageHero';
|
||||
import InlineCode from './InlineCode';
|
||||
import Intro from './Intro';
|
||||
import Link from './Link';
|
||||
@@ -398,7 +397,6 @@ export const MDXComponents = {
|
||||
Pitfall,
|
||||
Deprecated,
|
||||
Wip,
|
||||
HomepageHero,
|
||||
Illustration,
|
||||
IllustrationBlock,
|
||||
Intro,
|
||||
|
||||
@@ -23,7 +23,7 @@ function PageHeading({
|
||||
breadcrumbs,
|
||||
}: PageHeadingProps) {
|
||||
return (
|
||||
<div className="px-5 sm:px-12 pt-8 sm:pt-7 lg:pt-5">
|
||||
<div className="px-5 sm:px-12 pt-8 sm:pt-7 lg:pt-2">
|
||||
<div className="max-w-4xl ml-0 2xl:mx-auto">
|
||||
{breadcrumbs ? <Breadcrumbs breadcrumbs={breadcrumbs} /> : null}
|
||||
<H1 className="mt-0 text-primary dark:text-primary-dark -mx-.5 break-words">
|
||||
|
||||
@@ -11,6 +11,7 @@ import {useState, useCallback, useEffect} from 'react';
|
||||
import * as React from 'react';
|
||||
import {createPortal} from 'react-dom';
|
||||
import {siteConfig} from 'siteConfig';
|
||||
import cn from 'classnames';
|
||||
|
||||
export interface SearchProps {
|
||||
appId?: string;
|
||||
@@ -18,6 +19,7 @@ export interface SearchProps {
|
||||
indexName?: string;
|
||||
searchParameters?: any;
|
||||
renderModal?: boolean;
|
||||
fullsize?: boolean;
|
||||
}
|
||||
|
||||
function Hit({hit, children}: any) {
|
||||
@@ -99,6 +101,7 @@ export function Search({
|
||||
searchParameters = {
|
||||
hitsPerPage: 5,
|
||||
},
|
||||
fullsize,
|
||||
}: SearchProps) {
|
||||
const [isShowing, setIsShowing] = useState(false);
|
||||
|
||||
@@ -145,17 +148,22 @@ export function Search({
|
||||
/>
|
||||
</Head>
|
||||
|
||||
<button
|
||||
aria-label="Search"
|
||||
type="button"
|
||||
className="inline-flex md:hidden items-center text-lg p-1 ml-4 lg:ml-6"
|
||||
onClick={onOpen}>
|
||||
<IconSearch className="align-middle" />
|
||||
</button>
|
||||
{!fullsize && (
|
||||
<button
|
||||
aria-label="Search"
|
||||
type="button"
|
||||
className="inline-flex md:hidden items-center text-lg p-1 ml-4 lg:ml-6"
|
||||
onClick={onOpen}>
|
||||
<IconSearch className="align-middle" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="hidden md:flex relative pl-4 pr-1 py-1 h-10 bg-secondary-button dark:bg-gray-80 outline-none focus:ring focus:outline-none betterhover:hover:bg-opacity-80 pointer items-center shadow-inner text-left w-full text-gray-30 rounded-md align-middle text-sm"
|
||||
className={cn(
|
||||
' relative pl-4 pr-1 py-1 h-10 bg-secondary-button dark:bg-gray-80 outline-none focus:ring focus:outline-none betterhover:hover:bg-opacity-80 pointer items-center shadow-inner text-left w-full text-gray-30 rounded-md align-middle text-sm',
|
||||
fullsize ? 'flex' : 'hidden md:flex'
|
||||
)}
|
||||
onClick={onOpen}>
|
||||
<IconSearch className="mr-3 align-middle text-gray-30 shrink-0 group-betterhover:hover:text-gray-70" />
|
||||
Search
|
||||
|
||||
@@ -4,7 +4,6 @@ title: React Docs Beta
|
||||
permalink: index.html
|
||||
---
|
||||
|
||||
<HomepageHero />
|
||||
|
||||
## What is this site? {/*what-is-this-site*/}
|
||||
|
||||
@@ -29,4 +28,4 @@ Please use [this GitHub issue](https://github.com/reactjs/reactjs.org/issues/330
|
||||
|
||||
## Will this site replace the main site? {/*will-this-site-replace-the-main-site*/}
|
||||
|
||||
We aim to switch this site to be the main one once we reach content parity with the [existing React documentation.](https://reactjs.org/) The old React website will be archived at a subdomain so you'll still be able to access it. Old content links will redirect to the archived subdomain, which will have a notice about outdated content.
|
||||
We aim to switch this site to be the main one once we reach content parity with the [existing React documentation.](https://reactjs.org/) The old React website will be archived at a subdomain so you'll still be able to access it. Old content links will redirect to the archived subdomain, which will have a notice about outdated content.
|
||||
|
||||
Reference in New Issue
Block a user