|
@ -2,12 +2,10 @@ |
|
|
* Copyright (c) Facebook, Inc. and its affiliates. |
|
|
* Copyright (c) Facebook, Inc. and its affiliates. |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
import {IconSearch} from 'components/Icon/IconSearch'; |
|
|
|
|
|
import Head from 'next/head'; |
|
|
import Head from 'next/head'; |
|
|
import Link from 'next/link'; |
|
|
import Link from 'next/link'; |
|
|
import Router from 'next/router'; |
|
|
import Router from 'next/router'; |
|
|
import {useState, useCallback, useEffect} from 'react'; |
|
|
import {lazy, useCallback, useEffect} from 'react'; |
|
|
import * as React from 'react'; |
|
|
import * as React from 'react'; |
|
|
import {createPortal} from 'react-dom'; |
|
|
import {createPortal} from 'react-dom'; |
|
|
import {siteConfig} from 'siteConfig'; |
|
|
import {siteConfig} from 'siteConfig'; |
|
@ -18,8 +16,9 @@ export interface SearchProps { |
|
|
apiKey?: string; |
|
|
apiKey?: string; |
|
|
indexName?: string; |
|
|
indexName?: string; |
|
|
searchParameters?: any; |
|
|
searchParameters?: any; |
|
|
renderModal?: boolean; |
|
|
isOpen: boolean; |
|
|
fullsize?: boolean; |
|
|
onOpen: () => void; |
|
|
|
|
|
onClose: () => void; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function Hit({hit, children}: any) { |
|
|
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.
|
|
|
// Copy-pasted from @docsearch/react to avoid importing the whole bundle.
|
|
|
// Slightly trimmed to features we use.
|
|
|
// Slightly trimmed to features we use.
|
|
|
// (c) Algolia, Inc.
|
|
|
// (c) Algolia, Inc.
|
|
@ -99,49 +86,23 @@ const options = { |
|
|
apiKey: siteConfig.algolia.apiKey, |
|
|
apiKey: siteConfig.algolia.apiKey, |
|
|
indexName: siteConfig.algolia.indexName, |
|
|
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({ |
|
|
export function Search({ |
|
|
|
|
|
isOpen, |
|
|
|
|
|
onOpen, |
|
|
|
|
|
onClose, |
|
|
searchParameters = { |
|
|
searchParameters = { |
|
|
hitsPerPage: 5, |
|
|
hitsPerPage: 5, |
|
|
}, |
|
|
}, |
|
|
fullsize, |
|
|
|
|
|
}: SearchProps) { |
|
|
}: SearchProps) { |
|
|
const [isShowing, setIsShowing] = useState(false); |
|
|
useDocSearchKeyboardEvents({isOpen, onOpen, onClose}); |
|
|
|
|
|
|
|
|
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}); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<> |
|
|
<> |
|
|
<Head> |
|
|
<Head> |
|
@ -150,36 +111,7 @@ export function Search({ |
|
|
href={`https://${options.appId}-dsn.algolia.net`} |
|
|
href={`https://${options.appId}-dsn.algolia.net`} |
|
|
/> |
|
|
/> |
|
|
</Head> |
|
|
</Head> |
|
|
|
|
|
{isOpen && |
|
|
{!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 && |
|
|
|
|
|
createPortal( |
|
|
createPortal( |
|
|
<DocSearchModal |
|
|
<DocSearchModal |
|
|
{...options} |
|
|
{...options} |
|
|