mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
docs: use counter for search bar query (#9429)
Part 2 of https://github.com/puppeteer/puppeteer/pull/9428
This commit is contained in:
parent
3235b60dda
commit
65aedcdfc4
204
website/src/theme/SearchBar/index.js
Normal file
204
website/src/theme/SearchBar/index.js
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
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 {isRegexpStringMatch} from '@docusaurus/theme-common';
|
||||||
|
import {useSearchPage} from '@docusaurus/theme-common/internal';
|
||||||
|
import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
||||||
|
import Translate from '@docusaurus/Translate';
|
||||||
|
import translations from '@theme/SearchTranslations';
|
||||||
|
import {useContextualSearchFilters} from '@docusaurus/theme-common';
|
||||||
|
// eslint-disable-next-line import/extensions
|
||||||
|
import {tagToCounter} from '../SearchMetadata';
|
||||||
|
let DocSearchModal = null;
|
||||||
|
function Hit({hit, children}) {
|
||||||
|
return <Link to={hit.url}>{children}</Link>;
|
||||||
|
}
|
||||||
|
function ResultsFooter({state, onClose}) {
|
||||||
|
const {generateSearchPageLink} = useSearchPage();
|
||||||
|
return (
|
||||||
|
<Link to={generateSearchPageLink(state.query)} onClick={onClose}>
|
||||||
|
<Translate
|
||||||
|
id="theme.SearchBar.seeAll"
|
||||||
|
values={{count: state.context.nbHits}}
|
||||||
|
>
|
||||||
|
{'See all {count} results'}
|
||||||
|
</Translate>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function mergeFacetFilters(f1, f2) {
|
||||||
|
const normalize = f => {
|
||||||
|
return typeof f === 'string' ? [f] : f;
|
||||||
|
};
|
||||||
|
return [...normalize(f1), ...normalize(f2)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function usePuppeteerSearchFilters() {
|
||||||
|
const {locale, tags} = useContextualSearchFilters();
|
||||||
|
const languageFilter = `language:${locale}`;
|
||||||
|
return [
|
||||||
|
languageFilter,
|
||||||
|
tags.map(tag => {
|
||||||
|
return `counter:${tagToCounter.get(tag)}`;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
||||||
|
const {siteMetadata} = useDocusaurusContext();
|
||||||
|
const contextualSearchFacetFilters = usePuppeteerSearchFilters();
|
||||||
|
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 {withBaseUrl} = useBaseUrlUtils();
|
||||||
|
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 => {
|
||||||
|
return items.map(item => {
|
||||||
|
// If Algolia contains a external domain, we should navigate without
|
||||||
|
// relative URL
|
||||||
|
if (isRegexpStringMatch(externalUrlRegex, item.url)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
// We transform the absolute URL into a relative URL.
|
||||||
|
const url = new URL(item.url);
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
url: withBaseUrl(`${url.pathname}${url.hash}`),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}).current;
|
||||||
|
const resultsFooterComponent = useMemo(() => {
|
||||||
|
return footerProps => {
|
||||||
|
return <ResultsFooter {...footerProps} onClose={onClose} />;
|
||||||
|
};
|
||||||
|
}, [onClose]);
|
||||||
|
const transformSearchClient = useCallback(
|
||||||
|
searchClient => {
|
||||||
|
searchClient.addAlgoliaAgent(
|
||||||
|
'docusaurus',
|
||||||
|
siteMetadata.docusaurusVersion
|
||||||
|
);
|
||||||
|
return searchClient;
|
||||||
|
},
|
||||||
|
[siteMetadata.docusaurusVersion]
|
||||||
|
);
|
||||||
|
useDocSearchKeyboardEvents({
|
||||||
|
isOpen,
|
||||||
|
onOpen,
|
||||||
|
onClose,
|
||||||
|
onInput,
|
||||||
|
searchButtonRef,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
{/* 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. */}
|
||||||
|
<link
|
||||||
|
rel="preconnect"
|
||||||
|
href={`https://${props.appId}-dsn.algolia.net`}
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
|
||||||
|
<DocSearchButton
|
||||||
|
onTouchStart={importDocSearchModalIfNeeded}
|
||||||
|
onFocus={importDocSearchModalIfNeeded}
|
||||||
|
onMouseOver={importDocSearchModalIfNeeded}
|
||||||
|
onClick={onOpen}
|
||||||
|
ref={searchButtonRef}
|
||||||
|
translations={translations.button}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isOpen &&
|
||||||
|
DocSearchModal &&
|
||||||
|
searchContainer.current &&
|
||||||
|
createPortal(
|
||||||
|
<DocSearchModal
|
||||||
|
onClose={onClose}
|
||||||
|
initialScrollY={window.scrollY}
|
||||||
|
initialQuery={initialQuery}
|
||||||
|
navigator={navigator}
|
||||||
|
transformItems={transformItems}
|
||||||
|
hitComponent={Hit}
|
||||||
|
transformSearchClient={transformSearchClient}
|
||||||
|
{...(props.searchPagePath && {
|
||||||
|
resultsFooterComponent,
|
||||||
|
})}
|
||||||
|
{...props}
|
||||||
|
searchParameters={searchParameters}
|
||||||
|
placeholder={translations.placeholder}
|
||||||
|
translations={translations.modal}
|
||||||
|
/>,
|
||||||
|
searchContainer.current
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default function SearchBar() {
|
||||||
|
const {siteConfig} = useDocusaurusContext();
|
||||||
|
return <DocSearch {...siteConfig.themeConfig.algolia} />;
|
||||||
|
}
|
14
website/src/theme/SearchBar/styles.css
Normal file
14
website/src/theme/SearchBar/styles.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
: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);
|
||||||
|
}
|
@ -4,18 +4,29 @@ import Head from '@docusaurus/Head';
|
|||||||
// Tracks the global package version as a local, monotonic counter. This
|
// Tracks the global package version as a local, monotonic counter. This
|
||||||
// prevents Algolia from deleting based on differing package versions and
|
// prevents Algolia from deleting based on differing package versions and
|
||||||
// instead delete based on the number of versions we intend to keep documented.
|
// instead delete based on the number of versions we intend to keep documented.
|
||||||
let globalCounter = -1;
|
|
||||||
const versionToCounter = new Map();
|
|
||||||
|
|
||||||
export default function SearchMetadata({locale, version}) {
|
class MonotonicCountMap {
|
||||||
|
#counter = -1;
|
||||||
|
#map = new Map();
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
console.log(key);
|
||||||
|
let counter = this.#map.get(key);
|
||||||
|
if (!counter) {
|
||||||
|
counter = ++this.#counter;
|
||||||
|
this.#map.set(key, counter);
|
||||||
|
}
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tagToCounter = new MonotonicCountMap();
|
||||||
|
|
||||||
|
export default function SearchMetadata({locale, tag}) {
|
||||||
const language = locale;
|
const language = locale;
|
||||||
let counter;
|
let counter;
|
||||||
if (version) {
|
if (tag) {
|
||||||
counter = versionToCounter.get(version);
|
counter = tagToCounter.get(tag);
|
||||||
if (!counter) {
|
|
||||||
counter = ++globalCounter;
|
|
||||||
versionToCounter.set(version, counter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Head>
|
<Head>
|
||||||
|
@ -20,6 +20,8 @@ import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
|
|||||||
import Translate, {translate} from '@docusaurus/Translate';
|
import Translate, {translate} from '@docusaurus/Translate';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
// eslint-disable-next-line import/extensions
|
||||||
|
import {tagToCounter} from '../SearchMetadata';
|
||||||
// Very simple pluralization: probably good enough for now
|
// Very simple pluralization: probably good enough for now
|
||||||
function useDocumentsFoundPlural() {
|
function useDocumentsFoundPlural() {
|
||||||
const {selectMessage} = usePluralForm();
|
const {selectMessage} = usePluralForm();
|
||||||
@ -263,11 +265,17 @@ function SearchPageContent() {
|
|||||||
const makeSearch = useEvent((page = 0) => {
|
const makeSearch = useEvent((page = 0) => {
|
||||||
algoliaHelper.addDisjunctiveFacetRefinement(
|
algoliaHelper.addDisjunctiveFacetRefinement(
|
||||||
'counter',
|
'counter',
|
||||||
document
|
tagToCounter.get('default')
|
||||||
.querySelector('meta[name="docsearch:counter"]')
|
|
||||||
.getAttribute('content')
|
|
||||||
);
|
);
|
||||||
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
||||||
|
Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
|
||||||
|
([pluginId, searchVersion]) => {
|
||||||
|
algoliaHelper.addDisjunctiveFacetRefinement(
|
||||||
|
'counter',
|
||||||
|
tagToCounter.get(`docs-${pluginId}-${searchVersion}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
Loading…
Reference in New Issue
Block a user