import { useDocSearchKeyboardEvents } from "@docsearch/react"; import Head from "@docusaurus/Head"; import Link from "@docusaurus/Link"; import { useHistory } from "@docusaurus/router"; import { isRegexpStringMatch, useSearchLinkCreator, } from "@docusaurus/theme-common"; import { useAlgoliaContextualFacetFilters, useSearchResultUrlProcessor, } from "@docusaurus/theme-search-algolia/client"; import Translate from "@docusaurus/Translate"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import { DocSearchButton } from "@site/src/refine-theme/doc-search-button"; import translations from "@theme/SearchTranslations"; import React, { useCallback, useMemo, useRef, useState } from "react"; import { createPortal } from "react-dom"; let DocSearchModal = null; function Hit({ hit, children }) { return {children}; } function ResultsFooter({ state, onClose }) { const createSearchLink = useSearchLinkCreator(); return ( {"See all {count} results"} ); } function mergeFacetFilters(f1, f2) { const normalize = (f) => (typeof f === "string" ? [f] : f); return [...normalize(f1), ...normalize(f2)]; } function DocSearch({ contextualSearch, externalUrlRegex, CustomButton, ...props }: any) { const { siteMetadata } = useDocusaurusContext(); const processSearchResultUrl = useSearchResultUrlProcessor(); const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters(); const configFacetFilters = props.searchParameters?.facetFilters ?? []; const facetFilters = contextualSearch ? // Merge contextual search filters with config filters mergeFacetFilters(contextualSearchFacetFilters, configFacetFilters) : // ... or use config facetFilters configFacetFilters; // We let user override default searchParameters if she wants to const searchParameters = { ...props.searchParameters, facetFilters, }; const history = useHistory(); const searchContainer = useRef(null); const searchButtonRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const [initialQuery, setInitialQuery] = useState(undefined); const importDocSearchModalIfNeeded = useCallback(async () => { if (DocSearchModal) { return Promise.resolve(); } return Promise.all([ import("@docsearch/react/modal"), import("@docsearch/react/style"), import("./styles.css"), ]).then(([{ DocSearchModal: Modal }]) => { DocSearchModal = Modal; }); }, []); const onOpen = useCallback(() => { importDocSearchModalIfNeeded().then(() => { searchContainer.current = document.createElement("div"); document.body.insertBefore( searchContainer.current, document.body.firstChild, ); setIsOpen(true); }); }, [importDocSearchModalIfNeeded, setIsOpen]); const onClose = useCallback(() => { setIsOpen(false); searchContainer.current?.remove(); }, [setIsOpen]); const onInput = useCallback( (event) => { importDocSearchModalIfNeeded().then(() => { setIsOpen(true); setInitialQuery(event.key); }); }, [importDocSearchModalIfNeeded, setIsOpen, setInitialQuery], ); const navigator = useRef({ navigate({ itemUrl }) { // Algolia results could contain URL's from other domains which cannot // be served through history and should navigate with window.location if (isRegexpStringMatch(externalUrlRegex, itemUrl)) { window.location.href = itemUrl; } else { history.push(itemUrl); } }, }).current; const transformItems = useRef((items) => props.transformItems ? // Custom transformItems props.transformItems(items) : // Default transformItems items.map((item) => ({ ...item, url: processSearchResultUrl(item.url), })), ).current; const resultsFooterComponent = useMemo( () => function ResultsFooterWrapper(footerProps) { return ; }, [onClose], ); const transformSearchClient = useCallback( (searchClient) => { searchClient.addAlgoliaAgent( "docusaurus", siteMetadata.docusaurusVersion, ); return searchClient; }, [siteMetadata.docusaurusVersion], ); useDocSearchKeyboardEvents({ isOpen, onOpen, onClose, onInput, searchButtonRef, }); const SearchButton = CustomButton ?? DocSearchButton; const { apiKey: _apiKey, appId: _appId, searchPagePath: _searchPagePath, indexName: _indexName, searchParameters: _searchParameters, ...buttonProps } = props; return ( <> {/* This hints the browser that the website will load data from Algolia, and allows it to preconnect to the DocSearch cluster. It makes the first query faster, especially on mobile. */} {isOpen && DocSearchModal && searchContainer.current && createPortal( , searchContainer.current, )} ); } export default function SearchBar({ CustomButton }) { const { siteConfig } = useDocusaurusContext(); return ( )} CustomButton={CustomButton} /> ); }