@docusaurus/theme-search-algolia 3.3.1 → 3.4.0

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/index.js CHANGED
@@ -7,23 +7,11 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.validateThemeConfig = void 0;
10
- const tslib_1 = require("tslib");
11
- const path_1 = tslib_1.__importDefault(require("path"));
12
- const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
13
- const lodash_1 = tslib_1.__importDefault(require("lodash"));
14
- const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
- const eta_1 = require("eta");
16
10
  const utils_1 = require("@docusaurus/utils");
17
11
  const theme_translations_1 = require("@docusaurus/theme-translations");
18
- const opensearch_1 = tslib_1.__importDefault(require("./templates/opensearch"));
19
- const getCompiledOpenSearchTemplate = lodash_1.default.memoize(() => (0, eta_1.compile)(opensearch_1.default.trim()));
20
- function renderOpenSearchTemplate(data) {
21
- const compiled = getCompiledOpenSearchTemplate();
22
- return compiled(data, eta_1.defaultConfig);
23
- }
24
- const OPEN_SEARCH_FILENAME = 'opensearch.xml';
12
+ const opensearch_1 = require("./opensearch");
25
13
  function themeSearchAlgolia(context) {
26
- const { baseUrl, siteConfig: { title, url, favicon, themeConfig }, i18n: { currentLocale }, } = context;
14
+ const { baseUrl, siteConfig: { themeConfig }, i18n: { currentLocale }, } = context;
27
15
  const { algolia: { searchPagePath }, } = themeConfig;
28
16
  return {
29
17
  name: 'docusaurus-theme-search-algolia',
@@ -48,40 +36,16 @@ function themeSearchAlgolia(context) {
48
36
  });
49
37
  }
50
38
  },
