@docusaurus/theme-search-algolia 3.6.3 → 3.7.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/theme/SearchBar/index.d.ts +2 -1
- package/lib/theme/SearchBar/index.js +84 -64
- package/lib/theme/SearchPage/index.d.ts +2 -1
- package/lib/theme/SearchPage/index.js +2 -2
- package/package.json +15 -15
- package/src/theme/SearchBar/index.tsx +118 -94
- package/src/theme/SearchPage/index.tsx +11 -5
- package/src/theme-search-algolia.d.ts +33 -16
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
import { type ReactNode } from 'react';
|
|
8
|
+
export default function SearchBar(): ReactNode;
|
|
@@ -22,6 +22,70 @@ import Translate from '@docusaurus/Translate';
|
|
|
22
22
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
23
23
|
import translations from '@theme/SearchTranslations';
|
|
24
24
|
let DocSearchModal = null;
|
|
25
|
+
function importDocSearchModalIfNeeded() {
|
|
26
|
+
if (DocSearchModal) {
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
return Promise.all([
|
|
30
|
+
import('@docsearch/react/modal'),
|
|
31
|
+
import('@docsearch/react/style'),
|
|
32
|
+
import('./styles.css'),
|
|
33
|
+
]).then(([{DocSearchModal: Modal}]) => {
|
|
34
|
+
DocSearchModal = Modal;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function useNavigator({externalUrlRegex}) {
|
|
38
|
+
const history = useHistory();
|
|
39
|
+
const [navigator] = useState(() => {
|
|
40
|
+
return {
|
|
41
|
+
navigate(params) {
|
|
42
|
+
// Algolia results could contain URL's from other domains which cannot
|
|
43
|
+
// be served through history and should navigate with window.location
|
|
44
|
+
if (isRegexpStringMatch(externalUrlRegex, params.itemUrl)) {
|
|
45
|
+
window.location.href = params.itemUrl;
|
|
46
|
+
} else {
|
|
47
|
+
history.push(params.itemUrl);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
return navigator;
|
|
53
|
+
}
|
|
54
|
+
function useTransformSearchClient() {
|
|
55
|
+
const {
|
|
56
|
+
siteMetadata: {docusaurusVersion},
|
|
57
|
+
} = useDocusaurusContext();
|
|
58
|
+
return useCallback(
|
|
59
|
+
(searchClient) => {
|
|
60
|
+
searchClient.addAlgoliaAgent('docusaurus', docusaurusVersion);
|
|
61
|
+
return searchClient;
|
|
62
|
+
},
|
|
63
|
+
[docusaurusVersion],
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
function useTransformItems(props) {
|
|
67
|
+
const processSearchResultUrl = useSearchResultUrlProcessor();
|
|
68
|
+
const [transformItems] = useState(() => {
|
|
69
|
+
return (items) =>
|
|
70
|
+
props.transformItems
|
|
71
|
+
? // Custom transformItems
|
|
72
|
+
props.transformItems(items)
|
|
73
|
+
: // Default transformItems
|
|
74
|
+
items.map((item) => ({
|
|
75
|
+
...item,
|
|
76
|
+
url: processSearchResultUrl(item.url),
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
return transformItems;
|
|
80
|
+
}
|
|
81
|
+
function useResultsFooterComponent({closeModal}) {
|
|
82
|
+
return useMemo(
|
|
83
|
+
() =>
|
|
84
|
+
({state}) =>
|
|
85
|
+
<ResultsFooter state={state} onClose={closeModal} />,
|
|
86
|
+
[closeModal],
|
|
87
|
+
);
|
|
88
|
+
}
|
|
25
89
|
function Hit({hit, children}) {
|
|
26
90
|
return <Link to={hit.url}>{children}</Link>;
|
|
27
91
|
}
|
|
@@ -37,13 +101,11 @@ function ResultsFooter({state, onClose}) {
|
|
|
37
101
|
</Link>
|
|
38
102
|
);
|
|
39
103
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const {siteMetadata} = useDocusaurusContext();
|
|
46
|
-
const processSearchResultUrl = useSearchResultUrlProcessor();
|
|
104
|
+
function useSearchParameters({contextualSearch, ...props}) {
|
|
105
|
+
function mergeFacetFilters(f1, f2) {
|
|
106
|
+
const normalize = (f) => (typeof f === 'string' ? [f] : f);
|
|
107
|
+
return [...normalize(f1), ...normalize(f2)];
|
|
108
|
+
}
|
|
47
109
|
const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters();
|
|
48
110
|
const configFacetFilters = props.searchParameters?.facetFilters ?? [];
|
|
49
111
|
const facetFilters = contextualSearch
|
|
@@ -51,28 +113,22 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
51
113
|
mergeFacetFilters(contextualSearchFacetFilters, configFacetFilters)
|
|
52
114
|
: // ... or use config facetFilters
|
|
53
115
|
configFacetFilters;
|
|
54
|
-
// We let
|
|
55
|
-
|
|
116
|
+
// We let users override default searchParameters if they want to
|
|
117
|
+
return {
|
|
56
118
|
...props.searchParameters,
|
|
57
119
|
facetFilters,
|
|
58
120
|
};
|
|
59
|
-
|
|
121
|
+
}
|
|
122
|
+
function DocSearch({externalUrlRegex, ...props}) {
|
|
123
|
+
const navigator = useNavigator({externalUrlRegex});
|
|
124
|
+
const searchParameters = useSearchParameters({...props});
|
|
125
|
+
const transformItems = useTransformItems(props);
|
|
126
|
+
const transformSearchClient = useTransformSearchClient();
|
|
60
127
|
const searchContainer = useRef(null);
|
|
128
|
+
// TODO remove "as any" after React 19 upgrade
|
|
61
129
|
const searchButtonRef = useRef(null);
|
|
62
130
|
const [isOpen, setIsOpen] = useState(false);
|
|
63
131
|
const [initialQuery, setInitialQuery] = useState(undefined);
|
|
64
|
-
const importDocSearchModalIfNeeded = useCallback(() => {
|
|
65
|
-
if (DocSearchModal) {
|
|
66
|
-
return Promise.resolve();
|
|
67
|
-
}
|
|
68
|
-
return Promise.all([
|
|
69
|
-
import('@docsearch/react/modal'),
|
|
70
|
-
import('@docsearch/react/style'),
|
|
71
|
-
import('./styles.css'),
|
|
72
|
-
]).then(([{DocSearchModal: Modal}]) => {
|
|
73
|
-
DocSearchModal = Modal;
|
|
74
|
-
});
|
|
75
|
-
}, []);
|
|
76
132
|
const prepareSearchContainer = useCallback(() => {
|
|
77
133
|
if (!searchContainer.current) {
|
|
78
134
|
const divElement = document.createElement('div');
|
|
@@ -83,10 +139,11 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
83
139
|
const openModal = useCallback(() => {
|
|
84
140
|
prepareSearchContainer();
|
|
85
141
|
importDocSearchModalIfNeeded().then(() => setIsOpen(true));
|
|
86
|
-
}, [
|
|
142
|
+
}, [prepareSearchContainer]);
|
|
87
143
|
const closeModal = useCallback(() => {
|
|
88
144
|
setIsOpen(false);
|
|
89
145
|
searchButtonRef.current?.focus();
|
|
146
|
+
setInitialQuery(undefined);
|
|
90
147
|
}, []);
|
|
91
148
|
const handleInput = useCallback(
|
|
92
149
|
(event) => {
|
|
@@ -101,44 +158,7 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
101
158
|
},
|
|
102
159
|
[openModal],
|
|
103
160
|
);
|
|
104
|
-
const
|
|
105
|
-
navigate({itemUrl}) {
|
|
106
|
-
// Algolia results could contain URL's from other domains which cannot
|
|
107
|
-
// be served through history and should navigate with window.location
|
|
108
|
-
if (isRegexpStringMatch(externalUrlRegex, itemUrl)) {
|
|
109
|
-
window.location.href = itemUrl;
|
|
110
|
-
} else {
|
|
111
|
-
history.push(itemUrl);
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
}).current;
|
|
115
|
-
const transformItems = useRef((items) =>
|
|
116
|
-
props.transformItems
|
|
117
|
-
? // Custom transformItems
|
|
118
|
-
props.transformItems(items)
|
|
119
|
-
: // Default transformItems
|
|
120
|
-
items.map((item) => ({
|
|
121
|
-
...item,
|
|
122
|
-
url: processSearchResultUrl(item.url),
|
|
123
|
-
})),
|
|
124
|
-
).current;
|
|
125
|
-
const resultsFooterComponent = useMemo(
|
|
126
|
-
() =>
|
|
127
|
-
// eslint-disable-next-line react/no-unstable-nested-components
|
|
128
|
-
(footerProps) =>
|
|
129
|
-
<ResultsFooter {...footerProps} onClose={closeModal} />,
|
|
130
|
-
[closeModal],
|
|
131
|
-
);
|
|
132
|
-
const transformSearchClient = useCallback(
|
|
133
|
-
(searchClient) => {
|
|
134
|
-
searchClient.addAlgoliaAgent(
|
|
135
|
-
'docusaurus',
|
|
136
|
-
siteMetadata.docusaurusVersion,
|
|
137
|
-
);
|
|
138
|
-
return searchClient;
|
|
139
|
-
},
|
|
140
|
-
[siteMetadata.docusaurusVersion],
|
|
141
|
-
);
|
|
161
|
+
const resultsFooterComponent = useResultsFooterComponent({closeModal});
|
|
142
162
|
useDocSearchKeyboardEvents({
|
|
143
163
|
isOpen,
|
|
144
164
|
onOpen: openModal,
|
|
@@ -165,7 +185,7 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
165
185
|
onMouseOver={importDocSearchModalIfNeeded}
|
|
166
186
|
onClick={openModal}
|
|
167
187
|
ref={searchButtonRef}
|
|
168
|
-
translations={translations.button}
|
|
188
|
+
translations={props.translations?.button ?? translations.button}
|
|
169
189
|
/>
|
|
170
190
|
|
|
171
191
|
{isOpen &&
|
|
@@ -183,10 +203,10 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
183
203
|
{...(props.searchPagePath && {
|
|
184
204
|
resultsFooterComponent,
|
|
185
205
|
})}
|
|
206
|
+
placeholder={translations.placeholder}
|
|
186
207
|
{...props}
|
|
208
|
+
translations={props.translations?.modal ?? translations.modal}
|
|
187
209
|
searchParameters={searchParameters}
|
|
188
|
-
placeholder={translations.placeholder}
|
|
189
|
-
translations={translations.modal}
|
|
190
210
|
/>,
|
|
191
211
|
searchContainer.current,
|
|
192
212
|
)}
|
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
import { type ReactNode } from 'react';
|
|
8
|
+
export default function SearchPage(): ReactNode;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import React, {useEffect, useReducer, useRef, useState} from 'react';
|
|
9
9
|
import clsx from 'clsx';
|
|
10
10
|
import algoliaSearchHelper from 'algoliasearch-helper';
|
|
11
|
-
import
|
|
11
|
+
import {liteClient} from 'algoliasearch/lite';
|
|
12
12
|
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
13
13
|
import Head from '@docusaurus/Head';
|
|
14
14
|
import Link from '@docusaurus/Link';
|
|
@@ -173,7 +173,7 @@ function SearchPageContent() {
|
|
|
173
173
|
const disjunctiveFacets = contextualSearch
|
|
174
174
|
? ['language', 'docusaurus_tag']
|
|
175
175
|
: [];
|
|
176
|
-
const algoliaClient =
|
|
176
|
+
const algoliaClient = liteClient(appId, apiKey);
|
|
177
177
|
const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
|
|
178
178
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
179
179
|
// @ts-ignore: why errors happens after upgrading to TS 5.5 ?
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/theme-search-algolia",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "Algolia search component for Docusaurus.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"sideEffects": [
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@docsearch/react": "^3.
|
|
37
|
-
"@docusaurus/core": "3.
|
|
38
|
-
"@docusaurus/logger": "3.
|
|
39
|
-
"@docusaurus/plugin-content-docs": "3.
|
|
40
|
-
"@docusaurus/theme-common": "3.
|
|
41
|
-
"@docusaurus/theme-translations": "3.
|
|
42
|
-
"@docusaurus/utils": "3.
|
|
43
|
-
"@docusaurus/utils-validation": "3.
|
|
44
|
-
"algoliasearch": "^
|
|
45
|
-
"algoliasearch-helper": "^3.
|
|
36
|
+
"@docsearch/react": "^3.8.1",
|
|
37
|
+
"@docusaurus/core": "3.7.0",
|
|
38
|
+
"@docusaurus/logger": "3.7.0",
|
|
39
|
+
"@docusaurus/plugin-content-docs": "3.7.0",
|
|
40
|
+
"@docusaurus/theme-common": "3.7.0",
|
|
41
|
+
"@docusaurus/theme-translations": "3.7.0",
|
|
42
|
+
"@docusaurus/utils": "3.7.0",
|
|
43
|
+
"@docusaurus/utils-validation": "3.7.0",
|
|
44
|
+
"algoliasearch": "^5.17.1",
|
|
45
|
+
"algoliasearch-helper": "^3.22.6",
|
|
46
46
|
"clsx": "^2.0.0",
|
|
47
47
|
"eta": "^2.2.0",
|
|
48
48
|
"fs-extra": "^11.1.1",
|
|
@@ -51,14 +51,14 @@
|
|
|
51
51
|
"utility-types": "^3.10.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@docusaurus/module-type-aliases": "3.
|
|
54
|
+
"@docusaurus/module-type-aliases": "3.7.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"react": "^18.0.0",
|
|
58
|
-
"react-dom": "^18.0.0"
|
|
57
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
58
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
61
61
|
"node": ">=18.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "dd59750c16fe6908a26f18806a54d4c3dbe6db43"
|
|
64
64
|
}
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React, {
|
|
8
|
+
import React, {
|
|
9
|
+
useCallback,
|
|
10
|
+
useMemo,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
type ReactNode,
|
|
14
|
+
} from 'react';
|
|
9
15
|
import {createPortal} from 'react-dom';
|
|
10
16
|
import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
|
11
17
|
import Head from '@docusaurus/Head';
|
|
@@ -22,17 +28,17 @@ import {
|
|
|
22
28
|
import Translate from '@docusaurus/Translate';
|
|
23
29
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
24
30
|
import translations from '@theme/SearchTranslations';
|
|
25
|
-
|
|
26
|
-
import type {AutocompleteState} from '@algolia/autocomplete-core';
|
|
27
31
|
import type {
|
|
32
|
+
InternalDocSearchHit,
|
|
28
33
|
DocSearchModal as DocSearchModalType,
|
|
29
34
|
DocSearchModalProps,
|
|
30
|
-
} from '@docsearch/react';
|
|
31
|
-
import type {
|
|
32
|
-
InternalDocSearchHit,
|
|
33
35
|
StoredDocSearchHit,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
DocSearchTransformClient,
|
|
37
|
+
DocSearchHit,
|
|
38
|
+
} from '@docsearch/react';
|
|
39
|
+
|
|
40
|
+
import type {AutocompleteState} from '@algolia/autocomplete-core';
|
|
41
|
+
import type {FacetFilters} from 'algoliasearch/lite';
|
|
36
42
|
|
|
37
43
|
type DocSearchProps = Omit<
|
|
38
44
|
DocSearchModalProps,
|
|
@@ -45,6 +51,85 @@ type DocSearchProps = Omit<
|
|
|
45
51
|
|
|
46
52
|
let DocSearchModal: typeof DocSearchModalType | null = null;
|
|
47
53
|
|
|
54
|
+
function importDocSearchModalIfNeeded() {
|
|
55
|
+
if (DocSearchModal) {
|
|
56
|
+
return Promise.resolve();
|
|
57
|
+
}
|
|
58
|
+
return Promise.all([
|
|
59
|
+
import('@docsearch/react/modal') as Promise<
|
|
60
|
+
typeof import('@docsearch/react')
|
|
61
|
+
>,
|
|
62
|
+
import('@docsearch/react/style'),
|
|
63
|
+
import('./styles.css'),
|
|
64
|
+
]).then(([{DocSearchModal: Modal}]) => {
|
|
65
|
+
DocSearchModal = Modal;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function useNavigator({
|
|
70
|
+
externalUrlRegex,
|
|
71
|
+
}: Pick<DocSearchProps, 'externalUrlRegex'>) {
|
|
72
|
+
const history = useHistory();
|
|
73
|
+
const [navigator] = useState<DocSearchModalProps['navigator']>(() => {
|
|
74
|
+
return {
|
|
75
|
+
navigate(params) {
|
|
76
|
+
// Algolia results could contain URL's from other domains which cannot
|
|
77
|
+
// be served through history and should navigate with window.location
|
|
78
|
+
if (isRegexpStringMatch(externalUrlRegex, params.itemUrl)) {
|
|
79
|
+
window.location.href = params.itemUrl;
|
|
80
|
+
} else {
|
|
81
|
+
history.push(params.itemUrl);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
return navigator;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function useTransformSearchClient(): DocSearchModalProps['transformSearchClient'] {
|
|
90
|
+
const {
|
|
91
|
+
siteMetadata: {docusaurusVersion},
|
|
92
|
+
} = useDocusaurusContext();
|
|
93
|
+
return useCallback(
|
|
94
|
+
(searchClient: DocSearchTransformClient) => {
|
|
95
|
+
searchClient.addAlgoliaAgent('docusaurus', docusaurusVersion);
|
|
96
|
+
return searchClient;
|
|
97
|
+
},
|
|
98
|
+
[docusaurusVersion],
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function useTransformItems(props: Pick<DocSearchProps, 'transformItems'>) {
|
|
103
|
+
const processSearchResultUrl = useSearchResultUrlProcessor();
|
|
104
|
+
const [transformItems] = useState<DocSearchModalProps['transformItems']>(
|
|
105
|
+
() => {
|
|
106
|
+
return (items: DocSearchHit[]) =>
|
|
107
|
+
props.transformItems
|
|
108
|
+
? // Custom transformItems
|
|
109
|
+
props.transformItems(items)
|
|
110
|
+
: // Default transformItems
|
|
111
|
+
items.map((item) => ({
|
|
112
|
+
...item,
|
|
113
|
+
url: processSearchResultUrl(item.url),
|
|
114
|
+
}));
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
return transformItems;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function useResultsFooterComponent({
|
|
121
|
+
closeModal,
|
|
122
|
+
}: {
|
|
123
|
+
closeModal: () => void;
|
|
124
|
+
}): DocSearchProps['resultsFooterComponent'] {
|
|
125
|
+
return useMemo(
|
|
126
|
+
() =>
|
|
127
|
+
({state}) =>
|
|
128
|
+
<ResultsFooter state={state} onClose={closeModal} />,
|
|
129
|
+
[closeModal],
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
48
133
|
function Hit({
|
|
49
134
|
hit,
|
|
50
135
|
children,
|
|
@@ -74,25 +159,15 @@ function ResultsFooter({state, onClose}: ResultsFooterProps) {
|
|
|
74
159
|
);
|
|
75
160
|
}
|
|
76
161
|
|
|
77
|
-
|
|
78
|
-
Required<DocSearchProps>['searchParameters']
|
|
79
|
-
>['facetFilters'];
|
|
80
|
-
|
|
81
|
-
function mergeFacetFilters(f1: FacetFilters, f2: FacetFilters): FacetFilters {
|
|
82
|
-
const normalize = (
|
|
83
|
-
f: FacetFilters,
|
|
84
|
-
): readonly string[] | readonly (string | readonly string[])[] =>
|
|
85
|
-
typeof f === 'string' ? [f] : f;
|
|
86
|
-
return [...normalize(f1), ...normalize(f2)] as FacetFilters;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function DocSearch({
|
|
162
|
+
function useSearchParameters({
|
|
90
163
|
contextualSearch,
|
|
91
|
-
externalUrlRegex,
|
|
92
164
|
...props
|
|
93
|
-
}: DocSearchProps) {
|
|
94
|
-
|
|
95
|
-
|
|
165
|
+
}: DocSearchProps): DocSearchProps['searchParameters'] {
|
|
166
|
+
function mergeFacetFilters(f1: FacetFilters, f2: FacetFilters): FacetFilters {
|
|
167
|
+
const normalize = (f: FacetFilters): FacetFilters =>
|
|
168
|
+
typeof f === 'string' ? [f] : f;
|
|
169
|
+
return [...normalize(f1), ...normalize(f2)];
|
|
170
|
+
}
|
|
96
171
|
|
|
97
172
|
const contextualSearchFacetFilters =
|
|
98
173
|
useAlgoliaContextualFacetFilters() as FacetFilters;
|
|
@@ -106,36 +181,27 @@ function DocSearch({
|
|
|
106
181
|
: // ... or use config facetFilters
|
|
107
182
|
configFacetFilters;
|
|
108
183
|
|
|
109
|
-
// We let
|
|
110
|
-
|
|
184
|
+
// We let users override default searchParameters if they want to
|
|
185
|
+
return {
|
|
111
186
|
...props.searchParameters,
|
|
112
187
|
facetFilters,
|
|
113
188
|
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function DocSearch({externalUrlRegex, ...props}: DocSearchProps) {
|
|
192
|
+
const navigator = useNavigator({externalUrlRegex});
|
|
193
|
+
const searchParameters = useSearchParameters({...props});
|
|
194
|
+
const transformItems = useTransformItems(props);
|
|
195
|
+
const transformSearchClient = useTransformSearchClient();
|
|
114
196
|
|
|
115
|
-
const history = useHistory();
|
|
116
197
|
const searchContainer = useRef<HTMLDivElement | null>(null);
|
|
117
|
-
|
|
198
|
+
// TODO remove "as any" after React 19 upgrade
|
|
199
|
+
const searchButtonRef = useRef<HTMLButtonElement>(null as any);
|
|
118
200
|
const [isOpen, setIsOpen] = useState(false);
|
|
119
201
|
const [initialQuery, setInitialQuery] = useState<string | undefined>(
|
|
120
202
|
undefined,
|
|
121
203
|
);
|
|
122
204
|
|
|
123
|
-
const importDocSearchModalIfNeeded = useCallback(() => {
|
|
124
|
-
if (DocSearchModal) {
|
|
125
|
-
return Promise.resolve();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return Promise.all([
|
|
129
|
-
import('@docsearch/react/modal') as Promise<
|
|
130
|
-
typeof import('@docsearch/react')
|
|
131
|
-
>,
|
|
132
|
-
import('@docsearch/react/style'),
|
|
133
|
-
import('./styles.css'),
|
|
134
|
-
]).then(([{DocSearchModal: Modal}]) => {
|
|
135
|
-
DocSearchModal = Modal;
|
|
136
|
-
});
|
|
137
|
-
}, []);
|
|
138
|
-
|
|
139
205
|
const prepareSearchContainer = useCallback(() => {
|
|
140
206
|
if (!searchContainer.current) {
|
|
141
207
|
const divElement = document.createElement('div');
|
|
@@ -147,11 +213,12 @@ function DocSearch({
|
|
|
147
213
|
const openModal = useCallback(() => {
|
|
148
214
|
prepareSearchContainer();
|
|
149
215
|
importDocSearchModalIfNeeded().then(() => setIsOpen(true));
|
|
150
|
-
}, [
|
|
216
|
+
}, [prepareSearchContainer]);
|
|
151
217
|
|
|
152
218
|
const closeModal = useCallback(() => {
|
|
153
219
|
setIsOpen(false);
|
|
154
220
|
searchButtonRef.current?.focus();
|
|
221
|
+
setInitialQuery(undefined);
|
|
155
222
|
}, []);
|
|
156
223
|
|
|
157
224
|
const handleInput = useCallback(
|
|
@@ -168,50 +235,7 @@ function DocSearch({
|
|
|
168
235
|
[openModal],
|
|
169
236
|
);
|
|
170
237
|
|
|
171
|
-
const
|
|
172
|
-
navigate({itemUrl}: {itemUrl?: string}) {
|
|
173
|
-
// Algolia results could contain URL's from other domains which cannot
|
|
174
|
-
// be served through history and should navigate with window.location
|
|
175
|
-
if (isRegexpStringMatch(externalUrlRegex, itemUrl)) {
|
|
176
|
-
window.location.href = itemUrl!;
|
|
177
|
-
} else {
|
|
178
|
-
history.push(itemUrl!);
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
}).current;
|
|
182
|
-
|
|
183
|
-
const transformItems = useRef<DocSearchModalProps['transformItems']>(
|
|
184
|
-
(items) =>
|
|
185
|
-
props.transformItems
|
|
186
|
-
? // Custom transformItems
|
|
187
|
-
props.transformItems(items)
|
|
188
|
-
: // Default transformItems
|
|
189
|
-
items.map((item) => ({
|
|
190
|
-
...item,
|
|
191
|
-
url: processSearchResultUrl(item.url),
|
|
192
|
-
})),
|
|
193
|
-
).current;
|
|
194
|
-
|
|
195
|
-
const resultsFooterComponent: DocSearchProps['resultsFooterComponent'] =
|
|
196
|
-
useMemo(
|
|
197
|
-
() =>
|
|
198
|
-
// eslint-disable-next-line react/no-unstable-nested-components
|
|
199
|
-
(footerProps: Omit<ResultsFooterProps, 'onClose'>): JSX.Element =>
|
|
200
|
-
<ResultsFooter {...footerProps} onClose={closeModal} />,
|
|
201
|
-
[closeModal],
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
const transformSearchClient = useCallback(
|
|
205
|
-
(searchClient: SearchClient) => {
|
|
206
|
-
searchClient.addAlgoliaAgent(
|
|
207
|
-
'docusaurus',
|
|
208
|
-
siteMetadata.docusaurusVersion,
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
return searchClient;
|
|
212
|
-
},
|
|
213
|
-
[siteMetadata.docusaurusVersion],
|
|
214
|
-
);
|
|
238
|
+
const resultsFooterComponent = useResultsFooterComponent({closeModal});
|
|
215
239
|
|
|
216
240
|
useDocSearchKeyboardEvents({
|
|
217
241
|
isOpen,
|
|
@@ -240,7 +264,7 @@ function DocSearch({
|
|
|
240
264
|
onMouseOver={importDocSearchModalIfNeeded}
|
|
241
265
|
onClick={openModal}
|
|
242
266
|
ref={searchButtonRef}
|
|
243
|
-
translations={translations.button}
|
|
267
|
+
translations={props.translations?.button ?? translations.button}
|
|
244
268
|
/>
|
|
245
269
|
|
|
246
270
|
{isOpen &&
|
|
@@ -258,10 +282,10 @@ function DocSearch({
|
|
|
258
282
|
{...(props.searchPagePath && {
|
|
259
283
|
resultsFooterComponent,
|
|
260
284
|
})}
|
|
285
|
+
placeholder={translations.placeholder}
|
|
261
286
|
{...props}
|
|
287
|
+
translations={props.translations?.modal ?? translations.modal}
|
|
262
288
|
searchParameters={searchParameters}
|
|
263
|
-
placeholder={translations.placeholder}
|
|
264
|
-
translations={translations.modal}
|
|
265
289
|
/>,
|
|
266
290
|
searchContainer.current,
|
|
267
291
|
)}
|
|
@@ -269,7 +293,7 @@ function DocSearch({
|
|
|
269
293
|
);
|
|
270
294
|
}
|
|
271
295
|
|
|
272
|
-
export default function SearchBar():
|
|
296
|
+
export default function SearchBar(): ReactNode {
|
|
273
297
|
const {siteConfig} = useDocusaurusContext();
|
|
274
298
|
return <DocSearch {...(siteConfig.themeConfig.algolia as DocSearchProps)} />;
|
|
275
299
|
}
|
|
@@ -7,11 +7,17 @@
|
|
|
7
7
|
|
|
8
8
|
/* eslint-disable jsx-a11y/no-autofocus */
|
|
9
9
|
|
|
10
|
-
import React, {
|
|
10
|
+
import React, {
|
|
11
|
+
type ReactNode,
|
|
12
|
+
useEffect,
|
|
13
|
+
useReducer,
|
|
14
|
+
useRef,
|
|
15
|
+
useState,
|
|
16
|
+
} from 'react';
|
|
11
17
|
import clsx from 'clsx';
|
|
12
18
|
|
|
13
19
|
import algoliaSearchHelper from 'algoliasearch-helper';
|
|
14
|
-
import
|
|
20
|
+
import {liteClient} from 'algoliasearch/lite';
|
|
15
21
|
|
|
16
22
|
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
17
23
|
import Head from '@docusaurus/Head';
|
|
@@ -154,7 +160,7 @@ type ResultDispatcher =
|
|
|
154
160
|
| {type: 'update'; value: ResultDispatcherState}
|
|
155
161
|
| {type: 'advance'; value?: undefined};
|
|
156
162
|
|
|
157
|
-
function SearchPageContent():
|
|
163
|
+
function SearchPageContent(): ReactNode {
|
|
158
164
|
const {
|
|
159
165
|
i18n: {currentLocale},
|
|
160
166
|
} = useDocusaurusContext();
|
|
@@ -219,7 +225,7 @@ function SearchPageContent(): JSX.Element {
|
|
|
219
225
|
? ['language', 'docusaurus_tag']
|
|
220
226
|
: [];
|
|
221
227
|
|
|
222
|
-
const algoliaClient =
|
|
228
|
+
const algoliaClient = liteClient(appId, apiKey);
|
|
223
229
|
const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
|
|
224
230
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
225
231
|
// @ts-ignore: why errors happens after upgrading to TS 5.5 ?
|
|
@@ -530,7 +536,7 @@ function SearchPageContent(): JSX.Element {
|
|
|
530
536
|
);
|
|
531
537
|
}
|
|
532
538
|
|
|
533
|
-
export default function SearchPage():
|
|
539
|
+
export default function SearchPage(): ReactNode {
|
|
534
540
|
return (
|
|
535
541
|
<HtmlClassNameProvider className="search-page-wrapper">
|
|
536
542
|
<SearchPageContent />
|
|
@@ -7,23 +7,36 @@
|
|
|
7
7
|
|
|
8
8
|
declare module '@docusaurus/theme-search-algolia' {
|
|
9
9
|
import type {DeepPartial} from 'utility-types';
|
|
10
|
+
import type {DocSearchProps} from '@docsearch/react';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
// DocSearch props that Docusaurus exposes directly through props forwarding
|
|
13
|
+
type DocusaurusDocSearchProps = Pick<
|
|
14
|
+
DocSearchProps,
|
|
15
|
+
| 'appId'
|
|
16
|
+
| 'apiKey'
|
|
17
|
+
| 'indexName'
|
|
18
|
+
| 'placeholder'
|
|
19
|
+
| 'translations'
|
|
20
|
+
| 'searchParameters'
|
|
21
|
+
| 'insights'
|
|
22
|
+
| 'initialQuery'
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
type ThemeConfigAlgolia = DocusaurusDocSearchProps & {
|
|
26
|
+
// Docusaurus custom options, not coming from DocSearch
|
|
27
|
+
contextualSearch: boolean;
|
|
28
|
+
externalUrlRegex?: string;
|
|
29
|
+
searchPagePath: string | false | null;
|
|
30
|
+
replaceSearchResultPathname?: {
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
25
33
|
};
|
|
26
34
|
};
|
|
35
|
+
|
|
36
|
+
export type ThemeConfig = DocusaurusDocSearchProps & {
|
|
37
|
+
algolia: ThemeConfigAlgolia;
|
|
38
|
+
};
|
|
39
|
+
|
|
27
40
|
export type UserThemeConfig = DeepPartial<ThemeConfig>;
|
|
28
41
|
}
|
|
29
42
|
|
|
@@ -38,11 +51,15 @@ declare module '@docusaurus/theme-search-algolia/client' {
|
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
declare module '@theme/SearchPage' {
|
|
41
|
-
|
|
54
|
+
import type {ReactNode} from 'react';
|
|
55
|
+
|
|
56
|
+
export default function SearchPage(): ReactNode;
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
declare module '@theme/SearchBar' {
|
|
45
|
-
|
|
60
|
+
import type {ReactNode} from 'react';
|
|
61
|
+
|
|
62
|
+
export default function SearchBar(): ReactNode;
|
|
46
63
|
}
|
|
47
64
|
|
|
48
65
|
declare module '@theme/SearchTranslations' {
|