@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
|
@@ -0,0 +1,535 @@
|
|
|
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
|
+
/* eslint-disable jsx-a11y/no-autofocus */
|
|
9
|
+
|
|
10
|
+
import React, {useEffect, useState, useReducer, useRef} from 'react';
|
|
11
|
+
import clsx from 'clsx';
|
|
12
|
+
|
|
13
|
+
import algoliaSearch from 'algoliasearch/lite';
|
|
14
|
+
import algoliaSearchHelper from 'algoliasearch-helper';
|
|
15
|
+
|
|
16
|
+
import Head from '@docusaurus/Head';
|
|
17
|
+
import Link from '@docusaurus/Link';
|
|
18
|
+
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
19
|
+
import {
|
|
20
|
+
HtmlClassNameProvider,
|
|
21
|
+
usePluralForm,
|
|
22
|
+
isRegexpStringMatch,
|
|
23
|
+
useEvent,
|
|
24
|
+
} from '@docusaurus/theme-common';
|
|
25
|
+
import {
|
|
26
|
+
useTitleFormatter,
|
|
27
|
+
useSearchPage,
|
|
28
|
+
} from '@docusaurus/theme-common/internal';
|
|
29
|
+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
30
|
+
import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
|
|
31
|
+
import Translate, {translate} from '@docusaurus/Translate';
|
|
32
|
+
import Layout from '@theme/Layout';
|
|
33
|
+
|
|
34
|
+
import styles from './styles.module.css';
|
|
35
|
+
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
|
36
|
+
|
|
37
|
+
// Very simple pluralization: probably good enough for now
|
|
38
|
+
function useDocumentsFoundPlural() {
|
|
39
|
+
const {selectMessage} = usePluralForm();
|
|
40
|
+
return (count: number) =>
|
|
41
|
+
selectMessage(
|
|
42
|
+
count,
|
|
43
|
+
translate(
|
|
44
|
+
{
|
|
45
|
+
id: 'theme.SearchPage.documentsFound.plurals',
|
|
46
|
+
description:
|
|
47
|
+
'Pluralized label for "{count} documents found". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',
|
|
48
|
+
message: 'One document found|{count} documents found',
|
|
49
|
+
},
|
|
50
|
+
{count},
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function useDocsSearchVersionsHelpers() {
|
|
56
|
+
const allDocsData = useAllDocsData();
|
|
57
|
+
|
|
58
|
+
// State of the version select menus / algolia facet filters
|
|
59
|
+
// docsPluginId -> versionName map
|
|
60
|
+
const [searchVersions, setSearchVersions] = useState<{
|
|
61
|
+
[pluginId: string]: string;
|
|
62
|
+
}>(() =>
|
|
63
|
+
Object.entries(allDocsData).reduce(
|
|
64
|
+
(acc, [pluginId, pluginData]) => ({
|
|
65
|
+
...acc,
|
|
66
|
+
[pluginId]: pluginData.versions[0]!.name,
|
|
67
|
+
}),
|
|
68
|
+
{},
|
|
69
|
+
),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Set the value of a single select menu
|
|
73
|
+
const setSearchVersion = (pluginId: string, searchVersion: string) =>
|
|
74
|
+
setSearchVersions((s) => ({...s, [pluginId]: searchVersion}));
|
|
75
|
+
|
|
76
|
+
const versioningEnabled = Object.values(allDocsData).some(
|
|
77
|
+
(docsData) => docsData.versions.length > 1,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
allDocsData,
|
|
82
|
+
versioningEnabled,
|
|
83
|
+
searchVersions,
|
|
84
|
+
setSearchVersion,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// We want to display one select per versioned docs plugin instance
|
|
89
|
+
function SearchVersionSelectList({
|
|
90
|
+
docsSearchVersionsHelpers,
|
|
91
|
+
}: {
|
|
92
|
+
docsSearchVersionsHelpers: ReturnType<typeof useDocsSearchVersionsHelpers>;
|
|
93
|
+
}) {
|
|
94
|
+
const versionedPluginEntries = Object.entries(
|
|
95
|
+
docsSearchVersionsHelpers.allDocsData,
|
|
96
|
+
)
|
|
97
|
+
// Do not show a version select for unversioned docs plugin instances
|
|
98
|
+
.filter(([, docsData]) => docsData.versions.length > 1);
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<div
|
|
102
|
+
className={clsx(
|
|
103
|
+
'col',
|
|
104
|
+
'col--3',
|
|
105
|
+
'padding-left--none',
|
|
106
|
+
styles.searchVersionColumn,
|
|
107
|
+
)}>
|
|
108
|
+
{versionedPluginEntries.map(([pluginId, docsData]) => {
|
|
109
|
+
const labelPrefix =
|
|
110
|
+
versionedPluginEntries.length > 1 ? `${pluginId}: ` : '';
|
|
111
|
+
return (
|
|
112
|
+
<select
|
|
113
|
+
key={pluginId}
|
|
114
|
+
onChange={(e) =>
|
|
115
|
+
docsSearchVersionsHelpers.setSearchVersion(
|
|
116
|
+
pluginId,
|
|
117
|
+
e.target.value,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
defaultValue={docsSearchVersionsHelpers.searchVersions[pluginId]}
|
|
121
|
+
className={styles.searchVersionInput}>
|
|
122
|
+
{docsData.versions.map((version, i) => (
|
|
123
|
+
<option
|
|
124
|
+
key={i}
|
|
125
|
+
label={`${labelPrefix}${version.label}`}
|
|
126
|
+
value={version.name}
|
|
127
|
+
/>
|
|
128
|
+
))}
|
|
129
|
+
</select>
|
|
130
|
+
);
|
|
131
|
+
})}
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
type ResultDispatcherState = {
|
|
137
|
+
items: {
|
|
138
|
+
title: string;
|
|
139
|
+
url: string;
|
|
140
|
+
summary: string;
|
|
141
|
+
breadcrumbs: string[];
|
|
142
|
+
}[];
|
|
143
|
+
query: string | null;
|
|
144
|
+
totalResults: number | null;
|
|
145
|
+
totalPages: number | null;
|
|
146
|
+
lastPage: number | null;
|
|
147
|
+
hasMore: boolean | null;
|
|
148
|
+
loading: boolean | null;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
type ResultDispatcher =
|
|
152
|
+
| {type: 'reset'; value?: undefined}
|
|
153
|
+
| {type: 'loading'; value?: undefined}
|
|
154
|
+
| {type: 'update'; value: ResultDispatcherState}
|
|
155
|
+
| {type: 'advance'; value?: undefined};
|
|
156
|
+
|
|
157
|
+
function SearchPageContent(): JSX.Element {
|
|
158
|
+
const {
|
|
159
|
+
siteConfig: {themeConfig},
|
|
160
|
+
i18n: {currentLocale},
|
|
161
|
+
} = useDocusaurusContext();
|
|
162
|
+
const {
|
|
163
|
+
algolia: {appId, apiKey, indexName, externalUrlRegex},
|
|
164
|
+
} = themeConfig as ThemeConfig;
|
|
165
|
+
const documentsFoundPlural = useDocumentsFoundPlural();
|
|
166
|
+
|
|
167
|
+
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
|
168
|
+
const {searchQuery, setSearchQuery} = useSearchPage();
|
|
169
|
+
const initialSearchResultState: ResultDispatcherState = {
|
|
170
|
+
items: [],
|
|
171
|
+
query: null,
|
|
172
|
+
totalResults: null,
|
|
173
|
+
totalPages: null,
|
|
174
|
+
lastPage: null,
|
|
175
|
+
hasMore: null,
|
|
176
|
+
loading: null,
|
|
177
|
+
};
|
|
178
|
+
const [searchResultState, searchResultStateDispatcher] = useReducer(
|
|
179
|
+
(prevState: ResultDispatcherState, data: ResultDispatcher) => {
|
|
180
|
+
switch (data.type) {
|
|
181
|
+
case 'reset': {
|
|
182
|
+
return initialSearchResultState;
|
|
183
|
+
}
|
|
184
|
+
case 'loading': {
|
|
185
|
+
return {...prevState, loading: true};
|
|
186
|
+
}
|
|
187
|
+
case 'update': {
|
|
188
|
+
if (searchQuery !== data.value.query) {
|
|
189
|
+
return prevState;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
...data.value,
|
|
194
|
+
items:
|
|
195
|
+
data.value.lastPage === 0
|
|
196
|
+
? data.value.items
|
|
197
|
+
: prevState.items.concat(data.value.items),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
case 'advance': {
|
|
201
|
+
const hasMore = prevState.totalPages! > prevState.lastPage! + 1;
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
...prevState,
|
|
205
|
+
lastPage: hasMore ? prevState.lastPage! + 1 : prevState.lastPage,
|
|
206
|
+
hasMore,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
default:
|
|
210
|
+
return prevState;
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
initialSearchResultState,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const algoliaClient = algoliaSearch(appId, apiKey);
|
|
217
|
+
const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
|
|
218
|
+
hitsPerPage: 15,
|
|
219
|
+
advancedSyntax: true,
|
|
220
|
+
disjunctiveFacets: ['language', 'docusaurus_tag'],
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
algoliaHelper.on(
|
|
224
|
+
'result',
|
|
225
|
+
({results: {query, hits, page, nbHits, nbPages}}) => {
|
|
226
|
+
if (query === '' || !Array.isArray(hits)) {
|
|
227
|
+
searchResultStateDispatcher({type: 'reset'});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const sanitizeValue = (value: string) =>
|
|
232
|
+
value.replace(
|
|
233
|
+
/algolia-docsearch-suggestion--highlight/g,
|
|
234
|
+
'search-result-match',
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const items = hits.map(
|
|
238
|
+
({
|
|
239
|
+
url,
|
|
240
|
+
_highlightResult: {hierarchy},
|
|
241
|
+
_snippetResult: snippet = {},
|
|
242
|
+
}: {
|
|
243
|
+
url: string;
|
|
244
|
+
_highlightResult: {hierarchy: {[key: string]: {value: string}}};
|
|
245
|
+
_snippetResult: {content?: {value: string}};
|
|
246
|
+
}) => {
|
|
247
|
+
const parsedURL = new URL(url);
|
|
248
|
+
const titles = Object.keys(hierarchy).map((key) =>
|
|
249
|
+
sanitizeValue(hierarchy[key]!.value),
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
title: titles.pop()!,
|
|
254
|
+
url: isRegexpStringMatch(externalUrlRegex, parsedURL.href)
|
|
255
|
+
? parsedURL.href
|
|
256
|
+
: parsedURL.pathname + parsedURL.hash,
|
|
257
|
+
summary: snippet.content
|
|
258
|
+
? `${sanitizeValue(snippet.content.value)}...`
|
|
259
|
+
: '',
|
|
260
|
+
breadcrumbs: titles,
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
searchResultStateDispatcher({
|
|
266
|
+
type: 'update',
|
|
267
|
+
value: {
|
|
268
|
+
items,
|
|
269
|
+
query,
|
|
270
|
+
totalResults: nbHits,
|
|
271
|
+
totalPages: nbPages,
|
|
272
|
+
lastPage: page,
|
|
273
|
+
hasMore: nbPages > page + 1,
|
|
274
|
+
loading: false,
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
},
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
const [loaderRef, setLoaderRef] = useState<HTMLDivElement | null>(null);
|
|
281
|
+
const prevY = useRef(0);
|
|
282
|
+
const observer = useRef(
|
|
283
|
+
ExecutionEnvironment.canUseDOM &&
|
|
284
|
+
new IntersectionObserver(
|
|
285
|
+
(entries) => {
|
|
286
|
+
const {
|
|
287
|
+
isIntersecting,
|
|
288
|
+
boundingClientRect: {y: currentY},
|
|
289
|
+
} = entries[0]!;
|
|
290
|
+
|
|
291
|
+
if (isIntersecting && prevY.current > currentY) {
|
|
292
|
+
searchResultStateDispatcher({type: 'advance'});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
prevY.current = currentY;
|
|
296
|
+
},
|
|
297
|
+
{threshold: 1},
|
|
298
|
+
),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const getTitle = () =>
|
|
302
|
+
searchQuery
|
|
303
|
+
? translate(
|
|
304
|
+
{
|
|
305
|
+
id: 'theme.SearchPage.existingResultsTitle',
|
|
306
|
+
message: 'Search results for "{query}"',
|
|
307
|
+
description: 'The search page title for non-empty query',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
query: searchQuery,
|
|
311
|
+
},
|
|
312
|
+
)
|
|
313
|
+
: translate({
|
|
314
|
+
id: 'theme.SearchPage.emptyResultsTitle',
|
|
315
|
+
message: 'Search the documentation',
|
|
316
|
+
description: 'The search page title for empty query',
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const makeSearch = useEvent((page: number = 0) => {
|
|
320
|
+
algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
|
|
321
|
+
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
|
322
|
+
|
|
323
|
+
Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
|
|
324
|
+
([pluginId, searchVersion]) => {
|
|
325
|
+
algoliaHelper.addDisjunctiveFacetRefinement(
|
|
326
|
+
'docusaurus_tag',
|
|
327
|
+
`docs-${pluginId}-${searchVersion}`,
|
|
328
|
+
);
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
if (!loaderRef) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
const currentObserver = observer.current;
|
|
340
|
+
if (currentObserver) {
|
|
341
|
+
currentObserver.observe(loaderRef);
|
|
342
|
+
return () => currentObserver.unobserve(loaderRef);
|
|
343
|
+
}
|
|
344
|
+
return () => true;
|
|
345
|
+
}, [loaderRef]);
|
|
346
|
+
|
|
347
|
+
useEffect(() => {
|
|
348
|
+
searchResultStateDispatcher({type: 'reset'});
|
|
349
|
+
|
|
350
|
+
if (searchQuery) {
|
|
351
|
+
searchResultStateDispatcher({type: 'loading'});
|
|
352
|
+
|
|
353
|
+
setTimeout(() => {
|
|
354
|
+
makeSearch();
|
|
355
|
+
}, 300);
|
|
356
|
+
}
|
|
357
|
+
}, [searchQuery, docsSearchVersionsHelpers.searchVersions, makeSearch]);
|
|
358
|
+
|
|
359
|
+
useEffect(() => {
|
|
360
|
+
if (!searchResultState.lastPage || searchResultState.lastPage === 0) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
makeSearch(searchResultState.lastPage);
|
|
365
|
+
}, [makeSearch, searchResultState.lastPage]);
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<Layout>
|
|
369
|
+
<Head>
|
|
370
|
+
<title>{useTitleFormatter(getTitle())}</title>
|
|
371
|
+
{/*
|
|
372
|
+
We should not index search pages
|
|
373
|
+
See https://github.com/facebook/docusaurus/pull/3233
|
|
374
|
+
*/}
|
|
375
|
+
<meta property="robots" content="noindex, follow" />
|
|
376
|
+
</Head>
|
|
377
|
+
|
|
378
|
+
<div className="container margin-vert--lg">
|
|
379
|
+
<h1>{getTitle()}</h1>
|
|
380
|
+
|
|
381
|
+
<form className="row" onSubmit={(e) => e.preventDefault()}>
|
|
382
|
+
<div
|
|
383
|
+
className={clsx('col', styles.searchQueryColumn, {
|
|
384
|
+
'col--9': docsSearchVersionsHelpers.versioningEnabled,
|
|
385
|
+
'col--12': !docsSearchVersionsHelpers.versioningEnabled,
|
|
386
|
+
})}>
|
|
387
|
+
<input
|
|
388
|
+
type="search"
|
|
389
|
+
name="q"
|
|
390
|
+
className={styles.searchQueryInput}
|
|
391
|
+
placeholder={translate({
|
|
392
|
+
id: 'theme.SearchPage.inputPlaceholder',
|
|
393
|
+
message: 'Type your search here',
|
|
394
|
+
description: 'The placeholder for search page input',
|
|
395
|
+
})}
|
|
396
|
+
aria-label={translate({
|
|
397
|
+
id: 'theme.SearchPage.inputLabel',
|
|
398
|
+
message: 'Search',
|
|
399
|
+
description: 'The ARIA label for search page input',
|
|
400
|
+
})}
|
|
401
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
402
|
+
value={searchQuery}
|
|
403
|
+
autoComplete="off"
|
|
404
|
+
autoFocus
|
|
405
|
+
/>
|
|
406
|
+
</div>
|
|
407
|
+
|
|
408
|
+
{docsSearchVersionsHelpers.versioningEnabled && (
|
|
409
|
+
<SearchVersionSelectList
|
|
410
|
+
docsSearchVersionsHelpers={docsSearchVersionsHelpers}
|
|
411
|
+
/>
|
|
412
|
+
)}
|
|
413
|
+
</form>
|
|
414
|
+
|
|
415
|
+
<div className="row">
|
|
416
|
+
<div className={clsx('col', 'col--8', styles.searchResultsColumn)}>
|
|
417
|
+
{!!searchResultState.totalResults &&
|
|
418
|
+
documentsFoundPlural(searchResultState.totalResults)}
|
|
419
|
+
</div>
|
|
420
|
+
|
|
421
|
+
<div
|
|
422
|
+
className={clsx(
|
|
423
|
+
'col',
|
|
424
|
+
'col--4',
|
|
425
|
+
'text--right',
|
|
426
|
+
styles.searchLogoColumn,
|
|
427
|
+
)}>
|
|
428
|
+
<a
|
|
429
|
+
target="_blank"
|
|
430
|
+
rel="noopener noreferrer"
|
|
431
|
+
href="https://www.algolia.com/"
|
|
432
|
+
aria-label={translate({
|
|
433
|
+
id: 'theme.SearchPage.algoliaLabel',
|
|
434
|
+
message: 'Search by Algolia',
|
|
435
|
+
description: 'The ARIA label for Algolia mention',
|
|
436
|
+
})}>
|
|
437
|
+
<svg viewBox="0 0 168 24" className={styles.algoliaLogo}>
|
|
438
|
+
<g fill="none">
|
|
439
|
+
<path
|
|
440
|
+
className={styles.algoliaLogoPathFill}
|
|
441
|
+
d="M120.925 18.804c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 0 1 .233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17zM6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z"
|
|
442
|
+
/>
|
|
443
|
+
<path
|
|
444
|
+
fill="#5468FF"
|
|
445
|
+
d="M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938z"
|
|
446
|
+
/>
|
|
447
|
+
<path
|
|
448
|
+
fill="white"
|
|
449
|
+
d="M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36"
|
|
450
|
+
/>
|
|
451
|
+
</g>
|
|
452
|
+
</svg>
|
|
453
|
+
</a>
|
|
454
|
+
</div>
|
|
455
|
+
</div>
|
|
456
|
+
|
|
457
|
+
{searchResultState.items.length > 0 ? (
|
|
458
|
+
<main>
|
|
459
|
+
{searchResultState.items.map(
|
|
460
|
+
({title, url, summary, breadcrumbs}, i) => (
|
|
461
|
+
<article key={i} className={styles.searchResultItem}>
|
|
462
|
+
<h2 className={styles.searchResultItemHeading}>
|
|
463
|
+
<Link to={url} dangerouslySetInnerHTML={{__html: title}} />
|
|
464
|
+
</h2>
|
|
465
|
+
|
|
466
|
+
{breadcrumbs.length > 0 && (
|
|
467
|
+
<nav aria-label="breadcrumbs">
|
|
468
|
+
<ul
|
|
469
|
+
className={clsx(
|
|
470
|
+
'breadcrumbs',
|
|
471
|
+
styles.searchResultItemPath,
|
|
472
|
+
)}>
|
|
473
|
+
{breadcrumbs.map((html, index) => (
|
|
474
|
+
<li
|
|
475
|
+
key={index}
|
|
476
|
+
className="breadcrumbs__item"
|
|
477
|
+
// Developer provided the HTML, so assume it's safe.
|
|
478
|
+
// eslint-disable-next-line react/no-danger
|
|
479
|
+
dangerouslySetInnerHTML={{__html: html}}
|
|
480
|
+
/>
|
|
481
|
+
))}
|
|
482
|
+
</ul>
|
|
483
|
+
</nav>
|
|
484
|
+
)}
|
|
485
|
+
|
|
486
|
+
{summary && (
|
|
487
|
+
<p
|
|
488
|
+
className={styles.searchResultItemSummary}
|
|
489
|
+
// Developer provided the HTML, so assume it's safe.
|
|
490
|
+
// eslint-disable-next-line react/no-danger
|
|
491
|
+
dangerouslySetInnerHTML={{__html: summary}}
|
|
492
|
+
/>
|
|
493
|
+
)}
|
|
494
|
+
</article>
|
|
495
|
+
),
|
|
496
|
+
)}
|
|
497
|
+
</main>
|
|
498
|
+
) : (
|
|
499
|
+
[
|
|
500
|
+
searchQuery && !searchResultState.loading && (
|
|
501
|
+
<p key="no-results">
|
|
502
|
+
<Translate
|
|
503
|
+
id="theme.SearchPage.noResultsText"
|
|
504
|
+
description="The paragraph for empty search result">
|
|
505
|
+
No results were found
|
|
506
|
+
</Translate>
|
|
507
|
+
</p>
|
|
508
|
+
),
|
|
509
|
+
!!searchResultState.loading && (
|
|
510
|
+
<div key="spinner" className={styles.loadingSpinner} />
|
|
511
|
+
),
|
|
512
|
+
]
|
|
513
|
+
)}
|
|
514
|
+
|
|
515
|
+
{searchResultState.hasMore && (
|
|
516
|
+
<div className={styles.loader} ref={setLoaderRef}>
|
|
517
|
+
<Translate
|
|
518
|
+
id="theme.SearchPage.fetchingNewResults"
|
|
519
|
+
description="The paragraph for fetching new search results">
|
|
520
|
+
Fetching new results...
|
|
521
|
+
</Translate>
|
|
522
|
+
</div>
|
|
523
|
+
)}
|
|
524
|
+
</div>
|
|
525
|
+
</Layout>
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
export default function SearchPage(): JSX.Element {
|
|
530
|
+
return (
|
|
531
|
+
<HtmlClassNameProvider className="search-page-wrapper">
|
|
532
|
+
<SearchPageContent />
|
|
533
|
+
</HtmlClassNameProvider>
|
|
534
|
+
);
|
|
535
|
+
}
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
.searchResultItemSummary {
|
|
61
|
-
margin: 0.5rem 0 0
|
|
61
|
+
margin: 0.5rem 0 0;
|
|
62
62
|
font-style: italic;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -98,11 +98,11 @@
|
|
|
98
98
|
border: 0.4em solid #eee;
|
|
99
99
|
border-top-color: var(--ifm-color-primary);
|
|
100
100
|
border-radius: 50%;
|
|
101
|
-
animation:
|
|
101
|
+
animation: loading-spin 1s linear infinite;
|
|
102
102
|
margin: 0 auto;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
@keyframes
|
|
105
|
+
@keyframes loading-spin {
|
|
106
106
|
100% {
|
|
107
107
|
transform: rotate(360deg);
|
|
108
108
|
}
|
|
@@ -114,6 +114,6 @@
|
|
|
114
114
|
|
|
115
115
|
:global(.search-result-match) {
|
|
116
116
|
color: var(--docsearch-hit-color);
|
|
117
|
-
background:
|
|
117
|
+
background: rgb(255 215 142 / 25%);
|
|
118
118
|
padding: 0.09em 0;
|
|
119
119
|
}
|