diff --git a/docusaurus.config.js b/docusaurus.config.js index 5f52f3d2..d523f3e9 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -163,7 +163,10 @@ const config = { label: '市场', position: 'left', }, - + { + type: 'search', + position: 'right', + }, { type: 'localeDropdown', position: 'right', @@ -180,6 +183,33 @@ const config = { darkTheme: darkCodeTheme, additionalLanguages: ['shell-session', 'http', 'protobuf', 'rust'], }, + algolia: { + // The application ID provided by Algolia + appId: 'E5GVH515OK', + + // Public API key: it is safe to commit it + apiKey: '06b2d0933abc228a5979d16d1af26c0b', + + // 默认 index 是简体中文,其它语言需要动态切 indexName,逻辑在 SearchBar.tsx + indexName: 'open-longportapp', + + // Optional: see doc section below + contextualSearch: true, + + // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs + // replaceSearchResultPathname: { + // from: '/docs/', // or as RegExp: /\/docs\// + // to: '/', + // }, + + // Optional: Algolia search parameters + searchParameters: {}, + + // Optional: path for search page that enabled by default (`false` to disable it) + searchPagePath: 'search', + + //... other Algolia params + }, }), } diff --git a/src/components/dropdown/index.module.scss b/src/components/dropdown/index.module.scss index 446fb524..011f3af0 100644 --- a/src/components/dropdown/index.module.scss +++ b/src/components/dropdown/index.module.scss @@ -19,7 +19,7 @@ $border-color: #eeeeee; --dropdown-hover-bg: #{$hover-bg}; --dropdown-border-color: #{$border-color}; --dropdown-brand-color: #{$brand-color}; - @apply relative text-sm; + @apply relative text-sm mr-[50px] md:mr-auto; :global(.trigger) { @apply cursor-pointer py-1 px-2 rounded-full text-base font-medium; &:hover { diff --git a/src/theme/SearchBar/index.js b/src/theme/SearchBar/index.js new file mode 100644 index 00000000..7bad58e7 --- /dev/null +++ b/src/theme/SearchBar/index.js @@ -0,0 +1,192 @@ +import React, { useCallback, useMemo, useRef, useState } from 'react' +import { DocSearchButton, 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 { createPortal } from 'react-dom' +import translations from '@theme/SearchTranslations' + +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, ...props }) { + const { siteMetadata, i18n } = 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(() => { + 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( + () => + // eslint-disable-next-line react/no-unstable-nested-components + (footerProps) => , + [onClose] + ) + const transformSearchClient = useCallback( + (searchClient) => { + searchClient.addAlgoliaAgent('docusaurus', siteMetadata.docusaurusVersion) + return searchClient + }, + [siteMetadata.docusaurusVersion] + ) + useDocSearchKeyboardEvents({ + isOpen, + onOpen, + onClose, + onInput, + searchButtonRef, + }) + + const indexName = useMemo(() => { + switch (i18n.currentLocale) { + case 'en': + return 'open-longportapp-en' + break + case 'zh-HK': + return 'open-longportapp-zh-HK' + break + default: + return 'open-longportapp' + } + }, [i18n.currentLocale]) + + console.log('DocSearch-142', i18n.currentLocale, indexName) + + 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() { + const { siteConfig } = useDocusaurusContext() + return +} diff --git a/src/theme/SearchBar/styles.css b/src/theme/SearchBar/styles.css new file mode 100644 index 00000000..c80dd71d --- /dev/null +++ b/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); +}