Files
react.dev/src/components/Search.tsx
dan 8de8a0342d Fix search modal not disappearing on Enter (#5794)
* Remove dead prop

* Use React.lazy

* Render at most one dialog at a time
2023-03-22 01:56:34 +00:00

142 lines
3.3 KiB
TypeScript

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import Head from 'next/head';
import Link from 'next/link';
import Router from 'next/router';
import {lazy, 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;
apiKey?: string;
indexName?: string;
searchParameters?: any;
isOpen: boolean;
onOpen: () => void;
onClose: () => void;
}
function Hit({hit, children}: any) {
return (
<Link href={hit.url.replace()}>
<a>{children}</a>
</Link>
);
}
// Copy-pasted from @docsearch/react to avoid importing the whole bundle.
// Slightly trimmed to features we use.
// (c) Algolia, Inc.
function isEditingContent(event: any) {
var element = event.target;
var tagName = element.tagName;
return (
element.isContentEditable ||
tagName === 'INPUT' ||
tagName === 'SELECT' ||
tagName === 'TEXTAREA'
);
}
function useDocSearchKeyboardEvents({
isOpen,
onOpen,
onClose,
}: {
isOpen: boolean;
onOpen: () => void;
onClose: () => void;
}) {
useEffect(() => {
function onKeyDown(event: any) {
function open() {
// We check that no other DocSearch modal is showing before opening
// another one.
if (!document.body.classList.contains('DocSearch--active')) {
onOpen();
}
}
if (
(event.keyCode === 27 && isOpen) ||
(event.key === 'k' && (event.metaKey || event.ctrlKey)) ||
(!isEditingContent(event) && event.key === '/' && !isOpen)
) {
event.preventDefault();
if (isOpen) {
onClose();
} else if (!document.body.classList.contains('DocSearch--active')) {
open();
}
}
}
window.addEventListener('keydown', onKeyDown);
return function () {
window.removeEventListener('keydown', onKeyDown);
};
}, [isOpen, onOpen, onClose]);
}
const options = {
appId: siteConfig.algolia.appId,
apiKey: siteConfig.algolia.apiKey,
indexName: siteConfig.algolia.indexName,
};
const DocSearchModal: any = lazy(() =>
// @ts-ignore
import('@docsearch/react/modal').then((mod) => ({
default: mod.DocSearchModal,
}))
);
export function Search({
isOpen,
onOpen,
onClose,
searchParameters = {
hitsPerPage: 5,
},
}: SearchProps) {
useDocSearchKeyboardEvents({isOpen, onOpen, onClose});
return (
<>
<Head>
<link
rel="preconnect"
href={`https://${options.appId}-dsn.algolia.net`}
/>
</Head>
{isOpen &&
createPortal(
<DocSearchModal
{...options}
initialScrollY={window.scrollY}
searchParameters={searchParameters}
onClose={onClose}
navigator={{
navigate({itemUrl}: any) {
Router.push(itemUrl);
},
}}
transformItems={(items: any[]) => {
return items.map((item) => {
const url = new URL(item.url);
return {
...item,
url: item.url.replace(url.origin, '').replace('#__next', ''),
};
});
}}
hitComponent={Hit}
/>,
document.body
)}
</>
);
}