@docusaurus/theme-search-algolia 2.0.0-beta.2 → 2.0.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/index.d.ts +7 -0
- package/lib/client/index.js +7 -0
- package/lib/client/useAlgoliaContextualFacetFilters.d.ts +7 -0
- package/lib/client/useAlgoliaContextualFacetFilters.js +15 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +90 -0
- package/lib/templates/opensearch.d.ts +8 -0
- package/lib/templates/opensearch.js +23 -0
- package/lib/theme/SearchBar/index.d.ts +8 -0
- package/{src → lib}/theme/SearchBar/index.js +58 -70
- package/lib/theme/SearchBar/styles.css +21 -0
- package/lib/theme/SearchPage/index.d.ts +8 -0
- package/{src → lib}/theme/SearchPage/index.js +65 -92
- package/lib/theme/SearchPage/styles.module.css +119 -0
- package/lib/theme/SearchTranslations/index.d.ts +11 -0
- package/lib/theme/SearchTranslations/index.js +167 -0
- package/lib/validateThemeConfig.d.ts +15 -0
- package/lib/validateThemeConfig.js +44 -0
- package/package.json +43 -14
- package/src/client/index.ts +8 -0
- package/src/{theme/hooks/useAlgoliaContextualFacetFilters.js → client/useAlgoliaContextualFacetFilters.ts} +3 -3
- package/src/deps.d.ts +20 -0
- package/src/index.ts +116 -0
- package/src/templates/{opensearch.js → opensearch.ts} +7 -5
- package/src/theme/SearchBar/index.tsx +271 -0
- package/src/theme/SearchPage/index.tsx +535 -0
- package/src/theme/SearchPage/styles.module.css +4 -4
- package/src/theme/SearchTranslations/index.ts +172 -0
- package/src/theme-search-algolia.d.ts +42 -0
- package/src/types.d.ts +10 -0
- package/src/validateThemeConfig.ts +53 -0
- package/src/__tests__/validateThemeConfig.test.js +0 -121
- package/src/index.js +0 -92
- package/src/theme/SearchBar/styles.module.css +0 -20
- package/src/theme/SearchMetadatas/index.js +0 -25
- package/src/theme/hooks/useSearchQuery.js +0 -44
- package/src/validateThemeConfig.js +0 -45
package/package.json
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/theme-search-algolia",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.22",
|
|
4
4
|
"description": "Algolia search component for Docusaurus.",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"*.css"
|
|
8
|
+
],
|
|
9
|
+
"exports": {
|
|
10
|
+
"./client": {
|
|
11
|
+
"types": "./lib/client/index.d.ts",
|
|
12
|
+
"default": "./lib/client/index.js"
|
|
13
|
+
},
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./src/theme-search-algolia.d.ts",
|
|
16
|
+
"default": "./lib/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"types": "src/theme-search-algolia.d.ts",
|
|
6
20
|
"publishConfig": {
|
|
7
21
|
"access": "public"
|
|
8
22
|
},
|
|
@@ -12,24 +26,39 @@
|
|
|
12
26
|
"directory": "packages/docusaurus-theme-search-algolia"
|
|
13
27
|
},
|
|
14
28
|
"license": "MIT",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc --build && node ../../admin/scripts/copyUntypedFiles.js && prettier --config ../../.prettierrc --write \"lib/theme/**/*.js\"",
|
|
31
|
+
"watch": "run-p -c copy:watch build:watch",
|
|
32
|
+
"build:watch": "tsc --build --watch",
|
|
33
|
+
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
|
34
|
+
},
|
|
15
35
|
"dependencies": {
|
|
16
|
-
"@docsearch/react": "^3.
|
|
17
|
-
"@docusaurus/core": "2.0.0-beta.
|
|
18
|
-
"@docusaurus/
|
|
19
|
-
"@docusaurus/
|
|
20
|
-
"@docusaurus/
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
36
|
+
"@docsearch/react": "^3.1.1",
|
|
37
|
+
"@docusaurus/core": "2.0.0-beta.22",
|
|
38
|
+
"@docusaurus/logger": "2.0.0-beta.22",
|
|
39
|
+
"@docusaurus/plugin-content-docs": "2.0.0-beta.22",
|
|
40
|
+
"@docusaurus/theme-common": "2.0.0-beta.22",
|
|
41
|
+
"@docusaurus/theme-translations": "2.0.0-beta.22",
|
|
42
|
+
"@docusaurus/utils": "2.0.0-beta.22",
|
|
43
|
+
"@docusaurus/utils-validation": "2.0.0-beta.22",
|
|
44
|
+
"algoliasearch": "^4.13.1",
|
|
45
|
+
"algoliasearch-helper": "^3.10.0",
|
|
46
|
+
"clsx": "^1.2.0",
|
|
47
|
+
"eta": "^1.12.3",
|
|
48
|
+
"fs-extra": "^10.1.0",
|
|
49
|
+
"lodash": "^4.17.21",
|
|
50
|
+
"tslib": "^2.4.0",
|
|
51
|
+
"utility-types": "^3.10.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@docusaurus/module-type-aliases": "2.0.0-beta.22"
|
|
26
55
|
},
|
|
27
56
|
"peerDependencies": {
|
|
28
57
|
"react": "^16.8.4 || ^17.0.0",
|
|
29
58
|
"react-dom": "^16.8.4 || ^17.0.0"
|
|
30
59
|
},
|
|
31
60
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
61
|
+
"node": ">=16.14"
|
|
33
62
|
},
|
|
34
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "daf9e462c4eebb7ac26a940932311f987e768f87"
|
|
35
64
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export {useAlgoliaContextualFacetFilters} from './useAlgoliaContextualFacetFilters';
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import useContextualSearchFilters from '@theme
|
|
8
|
+
import {useContextualSearchFilters} from '@docusaurus/theme-common';
|
|
9
9
|
|
|
10
10
|
// Translate search-engine agnostic search filters to Algolia search filters
|
|
11
|
-
export
|
|
11
|
+
export function useAlgoliaContextualFacetFilters(): [string, string[]] {
|
|
12
12
|
const {locale, tags} = useContextualSearchFilters();
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// Seems safe to convert locale->language, see AlgoliaSearchMetadata comment
|
|
15
15
|
const languageFilter = `language:${locale}`;
|
|
16
16
|
|
|
17
17
|
const tagsFilter = tags.map((tag) => `docusaurus_tag:${tag}`);
|
package/src/deps.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare module '@docsearch/react/modal';
|
|
9
|
+
|
|
10
|
+
declare module '@docsearch/react/style';
|
|
11
|
+
|
|
12
|
+
// TODO incompatible declaration file
|
|
13
|
+
declare module 'eta' {
|
|
14
|
+
export const defaultConfig: object;
|
|
15
|
+
|
|
16
|
+
export function compile(
|
|
17
|
+
template: string,
|
|
18
|
+
options?: object,
|
|
19
|
+
): (data: object, config: object) => string;
|
|
20
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import _ from 'lodash';
|
|
11
|
+
import logger from '@docusaurus/logger';
|
|
12
|
+
import {defaultConfig, compile} from 'eta';
|
|
13
|
+
import {normalizeUrl} from '@docusaurus/utils';
|
|
14
|
+
import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations';
|
|
15
|
+
import openSearchTemplate from './templates/opensearch';
|
|
16
|
+
|
|
17
|
+
import type {LoadContext, Plugin} from '@docusaurus/types';
|
|
18
|
+
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
|
19
|
+
|
|
20
|
+
const getCompiledOpenSearchTemplate = _.memoize(() =>
|
|
21
|
+
compile(openSearchTemplate.trim()),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
function renderOpenSearchTemplate(data: {
|
|
25
|
+
title: string;
|
|
26
|
+
siteUrl: string;
|
|
27
|
+
searchUrl: string;
|
|
28
|
+
faviconUrl: string | null;
|
|
29
|
+
}) {
|
|
30
|
+
const compiled = getCompiledOpenSearchTemplate();
|
|
31
|
+
return compiled(data, defaultConfig);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const OPEN_SEARCH_FILENAME = 'opensearch.xml';
|
|
35
|
+
|
|
36
|
+
export default function themeSearchAlgolia(context: LoadContext): Plugin<void> {
|
|
37
|
+
const {
|
|
38
|
+
baseUrl,
|
|
39
|
+
siteConfig: {title, url, favicon, themeConfig},
|
|
40
|
+
i18n: {currentLocale},
|
|
41
|
+
} = context;
|
|
42
|
+
const {
|
|
43
|
+
algolia: {searchPagePath},
|
|
44
|
+
} = themeConfig as ThemeConfig;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
name: 'docusaurus-theme-search-algolia',
|
|
48
|
+
|
|
49
|
+
getThemePath() {
|
|
50
|
+
return '../lib/theme';
|
|
51
|
+
},
|
|
52
|
+
getTypeScriptThemePath() {
|
|
53
|
+
return '../src/theme';
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
getDefaultCodeTranslationMessages() {
|
|
57
|
+
return readDefaultCodeTranslationMessages({
|
|
58
|
+
locale: currentLocale,
|
|
59
|
+
name: 'theme-search-algolia',
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
contentLoaded({actions: {addRoute}}) {
|
|
64
|
+
if (searchPagePath) {
|
|
65
|
+
addRoute({
|
|
66
|
+
path: normalizeUrl([baseUrl, searchPagePath]),
|
|
67
|
+
component: '@theme/SearchPage',
|
|
68
|
+
exact: true,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async postBuild({outDir}) {
|
|
74
|
+
if (searchPagePath) {
|
|
75
|
+
const siteUrl = normalizeUrl([url, baseUrl]);
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await fs.writeFile(
|
|
79
|
+
path.join(outDir, OPEN_SEARCH_FILENAME),
|
|
80
|
+
renderOpenSearchTemplate({
|
|
81
|
+
title,
|
|
82
|
+
siteUrl,
|
|
83
|
+
searchUrl: normalizeUrl([siteUrl, searchPagePath]),
|
|
84
|
+
faviconUrl: favicon ? normalizeUrl([siteUrl, favicon]) : null,
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
logger.error('Generating OpenSearch file failed.');
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
injectHtmlTags() {
|
|
95
|
+
if (!searchPagePath) {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
headTags: [
|
|
101
|
+
{
|
|
102
|
+
tagName: 'link',
|
|
103
|
+
attributes: {
|
|
104
|
+
rel: 'search',
|
|
105
|
+
type: 'application/opensearchdescription+xml',
|
|
106
|
+
title,
|
|
107
|
+
href: normalizeUrl([baseUrl, OPEN_SEARCH_FILENAME]),
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export {validateThemeConfig} from './validateThemeConfig';
|
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
export default `
|
|
9
9
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
10
10
|
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
|
|
11
11
|
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
|
|
12
12
|
<ShortName><%= it.title %></ShortName>
|
|
13
13
|
<Description>Search <%= it.title %></Description>
|
|
14
14
|
<InputEncoding>UTF-8</InputEncoding>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
15
|
+
<% if (it.faviconUrl) { _%>
|
|
16
|
+
<Image width="16" height="16" type="image/x-icon"><%= it.faviconUrl %></Image>
|
|
17
|
+
<% } _%>
|
|
18
|
+
<Url type="text/html" method="get" template="<%= it.searchUrl %>?q={searchTerms}"/>
|
|
19
|
+
<Url type="application/opensearchdescription+xml" rel="self" template="<%= it.siteUrl %>opensearch.xml" />
|
|
20
|
+
<moz:SearchForm><%= it.siteUrl %></moz:SearchForm>
|
|
19
21
|
</OpenSearchDescription>
|
|
20
22
|
`;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {useState, useRef, useCallback, useMemo} from 'react';
|
|
9
|
+
import {createPortal} from 'react-dom';
|
|
10
|
+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
11
|
+
import {useHistory} from '@docusaurus/router';
|
|
12
|
+
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
|
|
13
|
+
import Link from '@docusaurus/Link';
|
|
14
|
+
import Head from '@docusaurus/Head';
|
|
15
|
+
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
|
16
|
+
import {useSearchPage} from '@docusaurus/theme-common/internal';
|
|
17
|
+
import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
|
18
|
+
import {useAlgoliaContextualFacetFilters} from '@docusaurus/theme-search-algolia/client';
|
|
19
|
+
import Translate from '@docusaurus/Translate';
|
|
20
|
+
import translations from '@theme/SearchTranslations';
|
|
21
|
+
|
|
22
|
+
import type {
|
|
23
|
+
DocSearchModal as DocSearchModalType,
|
|
24
|
+
DocSearchModalProps,
|
|
25
|
+
} from '@docsearch/react';
|
|
26
|
+
import type {
|
|
27
|
+
InternalDocSearchHit,
|
|
28
|
+
StoredDocSearchHit,
|
|
29
|
+
} from '@docsearch/react/dist/esm/types';
|
|
30
|
+
import type {SearchClient} from 'algoliasearch/lite';
|
|
31
|
+
import type {AutocompleteState} from '@algolia/autocomplete-core';
|
|
32
|
+
|
|
33
|
+
type DocSearchProps = Omit<
|
|
34
|
+
DocSearchModalProps,
|
|
35
|
+
'onClose' | 'initialScrollY'
|
|
36
|
+
> & {
|
|
37
|
+
contextualSearch?: string;
|
|
38
|
+
externalUrlRegex?: string;
|
|
39
|
+
searchPagePath: boolean | string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let DocSearchModal: typeof DocSearchModalType | null = null;
|
|
43
|
+
|
|
44
|
+
function Hit({
|
|
45
|
+
hit,
|
|
46
|
+
children,
|
|
47
|
+
}: {
|
|
48
|
+
hit: InternalDocSearchHit | StoredDocSearchHit;
|
|
49
|
+
children: React.ReactNode;
|
|
50
|
+
}) {
|
|
51
|
+
return <Link to={hit.url}>{children}</Link>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type ResultsFooterProps = {
|
|
55
|
+
state: AutocompleteState<InternalDocSearchHit>;
|
|
56
|
+
onClose: () => void;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function ResultsFooter({state, onClose}: ResultsFooterProps) {
|
|
60
|
+
const {generateSearchPageLink} = useSearchPage();
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Link to={generateSearchPageLink(state.query)} onClick={onClose}>
|
|
64
|
+
<Translate
|
|
65
|
+
id="theme.SearchBar.seeAll"
|
|
66
|
+
values={{count: state.context.nbHits}}>
|
|
67
|
+
{'See all {count} results'}
|
|
68
|
+
</Translate>
|
|
69
|
+
</Link>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type FacetFilters = Required<
|
|
74
|
+
Required<DocSearchProps>['searchParameters']
|
|
75
|
+
>['facetFilters'];
|
|
76
|
+
|
|
77
|
+
function mergeFacetFilters(f1: FacetFilters, f2: FacetFilters): FacetFilters {
|
|
78
|
+
const normalize = (
|
|
79
|
+
f: FacetFilters,
|
|
80
|
+
): readonly string[] | readonly (string | readonly string[])[] =>
|
|
81
|
+
typeof f === 'string' ? [f] : f;
|
|
82
|
+
return [...normalize(f1), ...normalize(f2)] as FacetFilters;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function DocSearch({
|
|
86
|
+
contextualSearch,
|
|
87
|
+
externalUrlRegex,
|
|
88
|
+
...props
|
|
89
|
+
}: DocSearchProps) {
|
|
90
|
+
const {siteMetadata} = useDocusaurusContext();
|
|
91
|
+
|
|
92
|
+
const contextualSearchFacetFilters =
|
|
93
|
+
useAlgoliaContextualFacetFilters() as FacetFilters;
|
|
94
|
+
|
|
95
|
+
const configFacetFilters: FacetFilters =
|
|
96
|
+
props.searchParameters?.facetFilters ?? [];
|
|
97
|
+
|
|
98
|
+
const facetFilters: FacetFilters = contextualSearch
|
|
99
|
+
? // Merge contextual search filters with config filters
|
|
100
|
+
mergeFacetFilters(contextualSearchFacetFilters, configFacetFilters)
|
|
101
|
+
: // ... or use config facetFilters
|
|
102
|
+
configFacetFilters;
|
|
103
|
+
|
|
104
|
+
// We let user override default searchParameters if she wants to
|
|
105
|
+
const searchParameters: DocSearchProps['searchParameters'] = {
|
|
106
|
+
...props.searchParameters,
|
|
107
|
+
facetFilters,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const {withBaseUrl} = useBaseUrlUtils();
|
|
111
|
+
const history = useHistory();
|
|
112
|
+
const searchContainer = useRef<HTMLDivElement | null>(null);
|
|
113
|
+
const searchButtonRef = useRef<HTMLButtonElement>(null);
|
|
114
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
115
|
+
const [initialQuery, setInitialQuery] = useState<string | undefined>(
|
|
116
|
+
undefined,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const importDocSearchModalIfNeeded = useCallback(() => {
|
|
120
|
+
if (DocSearchModal) {
|
|
121
|
+
return Promise.resolve();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return Promise.all([
|
|
125
|
+
import('@docsearch/react/modal') as Promise<
|
|
126
|
+
typeof import('@docsearch/react')
|
|
127
|
+
>,
|
|
128
|
+
import('@docsearch/react/style'),
|
|
129
|
+
import('./styles.css'),
|
|
130
|
+
]).then(([{DocSearchModal: Modal}]) => {
|
|
131
|
+
DocSearchModal = Modal;
|
|
132
|
+
});
|
|
133
|
+
}, []);
|
|
134
|
+
|
|
135
|
+
const onOpen = useCallback(() => {
|
|
136
|
+
importDocSearchModalIfNeeded().then(() => {
|
|
137
|
+
searchContainer.current = document.createElement('div');
|
|
138
|
+
document.body.insertBefore(
|
|
139
|
+
searchContainer.current,
|
|
140
|
+
document.body.firstChild,
|
|
141
|
+
);
|
|
142
|
+
setIsOpen(true);
|
|
143
|
+
});
|
|
144
|
+
}, [importDocSearchModalIfNeeded, setIsOpen]);
|
|
145
|
+
|
|
146
|
+
const onClose = useCallback(() => {
|
|
147
|
+
setIsOpen(false);
|
|
148
|
+
searchContainer.current?.remove();
|
|
149
|
+
}, [setIsOpen]);
|
|
150
|
+
|
|
151
|
+
const onInput = useCallback(
|
|
152
|
+
(event: KeyboardEvent) => {
|
|
153
|
+
importDocSearchModalIfNeeded().then(() => {
|
|
154
|
+
setIsOpen(true);
|
|
155
|
+
setInitialQuery(event.key);
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
[importDocSearchModalIfNeeded, setIsOpen, setInitialQuery],
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const navigator = useRef({
|
|
162
|
+
navigate({itemUrl}: {itemUrl?: string}) {
|
|
163
|
+
// Algolia results could contain URL's from other domains which cannot
|
|
164
|
+
// be served through history and should navigate with window.location
|
|
165
|
+
if (isRegexpStringMatch(externalUrlRegex, itemUrl)) {
|
|
166
|
+
window.location.href = itemUrl!;
|
|
167
|
+
} else {
|
|
168
|
+
history.push(itemUrl!);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
}).current;
|
|
172
|
+
|
|
173
|
+
const transformItems = useRef<DocSearchModalProps['transformItems']>(
|
|
174
|
+
(items) =>
|
|
175
|
+
items.map((item) => {
|
|
176
|
+
// If Algolia contains a external domain, we should navigate without
|
|
177
|
+
// relative URL
|
|
178
|
+
if (isRegexpStringMatch(externalUrlRegex, item.url)) {
|
|
179
|
+
return item;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// We transform the absolute URL into a relative URL.
|
|
183
|
+
const url = new URL(item.url);
|
|
184
|
+
return {
|
|
185
|
+
...item,
|
|
186
|
+
url: withBaseUrl(`${url.pathname}${url.hash}`),
|
|
187
|
+
};
|
|
188
|
+
}),
|
|
189
|
+
).current;
|
|
190
|
+
|
|
191
|
+
const resultsFooterComponent: DocSearchProps['resultsFooterComponent'] =
|
|
192
|
+
useMemo(
|
|
193
|
+
() =>
|
|
194
|
+
// eslint-disable-next-line react/no-unstable-nested-components
|
|
195
|
+
(footerProps: Omit<ResultsFooterProps, 'onClose'>): JSX.Element =>
|
|
196
|
+
<ResultsFooter {...footerProps} onClose={onClose} />,
|
|
197
|
+
[onClose],
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const transformSearchClient = useCallback(
|
|
201
|
+
(searchClient: SearchClient) => {
|
|
202
|
+
searchClient.addAlgoliaAgent(
|
|
203
|
+
'docusaurus',
|
|
204
|
+
siteMetadata.docusaurusVersion,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
return searchClient;
|
|
208
|
+
},
|
|
209
|
+
[siteMetadata.docusaurusVersion],
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
useDocSearchKeyboardEvents({
|
|
213
|
+
isOpen,
|
|
214
|
+
onOpen,
|
|
215
|
+
onClose,
|
|
216
|
+
onInput,
|
|
217
|
+
searchButtonRef,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<>
|
|
222
|
+
<Head>
|
|
223
|
+
{/* This hints the browser that the website will load data from Algolia,
|
|
224
|
+
and allows it to preconnect to the DocSearch cluster. It makes the first
|
|
225
|
+
query faster, especially on mobile. */}
|
|
226
|
+
<link
|
|
227
|
+
rel="preconnect"
|
|
228
|
+
href={`https://${props.appId}-dsn.algolia.net`}
|
|
229
|
+
crossOrigin="anonymous"
|
|
230
|
+
/>
|
|
231
|
+
</Head>
|
|
232
|
+
|
|
233
|
+
<DocSearchButton
|
|
234
|
+
onTouchStart={importDocSearchModalIfNeeded}
|
|
235
|
+
onFocus={importDocSearchModalIfNeeded}
|
|
236
|
+
onMouseOver={importDocSearchModalIfNeeded}
|
|
237
|
+
onClick={onOpen}
|
|
238
|
+
ref={searchButtonRef}
|
|
239
|
+
translations={translations.button}
|
|
240
|
+
/>
|
|
241
|
+
|
|
242
|
+
{isOpen &&
|
|
243
|
+
DocSearchModal &&
|
|
244
|
+
searchContainer.current &&
|
|
245
|
+
createPortal(
|
|
246
|
+
<DocSearchModal
|
|
247
|
+
onClose={onClose}
|
|
248
|
+
initialScrollY={window.scrollY}
|
|
249
|
+
initialQuery={initialQuery}
|
|
250
|
+
navigator={navigator}
|
|
251
|
+
transformItems={transformItems}
|
|
252
|
+
hitComponent={Hit}
|
|
253
|
+
transformSearchClient={transformSearchClient}
|
|
254
|
+
{...(props.searchPagePath && {
|
|
255
|
+
resultsFooterComponent,
|
|
256
|
+
})}
|
|
257
|
+
{...props}
|
|
258
|
+
searchParameters={searchParameters}
|
|
259
|
+
placeholder={translations.placeholder}
|
|
260
|
+
translations={translations.modal}
|
|
261
|
+
/>,
|
|
262
|
+
searchContainer.current,
|
|
263
|
+
)}
|
|
264
|
+
</>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export default function SearchBar(): JSX.Element {
|
|
269
|
+
const {siteConfig} = useDocusaurusContext();
|
|
270
|
+
return <DocSearch {...(siteConfig.themeConfig.algolia as DocSearchProps)} />;
|
|
271
|
+
}
|