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
|
||||
// prevents Algolia from deleting based on differing package versions and
|
||||
// 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;
|
||||
let counter;
|
||||
if (version) {
|
||||
counter = versionToCounter.get(version);
|
||||
if (!counter) {
|
||||
counter = ++globalCounter;
|
||||
versionToCounter.set(version, counter);
|
||||
}
|
||||
if (tag) {
|
||||
counter = tagToCounter.get(tag);
|
||||
}
|
||||
return (
|
||||
<Head>
|
||||
|
@ -20,6 +20,8 @@ import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
|
||||
import Translate, {translate} from '@docusaurus/Translate';
|
||||
import Layout from '@theme/Layout';
|
||||
import styles from './styles.module.css';
|
||||
// eslint-disable-next-line import/extensions
|
||||
import {tagToCounter} from '../SearchMetadata';
|
||||
// Very simple pluralization: probably good enough for now
|
||||
function useDocumentsFoundPlural() {
|
||||
const {selectMessage} = usePluralForm();
|
||||
@ -263,11 +265,17 @@ function SearchPageContent() {
|
||||
const makeSearch = useEvent((page = 0) => {
|
||||
algoliaHelper.addDisjunctiveFacetRefinement(
|
||||
'counter',
|
||||
document
|
||||
.querySelector('meta[name="docsearch:counter"]')
|
||||
.getAttribute('content')
|
||||
tagToCounter.get('default')
|
||||
);
|
||||
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();
|
||||
});
|
||||
useEffect(() => {
|
||||
|
Loading…
Reference in New Issue
Block a user