From 15fce216c91b16805c546618917685b1d5c6261b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Parrino?= Date: Mon, 11 Oct 2021 09:17:16 -0300 Subject: [PATCH] added swizzled search feature to fix external search issue (#873) Co-authored-by: Dorian Kinoo Crutcher --- website/src/theme/SearchBar/index.js | 216 ++++++++++++++++++ website/src/theme/SearchBar/styles.css | 21 ++ website/src/theme/SearchBar/styles.module.css | 20 ++ 3 files changed, 257 insertions(+) create mode 100644 website/src/theme/SearchBar/index.js create mode 100644 website/src/theme/SearchBar/styles.css create mode 100644 website/src/theme/SearchBar/styles.module.css diff --git a/website/src/theme/SearchBar/index.js b/website/src/theme/SearchBar/index.js new file mode 100644 index 00000000000..896bcc87a2c --- /dev/null +++ b/website/src/theme/SearchBar/index.js @@ -0,0 +1,216 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useState, useRef, useCallback, useMemo } from "react"; +import { createPortal } from "react-dom"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import { useHistory } from "@docusaurus/router"; +import { useBaseUrlUtils } from "@docusaurus/useBaseUrl"; +import Link from "@docusaurus/Link"; +import Head from "@docusaurus/Head"; +import useSearchQuery from "@theme/hooks/useSearchQuery"; +import { DocSearchButton, useDocSearchKeyboardEvents } from "@docsearch/react"; +import useAlgoliaContextualFacetFilters from "@theme/hooks/useAlgoliaContextualFacetFilters"; +import { translate } from "@docusaurus/Translate"; +import styles from "./styles.module.css"; + +let DocSearchModal = null; + +function Hit({ hit, children }) { + return {children}; +} + +function ResultsFooter({ state, onClose }) { + const { generateSearchPageLink } = useSearchQuery(); + + return ( + + See all {state.context.nbHits} results + + ); +} + +function DocSearch({ contextualSearch, ...props }) { + const { siteMetadata } = useDocusaurusContext(); + + const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters(); + + const configFacetFilters = props.searchParameters?.facetFilters ?? []; + + const facetFilters = contextualSearch + ? // Merge contextual search filters with config filters + [...contextualSearchFacetFilters, ...configFacetFilters] + : // ... or use config facetFilters + configFacetFilters; + + // we let user override default searchParameters if he wants to + const searchParameters = { + ...props.searchParameters, + facetFilters, + }; + + const { withBaseUrl } = useBaseUrlUtils(); + const history = useHistory(); + const searchContainer = useRef(null); + const searchButtonRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + const [initialQuery, setInitialQuery] = useState(null); + + const importDocSearchModalIfNeeded = useCallback(() => { + 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 }) { + // If is external link, we navigate with window + if (httpRegex.test(itemUrl)) { + window.location.href = itemUrl; + } else { + history.push(itemUrl); + } + }, + }).current; + + const transformItems = useRef((items) => { + return items.map((item) => { + if ( + item.url.includes( + "Entrypoint domain, the website where Docusaurus 2 lives and people searches" + ) + ) { + const a = document.createElement("a"); + a.href = item.url; + return { + ...item, + url: withBaseUrl(`${a.pathname}${a.hash}`), + }; + } else { + return item; + } + }); + }).current; + + const resultsFooterComponent = useMemo( + () => (footerProps) => , + [onClose] + ); + + const transformSearchClient = useCallback( + (searchClient) => { + searchClient.addAlgoliaAgent( + "docusaurus", + siteMetadata.docusaurusVersion + ); + + return searchClient; + }, + [siteMetadata.docusaurusVersion] + ); + + useDocSearchKeyboardEvents({ + isOpen, + onOpen, + onClose, + onInput, + searchButtonRef, + }); + + const translatedSearchLabel = translate({ + id: "theme.SearchBar.label", + message: "Search", + description: "The ARIA label and placeholder for search button", + }); + + 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 && + createPortal( + , + searchContainer.current + )} + + ); +} + +function SearchBar() { + const { siteConfig } = useDocusaurusContext(); + return ; +} + +export default SearchBar; diff --git a/website/src/theme/SearchBar/styles.css b/website/src/theme/SearchBar/styles.css new file mode 100644 index 00000000000..c80dd71d37d --- /dev/null +++ b/website/src/theme/SearchBar/styles.css @@ -0,0 +1,21 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +:root { + --docsearch-primary-color: var(--ifm-color-primary); + --docsearch-text-color: var(--ifm-font-color-base); +} + +.DocSearch-Button { + margin: 0; + transition: all var(--ifm-transition-fast) + var(--ifm-transition-timing-default); +} + +.DocSearch-Container { + z-index: calc(var(--ifm-z-index-fixed) + 1); +} diff --git a/website/src/theme/SearchBar/styles.module.css b/website/src/theme/SearchBar/styles.module.css new file mode 100644 index 00000000000..ca386bb12db --- /dev/null +++ b/website/src/theme/SearchBar/styles.module.css @@ -0,0 +1,20 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +@media (max-width: 996px) { + .searchBox { + position: absolute; + right: var(--ifm-navbar-padding-horizontal); + } +} + +@media (min-width: 997px) { + .searchBox { + padding: var(--ifm-navbar-item-padding-vertical) + var(--ifm-navbar-item-padding-horizontal); + } +}