51
- async postBuild({ outDir }) {
52
- if (searchPagePath) {
53
- const siteUrl = (0, utils_1.normalizeUrl)([url, baseUrl]);
54
- try {
55
- await fs_extra_1.default.writeFile(path_1.default.join(outDir, OPEN_SEARCH_FILENAME), renderOpenSearchTemplate({
56
- title,
57
- siteUrl,
58
- searchUrl: (0, utils_1.normalizeUrl)([siteUrl, searchPagePath]),
59
- faviconUrl: favicon ? (0, utils_1.normalizeUrl)([siteUrl, favicon]) : null,
60
- }));
61
- }
62
- catch (err) {
63
- logger_1.default.error('Generating OpenSearch file failed.');
64
- throw err;
65
- }
39
+ async postBuild() {
40
+ if ((0, opensearch_1.shouldCreateOpenSearchFile)({ context })) {
41
+ await (0, opensearch_1.createOpenSearchFile)({ context });
66
42
  }
67
43
  },
68
44
  injectHtmlTags() {
69
- if (!searchPagePath) {
70
- return {};
45
+ if ((0, opensearch_1.shouldCreateOpenSearchFile)({ context })) {
46
+ return { headTags: (0, opensearch_1.createOpenSearchHeadTags)({ context }) };
71
47
  }
72
- return {
73
- headTags: [
74
- {
75
- tagName: 'link',
76
- attributes: {
77
- rel: 'search',
78
- type: 'application/opensearchdescription+xml',
79
- title,
80
- href: (0, utils_1.normalizeUrl)([baseUrl, OPEN_SEARCH_FILENAME]),
81
- },
82
- },
83
- ],
84
- };
48
+ return {};
85
49
  },
86
50
  };
87
51
  }
@@ -0,0 +1,16 @@
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
+ import type { HtmlTags, LoadContext } from '@docusaurus/types';
8
+ export declare function shouldCreateOpenSearchFile({ context, }: {
9
+ context: LoadContext;
10
+ }): boolean;
11
+ export declare function createOpenSearchFile({ context, }: {
12
+ context: LoadContext;
13
+ }): Promise<void>;
14
+ export declare function createOpenSearchHeadTags({ context, }: {
15
+ context: LoadContext;
16
+ }): HtmlTags;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createOpenSearchHeadTags = exports.createOpenSearchFile = exports.shouldCreateOpenSearchFile = void 0;
10
+ const tslib_1 = require("tslib");
11
+ const path_1 = tslib_1.__importDefault(require("path"));
12
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
13
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
14
+ const eta_1 = require("eta");
15
+ const utils_1 = require("@docusaurus/utils");
16
+ const opensearch_1 = tslib_1.__importDefault(require("./templates/opensearch"));
17
+ const getCompiledOpenSearchTemplate = lodash_1.default.memoize(() => (0, eta_1.compile)(opensearch_1.default.trim()));
18
+ function renderOpenSearchTemplate(data) {
19
+ const compiled = getCompiledOpenSearchTemplate();
20
+ return compiled(data, eta_1.defaultConfig);
21
+ }
22
+ const OPEN_SEARCH_FILENAME = 'opensearch.xml';
23
+ function shouldCreateOpenSearchFile({ context, }) {
24
+ const { siteConfig: { themeConfig, future: { experimental_router: router }, }, } = context;
25
+ const { algolia: { searchPagePath }, } = themeConfig;
26
+ return !!searchPagePath && router !== 'hash';
27
+ }
28
+ exports.shouldCreateOpenSearchFile = shouldCreateOpenSearchFile;
29
+ function createOpenSearchFileContent({ context, searchPagePath, }) {
30
+ const { baseUrl, siteConfig: { title, url, favicon }, } = context;
31
+ const siteUrl = (0, utils_1.normalizeUrl)([url, baseUrl]);
32
+ return renderOpenSearchTemplate({
33
+ title,
34
+ siteUrl,
35
+ searchUrl: (0, utils_1.normalizeUrl)([siteUrl, searchPagePath]),
36
+ faviconUrl: favicon ? (0, utils_1.normalizeUrl)([siteUrl, favicon]) : null,
37
+ });
38
+ }
39
+ async function createOpenSearchFile({ context, }) {
40
+ const { outDir, siteConfig: { themeConfig }, } = context;
41
+ const { algolia: { searchPagePath }, } = themeConfig;
42
+ if (!searchPagePath) {
43
+ throw new Error('no searchPagePath provided in themeConfig.algolia');
44
+ }
45
+ const fileContent = createOpenSearchFileContent({ context, searchPagePath });
46
+ try {
47
+ await fs_extra_1.default.writeFile(path_1.default.join(outDir, OPEN_SEARCH_FILENAME), fileContent);
48
+ }
49
+ catch (err) {
50
+ throw new Error('Generating OpenSearch file failed.', { cause: err });
51
+ }
52
+ }
53
+ exports.createOpenSearchFile = createOpenSearchFile;
54
+ function createOpenSearchHeadTags({ context, }) {
55
+ const { baseUrl, siteConfig: { title }, } = context;
56
+ return {
57
+ tagName: 'link',
58
+ attributes: {
59
+ rel: 'search',
60
+ type: 'application/opensearchdescription+xml',
61
+ title,
62
+ href: (0, utils_1.normalizeUrl)([baseUrl, OPEN_SEARCH_FILENAME]),
63
+ },
64
+ };
65
+ }
66
+ exports.createOpenSearchHeadTags = createOpenSearchHeadTags;
@@ -73,29 +73,29 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
73
73
  DocSearchModal = Modal;
74
74
  });
75
75
  }, []);
