mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-23 04:12:34 +00:00
Fix search modal not disappearing on Enter (#5794)
* Remove dead prop * Use React.lazy * Render at most one dialog at a time
This commit is contained in:
@@ -2,7 +2,14 @@
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
import {useState, useRef, useEffect, Suspense} from 'react';
|
||||
import {
|
||||
useState,
|
||||
useRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
startTransition,
|
||||
Suspense,
|
||||
} from 'react';
|
||||
import * as React from 'react';
|
||||
import cn from 'classnames';
|
||||
import NextLink from 'next/link';
|
||||
@@ -11,6 +18,7 @@ import {disableBodyScroll, enableBodyScroll} from 'body-scroll-lock';
|
||||
|
||||
import {IconClose} from 'components/Icon/IconClose';
|
||||
import {IconHamburger} from 'components/Icon/IconHamburger';
|
||||
import {IconSearch} from 'components/Icon/IconSearch';
|
||||
import {Search} from 'components/Search';
|
||||
import {Logo} from '../../Logo';
|
||||
import {Feedback} from '../Feedback';
|
||||
@@ -112,6 +120,18 @@ function NavItem({url, isActive, children}: any) {
|
||||
);
|
||||
}
|
||||
|
||||
function Kbd(props: {children?: React.ReactNode; wide?: boolean}) {
|
||||
const {wide, ...rest} = props;
|
||||
const width = wide ? 'w-10' : 'w-5';
|
||||
|
||||
return (
|
||||
<kbd
|
||||
className={`${width} h-5 border border-transparent mr-1 bg-wash dark:bg-wash-dark text-gray-30 align-middle p-0 inline-flex justify-center items-center text-xs text-center rounded-md`}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TopNav({
|
||||
routeTree,
|
||||
breadcrumbs,
|
||||
@@ -183,8 +203,23 @@ export default function TopNav({
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
const [showSearch, setShowSearch] = useState(false);
|
||||
const onOpenSearch = useCallback(() => {
|
||||
startTransition(() => {
|
||||
setShowSearch(true);
|
||||
});
|
||||
}, []);
|
||||
const onCloseSearch = useCallback(() => {
|
||||
setShowSearch(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Search
|
||||
isOpen={showSearch}
|
||||
onOpen={onOpenSearch}
|
||||
onClose={onCloseSearch}
|
||||
/>
|
||||
<div ref={scrollDetectorRef} />
|
||||
<div
|
||||
className={cn(
|
||||
@@ -226,7 +261,22 @@ export default function TopNav({
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden md:flex flex-1 justify-center items-center w-full 3xl:w-auto 3xl:shrink-0 3xl:justify-center">
|
||||
<Search />
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex 3xl:w-[56rem] 3xl:mx-0 relative pl-4 pr-1 py-1 h-10 bg-gray-30/20 dark:bg-gray-40/20 outline-none focus:outline-link betterhover:hover:bg-opacity-80 pointer items-center text-left w-full text-gray-30 rounded-full align-middle text-base'
|
||||
)}
|
||||
onClick={onOpenSearch}>
|
||||
<IconSearch className="mr-3 align-middle text-gray-30 shrink-0 group-betterhover:hover:text-gray-70" />
|
||||
Search
|
||||
<span className="ml-auto hidden sm:flex item-center mr-1">
|
||||
<Kbd data-platform="mac">⌘</Kbd>
|
||||
<Kbd data-platform="win" wide>
|
||||
Ctrl
|
||||
</Kbd>
|
||||
<Kbd>K</Kbd>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-base justify-center items-center gap-1.5 flex 3xl:flex-1 flex-row 3xl:justify-end">
|
||||
<div className="mx-2.5 gap-1.5 hidden lg:flex">
|
||||
@@ -248,7 +298,13 @@ export default function TopNav({
|
||||
<div className="flex w-full md:hidden"></div>
|
||||
<div className="flex items-center -space-x-2.5 xs:space-x-0 ">
|
||||
<div className="flex md:hidden">
|
||||
<Search />
|
||||
<button
|
||||
aria-label="Search"
|
||||
type="button"
|
||||
className="active:scale-95 transition-transform flex md:hidden w-12 h-12 rounded-full items-center justify-center hover:bg-secondary-button hover:dark:bg-secondary-button-dark outline-link"
|
||||
onClick={onOpenSearch}>
|
||||
<IconSearch className="align-middle w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex dark:hidden">
|
||||
<button
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import {IconSearch} from 'components/Icon/IconSearch';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import Router from 'next/router';
|
||||
import {useState, useCallback, useEffect} from 'react';
|
||||
import {lazy, useCallback, useEffect} from 'react';
|
||||
import * as React from 'react';
|
||||
import {createPortal} from 'react-dom';
|
||||
import {siteConfig} from 'siteConfig';
|
||||
@@ -18,8 +16,9 @@ export interface SearchProps {
|
||||
apiKey?: string;
|
||||
indexName?: string;
|
||||
searchParameters?: any;
|
||||
renderModal?: boolean;
|
||||
fullsize?: boolean;
|
||||
isOpen: boolean;
|
||||
onOpen: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function Hit({hit, children}: any) {
|
||||
@@ -30,18 +29,6 @@ function Hit({hit, children}: any) {
|
||||
);
|
||||
}
|
||||
|
||||
function Kbd(props: {children?: React.ReactNode; wide?: boolean}) {
|
||||
const {wide, ...rest} = props;
|
||||
const width = wide ? 'w-10' : 'w-5';
|
||||
|
||||
return (
|
||||
<kbd
|
||||
className={`${width} h-5 border border-transparent mr-1 bg-wash dark:bg-wash-dark text-gray-30 align-middle p-0 inline-flex justify-center items-center text-xs text-center rounded-md`}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Copy-pasted from @docsearch/react to avoid importing the whole bundle.
|
||||
// Slightly trimmed to features we use.
|
||||
// (c) Algolia, Inc.
|
||||
@@ -99,49 +86,23 @@ const options = {
|
||||
apiKey: siteConfig.algolia.apiKey,
|
||||
indexName: siteConfig.algolia.indexName,
|
||||
};
|
||||
let DocSearchModal: any = null;
|
||||
|
||||
const DocSearchModal: any = lazy(() =>
|
||||
// @ts-ignore
|
||||
import('@docsearch/react/modal').then((mod) => ({
|
||||
default: mod.DocSearchModal,
|
||||
}))
|
||||
);
|
||||
|
||||
export function Search({
|
||||
isOpen,
|
||||
onOpen,
|
||||
onClose,
|
||||
searchParameters = {
|
||||
hitsPerPage: 5,
|
||||
},
|
||||
fullsize,
|
||||
}: SearchProps) {
|
||||
const [isShowing, setIsShowing] = useState(false);
|
||||
|
||||
const importDocSearchModalIfNeeded = useCallback(
|
||||
function importDocSearchModalIfNeeded() {
|
||||
if (DocSearchModal) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return import('@docsearch/react/modal').then(
|
||||
({DocSearchModal: Modal}) => {
|
||||
DocSearchModal = Modal;
|
||||
}
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const onOpen = useCallback(
|
||||
function onOpen() {
|
||||
importDocSearchModalIfNeeded().then(() => {
|
||||
setIsShowing(true);
|
||||
});
|
||||
},
|
||||
[importDocSearchModalIfNeeded, setIsShowing]
|
||||
);
|
||||
|
||||
const onClose = useCallback(
|
||||
function onClose() {
|
||||
setIsShowing(false);
|
||||
},
|
||||
[setIsShowing]
|
||||
);
|
||||
|
||||
useDocSearchKeyboardEvents({isOpen: isShowing, onOpen, onClose});
|
||||
|
||||
useDocSearchKeyboardEvents({isOpen, onOpen, onClose});
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -150,36 +111,7 @@ export function Search({
|
||||
href={`https://${options.appId}-dsn.algolia.net`}
|
||||
/>
|
||||
</Head>
|
||||
|
||||
{!fullsize && (
|
||||
<button
|
||||
aria-label="Search"
|
||||
type="button"
|
||||
className="active:scale-95 transition-transform flex md:hidden w-12 h-12 rounded-full items-center justify-center hover:bg-secondary-button hover:dark:bg-secondary-button-dark outline-link"
|
||||
onClick={onOpen}>
|
||||
<IconSearch className="align-middle w-5 h-5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'3xl:w-[56rem] 3xl:mx-0 relative pl-4 pr-1 py-1 h-10 bg-gray-30/20 dark:bg-gray-40/20 outline-none focus:outline-link betterhover:hover:bg-opacity-80 pointer items-center text-left w-full text-gray-30 rounded-full align-middle text-base',
|
||||
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
|
||||
<span className="ml-auto hidden sm:flex item-center mr-1">
|
||||
<Kbd data-platform="mac">⌘</Kbd>
|
||||
<Kbd data-platform="win" wide>
|
||||
Ctrl
|
||||
</Kbd>
|
||||
<Kbd>K</Kbd>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{isShowing &&
|
||||
{isOpen &&
|
||||
createPortal(
|
||||
<DocSearchModal
|
||||
{...options}
|
||||
|
||||
Reference in New Issue
Block a user