@docusaurus/theme-search-algolia 2.0.0-beta.fc64c12e4 → 2.0.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.
Files changed (37) hide show
  1. package/lib/client/index.d.ts +7 -0
  2. package/lib/client/index.js +7 -0
  3. package/lib/client/useAlgoliaContextualFacetFilters.d.ts +7 -0
  4. package/lib/client/useAlgoliaContextualFacetFilters.js +15 -0
  5. package/lib/index.d.ts +9 -0
  6. package/lib/index.js +90 -0
  7. package/lib/templates/opensearch.d.ts +8 -0
  8. package/lib/templates/opensearch.js +23 -0
  9. package/lib/theme/SearchBar/index.d.ts +8 -0
  10. package/{src → lib}/theme/SearchBar/index.js +58 -70
  11. package/lib/theme/SearchBar/styles.css +21 -0
  12. package/lib/theme/SearchPage/index.d.ts +8 -0
  13. package/{src → lib}/theme/SearchPage/index.js +66 -93
  14. package/lib/theme/SearchPage/styles.module.css +119 -0
  15. package/lib/theme/SearchTranslations/index.d.ts +11 -0
  16. package/lib/theme/SearchTranslations/index.js +167 -0
  17. package/lib/validateThemeConfig.d.ts +15 -0
  18. package/lib/validateThemeConfig.js +44 -0
  19. package/package.json +43 -14
  20. package/src/client/index.ts +8 -0
  21. package/src/{theme/hooks/useAlgoliaContextualFacetFilters.js → client/useAlgoliaContextualFacetFilters.ts} +3 -3
  22. package/src/deps.d.ts +20 -0
  23. package/src/index.ts +116 -0
  24. package/src/templates/{opensearch.js → opensearch.ts} +7 -5
  25. package/src/theme/SearchBar/index.tsx +271 -0
  26. package/src/theme/SearchPage/index.tsx +535 -0
  27. package/src/theme/SearchPage/styles.module.css +4 -4
  28. package/src/theme/SearchTranslations/index.ts +172 -0
  29. package/src/theme-search-algolia.d.ts +42 -0
  30. package/src/types.d.ts +10 -0
  31. package/src/validateThemeConfig.ts +53 -0
  32. package/src/__tests__/validateThemeConfig.test.js +0 -121
  33. package/src/index.js +0 -92
  34. package/src/theme/SearchBar/styles.module.css +0 -20
  35. package/src/theme/SearchMetadatas/index.js +0 -25
  36. package/src/theme/hooks/useSearchQuery.js +0 -44
  37. 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.canUseIntersectionObserver &&
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 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: loadingspin 1s linear infinite;
101
+ animation: loading-spin 1s linear infinite;
102
102
  margin: 0 auto;
103
103
  }
104
104
 
105
- @keyframes loadingspin {
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: rgba(255, 215, 142, 0.25);
117
+ background: rgb(255 215 142 / 25%);
118
118
  padding: 0.09em 0;
119
119
  }