76
- const onOpen = useCallback(() => {
77
- importDocSearchModalIfNeeded().then(() => {
78
- searchContainer.current = document.createElement('div');
79
- document.body.insertBefore(
80
- searchContainer.current,
81
- document.body.firstChild,
82
- );
83
- setIsOpen(true);
84
- });
85
- }, [importDocSearchModalIfNeeded, setIsOpen]);
86
- const onClose = useCallback(() => {
76
+ const prepareSearchContainer = useCallback(() => {
77
+ if (!searchContainer.current) {
78
+ const divElement = document.createElement('div');
79
+ searchContainer.current = divElement;
80
+ document.body.insertBefore(divElement, document.body.firstChild);
81
+ }
82
+ }, []);
83
+ const openModal = useCallback(() => {
84
+ prepareSearchContainer();
85
+ importDocSearchModalIfNeeded().then(() => setIsOpen(true));
86
+ }, [importDocSearchModalIfNeeded, prepareSearchContainer]);
87
+ const closeModal = useCallback(() => {
87
88
  setIsOpen(false);
88
- searchContainer.current?.remove();
89
89
  searchButtonRef.current?.focus();
90
- }, [setIsOpen]);
91
- const onInput = useCallback(
90
+ }, []);
91
+ const handleInput = useCallback(
92
92
  (event) => {
93
- importDocSearchModalIfNeeded().then(() => {
94
- setIsOpen(true);
95
- setInitialQuery(event.key);
96
- });
93
+ // prevents duplicate key insertion in the modal input
94
+ event.preventDefault();
95
+ setInitialQuery(event.key);
96
+ openModal();
97
97
  },
98
- [importDocSearchModalIfNeeded, setIsOpen, setInitialQuery],
98
+ [openModal],
99
99
  );
100
100
  const navigator = useRef({
101
101
  navigate({itemUrl}) {
@@ -122,8 +122,8 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
122
122
  () =>
123
123
  // eslint-disable-next-line react/no-unstable-nested-components
124
124
  (footerProps) =>
125
- <ResultsFooter {...footerProps} onClose={onClose} />,
126
- [onClose],
125
+ <ResultsFooter {...footerProps} onClose={closeModal} />,
126
+ [closeModal],
127
127
  );
128
128
  const transformSearchClient = useCallback(
129
129
  (searchClient) => {
@@ -137,9 +137,9 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
137
137
  );
138
138
  useDocSearchKeyboardEvents({
139
139
  isOpen,
140
- onOpen,
141
- onClose,
142
- onInput,
140
+ onOpen: openModal,
141
+ onClose: closeModal,
142
+ onInput: handleInput,
143
143
  searchButtonRef,
144
144
  });
145
145
  return (
@@ -159,7 +159,7 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
159
159
  onTouchStart={importDocSearchModalIfNeeded}
160
160
  onFocus={importDocSearchModalIfNeeded}
161
161
  onMouseOver={importDocSearchModalIfNeeded}
162
- onClick={onOpen}
162
+ onClick={openModal}
163
163
  ref={searchButtonRef}
164
164
  translations={translations.button}
165
165
  />
@@ -169,7 +169,7 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
169
169
  searchContainer.current &&
170
170
  createPortal(
171
171
  <DocSearchModal
172
- onClose={onClose}
172
+ onClose={closeModal}
173
173
  initialScrollY={window.scrollY}
174
174
  initialQuery={initialQuery}
175
175
  navigator={navigator}
@@ -119,7 +119,7 @@ function SearchPageContent() {
119
119
  i18n: {currentLocale},
120
120
  } = useDocusaurusContext();
121
121
  const {
122
- algolia: {appId, apiKey, indexName},
122
+ algolia: {appId, apiKey, indexName, contextualSearch},
123
123
  } = useAlgoliaThemeConfig();
124
124
  const processSearchResultUrl = useSearchResultUrlProcessor();
125
125
  const documentsFoundPlural = useDocumentsFoundPlural();
@@ -169,11 +169,15 @@ function SearchPageContent() {
169
169
  },
170
170
  initialSearchResultState,
171
171
  );
172
+ // respect settings from the theme config for facets
173
+ const disjunctiveFacets = contextualSearch
174
+ ? ['language', 'docusaurus_tag']
175
+ : [];
172
176
  const algoliaClient = algoliaSearch(appId, apiKey);
173
177
  const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
174
178
  hitsPerPage: 15,
175
179
  advancedSyntax: true,
176
- disjunctiveFacets: ['language', 'docusaurus_tag'],
180
+ disjunctiveFacets,
177
181
  });
178
182
  algoliaHelper.on(
179
183
  'result',
@@ -256,16 +260,18 @@ function SearchPageContent() {
256
260
  description: 'The search page title for empty query',
257
261
  });
258
262
  const makeSearch = useEvent((page = 0) => {
259
- algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
260
- algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
261
- Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
262
- ([pluginId, searchVersion]) => {
263
- algoliaHelper.addDisjunctiveFacetRefinement(
264
- 'docusaurus_tag',
265
- `docs-${pluginId}-${searchVersion}`,
266
- );
267
- },
268
- );
263
+ if (contextualSearch) {
264
+ algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
265
+ algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
266
+ Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
267
+ ([pluginId, searchVersion]) => {
268
+ algoliaHelper.addDisjunctiveFacetRefinement(
269
+ 'docusaurus_tag',
270
+ `docs-${pluginId}-${searchVersion}`,
271
+ );
272
+ },
273
+ );
274
+ }
269
275
  algoliaHelper.setQuery(searchQuery).setPage(page).search();
270
276
  });
271
277
  useEffect(() => {
@@ -335,7 +341,7 @@ function SearchPageContent() {
335
341
  />
336
342
  </div>
337
343
 
338
- {docsSearchVersionsHelpers.versioningEnabled && (
344
+ {contextualSearch && docsSearchVersionsHelpers.versioningEnabled && (
339
345
  <SearchVersionSelectList
340
346
  docsSearchVersionsHelpers={docsSearchVersionsHelpers}
341
347
  />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/theme-search-algolia",
3
- "version": "3.3.1",
3
+ "version": "3.4.0",
4
4
  "description": "Algolia search component for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "sideEffects": [
@@ -34,13 +34,13 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@docsearch/react": "^3.5.2",
37
- "@docusaurus/core": "3.3.1",
38
- "@docusaurus/logger": "3.3.0",
39
- "@docusaurus/plugin-content-docs": "3.3.1",
40
- "@docusaurus/theme-common": "3.3.1",
41
- "@docusaurus/theme-translations": "3.3.1",
42
- "@docusaurus/utils": "3.3.0",
43
- "@docusaurus/utils-validation": "3.3.0",
37
+ "@docusaurus/core": "3.4.0",
38
+ "@docusaurus/logger": "3.4.0",
39
+ "@docusaurus/plugin-content-docs": "3.4.0",
40
+ "@docusaurus/theme-common": "3.4.0",
41
+ "@docusaurus/theme-translations": "3.4.0",
42
+ "@docusaurus/utils": "3.4.0",
43
+ "@docusaurus/utils-validation": "3.4.0",
44
44
  "algoliasearch": "^4.18.0",
45
45
  "algoliasearch-helper": "^3.13.3",
46
46
  "clsx": "^2.0.0",
@@ -51,7 +51,7 @@
51
51
  "utility-types": "^3.10.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@docusaurus/module-type-aliases": "3.3.1"
54
+ "@docusaurus/module-type-aliases": "3.4.0"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "react": "^18.0.0",
@@ -60,5 +60,5 @@
60
60
  "engines": {
61
61
  "node": ">=18.0"
62
62
  },
63
- "gitHead": "f3524cf332e803ff82783ee9c1c86c4342fc1f71"
63
+ "gitHead": "49e9a2143274a8dd795659b417b470bc42abbd6e"
64
64
  }
package/src/index.ts CHANGED
@@ -5,38 +5,21 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
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
8
  import {normalizeUrl} from '@docusaurus/utils';
14
9
  import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations';
15
- import openSearchTemplate from './templates/opensearch';
10
+ import {
11
+ createOpenSearchFile,
12
+ createOpenSearchHeadTags,
13
+ shouldCreateOpenSearchFile,
14
+ } from './opensearch';
16
15
 
17
16
  import type {LoadContext, Plugin} from '@docusaurus/types';
18
17
  import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
19
18
 
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
19
  export default function themeSearchAlgolia(context: LoadContext): Plugin<void> {
37
20
  const {
38
21
  baseUrl,
39
- siteConfig: {title, url, favicon, themeConfig},
22
+ siteConfig: {themeConfig},
40
23
  i18n: {currentLocale},
41
24
  } = context;
42
25
  const {
@@ -70,45 +53,17 @@ export default function themeSearchAlgolia(context: LoadContext): Plugin<void> {
70
53
  }
71
54
  },
72
55
 
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
- }
56
+ async postBuild() {
57
+ if (shouldCreateOpenSearchFile({context})) {
58
+ await createOpenSearchFile({context});
91
59
  }
92
60
  },
93
61
 
94
62
  injectHtmlTags() {
95
- if (!searchPagePath) {
96
- return {};
63
+ if (shouldCreateOpenSearchFile({context})) {
64
+ return {headTags: createOpenSearchHeadTags({context})};
97
65
  }
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
- };
66
+ return {};
112
67
  },
113
68
  };
114
69
  }
@@ -0,0 +1,115 @@
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 {defaultConfig, compile} from 'eta';
12
+ import {normalizeUrl} from '@docusaurus/utils';
13
+ import openSearchTemplate from './templates/opensearch';
14
+
15
+ import type {HtmlTags, LoadContext} from '@docusaurus/types';
16
+ import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
17
+
18
+ const getCompiledOpenSearchTemplate = _.memoize(() =>
19
+ compile(openSearchTemplate.trim()),
20
+ );
21
+
22
+ function renderOpenSearchTemplate(data: {
23
+ title: string;
24
+ siteUrl: string;
25
+ searchUrl: string;
26
+ faviconUrl: string | null;
27
+ }) {
28
+ const compiled = getCompiledOpenSearchTemplate();
29
+ return compiled(data, defaultConfig);
30
+ }
31
+
32
+ const OPEN_SEARCH_FILENAME = 'opensearch.xml';
33
+
34
+ export function shouldCreateOpenSearchFile({
35
+ context,
36
+ }: {
37
+ context: LoadContext;
38
+ }): boolean {
39
+ const {
40
+ siteConfig: {
41
+ themeConfig,
42
+ future: {experimental_router: router},
43
+ },
44
+ } = context;
45
+ const {
46
+ algolia: {searchPagePath},
47
+ } = themeConfig as ThemeConfig;
48
+
49
+ return !!searchPagePath && router !== 'hash';
50
+ }
51
+
52
+ function createOpenSearchFileContent({
53
+ context,
54
+ searchPagePath,
55
+ }: {
56
+ context: LoadContext;
57
+ searchPagePath: string;
58
+ }): string {
59
+ const {
60
+ baseUrl,
61
+ siteConfig: {title, url, favicon},
62
+ } = context;
63
+
64
+ const siteUrl = normalizeUrl([url, baseUrl]);
65
+
66
+ return renderOpenSearchTemplate({
67
+ title,
68
+ siteUrl,
69
+ searchUrl: normalizeUrl([siteUrl, searchPagePath]),
70
+ faviconUrl: favicon ? normalizeUrl([siteUrl, favicon]) : null,
71
+ });
72
+ }
73
+
74
+ export async function createOpenSearchFile({
75
+ context,
76
+ }: {
77
+ context: LoadContext;
78
+ }): Promise<void> {
79
+ const {
80
+ outDir,
81
+ siteConfig: {themeConfig},
82
+ } = context;
83
+ const {
84
+ algolia: {searchPagePath},
85
+ } = themeConfig as ThemeConfig;
86
+ if (!searchPagePath) {
87
+ throw new Error('no searchPagePath provided in themeConfig.algolia');
88
+ }
89
+ const fileContent = createOpenSearchFileContent({context, searchPagePath});
90
+ try {
91
+ await fs.writeFile(path.join(outDir, OPEN_SEARCH_FILENAME), fileContent);
92
+ } catch (err) {
93
+ throw new Error('Generating OpenSearch file failed.', {cause: err});
94
+ }
95
+ }
96
+
97
+ export function createOpenSearchHeadTags({
98
+ context,
99
+ }: {
100
+ context: LoadContext;
101
+ }): HtmlTags {
102
+ const {
103
+ baseUrl,
104
+ siteConfig: {title},
105
+ } = context;
106
+ return {
107
+ tagName: 'link',
108
+ attributes: {
109
+ rel: 'search',
110
+ type: 'application/opensearchdescription+xml',
111
+ title,
112
+ href: normalizeUrl([baseUrl, OPEN_SEARCH_FILENAME]),
113
+ },
114
+ };
115
+ }
@@ -136,31 +136,32 @@ function DocSearch({
136
136
  });
137
137
  }, []);
138
138
 
139
- const onOpen = useCallback(() => {
140
- importDocSearchModalIfNeeded().then(() => {
141
- searchContainer.current = document.createElement('div');
142
- document.body.insertBefore(
143
- searchContainer.current,
144
- document.body.firstChild,
145
- );
146
- setIsOpen(true);
147
- });
148
- }, [importDocSearchModalIfNeeded, setIsOpen]);
139
+ const prepareSearchContainer = useCallback(() => {
140
+ if (!searchContainer.current) {
141
+ const divElement = document.createElement('div');
142
+ searchContainer.current = divElement;
143
+ document.body.insertBefore(divElement, document.body.firstChild);
144
+ }
145
+ }, []);
146
+
147
+ const openModal = useCallback(() => {
148
+ prepareSearchContainer();
149
+ importDocSearchModalIfNeeded().then(() => setIsOpen(true));
150
+ }, [importDocSearchModalIfNeeded, prepareSearchContainer]);
149
151
 
150
- const onClose = useCallback(() => {
152
+ const closeModal = useCallback(() => {
151
153
  setIsOpen(false);
152
- searchContainer.current?.remove();
153
154
  searchButtonRef.current?.focus();
154
- }, [setIsOpen]);
155
+ }, []);
155
156
 
156
- const onInput = useCallback(
157
+ const handleInput = useCallback(
157
158
  (event: KeyboardEvent) => {
158
- importDocSearchModalIfNeeded().then(() => {
159
- setIsOpen(true);
160
- setInitialQuery(event.key);
161
- });
159
+ // prevents duplicate key insertion in the modal input
160
+ event.preventDefault();
161
+ setInitialQuery(event.key);
162
+ openModal();
162
163
  },
163
- [importDocSearchModalIfNeeded, setIsOpen, setInitialQuery],
164
+ [openModal],
164
165
  );
165
166
 
166
167
  const navigator = useRef({
@@ -192,8 +193,8 @@ function DocSearch({
192
193
  () =>
193
194
  // eslint-disable-next-line react/no-unstable-nested-components
194
195
  (footerProps: Omit<ResultsFooterProps, 'onClose'>): JSX.Element =>
195
- <ResultsFooter {...footerProps} onClose={onClose} />,
196
- [onClose],
196
+ <ResultsFooter {...footerProps} onClose={closeModal} />,
197
+ [closeModal],
197
198
  );
198
199
 
199
200
  const transformSearchClient = useCallback(
@@ -210,9 +211,9 @@ function DocSearch({
210
211
 
211
212
  useDocSearchKeyboardEvents({
212
213
  isOpen,
213
- onOpen,
214
- onClose,
215
- onInput,
214
+ onOpen: openModal,
215
+ onClose: closeModal,
216
+ onInput: handleInput,
216
217
  searchButtonRef,
217
218
  });
218
219
 
@@ -233,7 +234,7 @@ function DocSearch({
233
234
  onTouchStart={importDocSearchModalIfNeeded}
234
235
  onFocus={importDocSearchModalIfNeeded}
235
236
  onMouseOver={importDocSearchModalIfNeeded}
236
- onClick={onOpen}
237
+ onClick={openModal}
237
238
  ref={searchButtonRef}
238
239
  translations={translations.button}
239
240
  />
@@ -243,7 +244,7 @@ function DocSearch({
243
244
  searchContainer.current &&
244
245
  createPortal(
245
246
  <DocSearchModal
246
- onClose={onClose}
247
+ onClose={closeModal}
247
248
  initialScrollY={window.scrollY}
248
249
  initialQuery={initialQuery}
249
250
  navigator={navigator}
@@ -159,8 +159,9 @@ function SearchPageContent(): JSX.Element {
159
159
  i18n: {currentLocale},
160
160
  } = useDocusaurusContext();
161
161
  const {
162
- algolia: {appId, apiKey, indexName},
162
+ algolia: {appId, apiKey, indexName, contextualSearch},
163
163
  } = useAlgoliaThemeConfig();
164
+
164
165
  const processSearchResultUrl = useSearchResultUrlProcessor();
165
166
  const documentsFoundPlural = useDocumentsFoundPlural();
166
167
 
@@ -213,11 +214,16 @@ function SearchPageContent(): JSX.Element {
213
214
  initialSearchResultState,
214
215
  );
215
216
 
217
+ // respect settings from the theme config for facets
218
+ const disjunctiveFacets = contextualSearch
219
+ ? ['language', 'docusaurus_tag']
220
+ : [];
221
+
216
222
  const algoliaClient = algoliaSearch(appId, apiKey);
217
223
  const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
218
224
  hitsPerPage: 15,
219
225
  advancedSyntax: true,
220
- disjunctiveFacets: ['language', 'docusaurus_tag'],
226
+ disjunctiveFacets,
221
227
  });
222
228
 
223
229
  algoliaHelper.on(
@@ -313,17 +319,19 @@ function SearchPageContent(): JSX.Element {
313
319
  });
314
320
 
315
321
  const makeSearch = useEvent((page: number = 0) => {
316
- algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
317
- algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
318
-
319
- Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
320
- ([pluginId, searchVersion]) => {
321
- algoliaHelper.addDisjunctiveFacetRefinement(
322
- 'docusaurus_tag',
323
- `docs-${pluginId}-${searchVersion}`,
324
- );
325
- },
326
- );
322
+ if (contextualSearch) {
323
+ algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
324
+ algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
325
+
326
+ Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
327
+ ([pluginId, searchVersion]) => {
328
+ algoliaHelper.addDisjunctiveFacetRefinement(
329
+ 'docusaurus_tag',
330
+ `docs-${pluginId}-${searchVersion}`,
331
+ );
332
+ },
333
+ );
334
+ }
327
335
 
328
336
  algoliaHelper.setQuery(searchQuery).setPage(page).search();
329
337
  });
@@ -401,7 +409,7 @@ function SearchPageContent(): JSX.Element {
401
409
  />
402
410
  </div>
403
411
 
404
- {docsSearchVersionsHelpers.versioningEnabled && (
412
+ {contextualSearch && docsSearchVersionsHelpers.versioningEnabled && (
405
413
  <SearchVersionSelectList
406
414
  docsSearchVersionsHelpers={docsSearchVersionsHelpers}
407
415
  />