@docusaurus/theme-search-algolia 2.0.0-beta.ff31de0ff → 2.0.1

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 +51 -60
  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 +100 -131
  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/SearchBar/styles.css +1 -0
  27. package/src/theme/SearchPage/index.tsx +535 -0
  28. package/src/theme/SearchPage/styles.module.css +15 -34
  29. package/src/theme/SearchTranslations/index.ts +172 -0
  30. package/src/theme-search-algolia.d.ts +42 -0
  31. package/src/types.d.ts +10 -0
  32. package/src/validateThemeConfig.ts +53 -0
  33. package/src/__tests__/validateThemeConfig.test.js +0 -121
  34. package/src/index.js +0 -92
  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
+ }
@@ -8,23 +8,20 @@
8
8
  .searchQueryInput,
9
9
  .searchVersionInput {
10
10
  border-radius: var(--ifm-global-radius);
11
- border: 0;
12
- font-size: var(--ifm-font-size-base);
13
- font-family: var(--ifm-font-family-base);
11
+ border: 2px solid var(--ifm-toc-border-color);
12
+ font: var(--ifm-font-size-base) var(--ifm-font-family-base);
14
13
  padding: 0.8rem;
15
14
  width: 100%;
16
15
  background: var(--docsearch-searchbox-focus-background);
17
16
  color: var(--docsearch-text-color);
18
- margin-bottom: 1rem;
19
- box-shadow: inset 0 0 0 2px var(--ifm-toc-border-color);
20
- transition: var(--ifm-transition-fast) ease box-shadow;
17
+ margin-bottom: 0.5rem;
18
+ transition: border var(--ifm-transition-fast) ease;
21
19
  }
22
20
 
23
- .searchQueryInput:active,
24
21
  .searchQueryInput:focus,
25
- .searchVersionInput:active,
26
22
  .searchVersionInput:focus {
27
- box-shadow: var(--docsearch-searchbox-shadow);
23
+ border-color: var(--docsearch-primary-color);
24
+ outline: none;
28
25
  }
29
26
 
30
27
  .searchQueryInput::placeholder {
@@ -33,10 +30,7 @@
33
30
 
34
31
  .searchResultsColumn {
35
32
  font-size: 0.9rem;
36
- }
37
-
38
- .searchLogoColumn {
39
- text-align: right;
33
+ font-weight: bold;
40
34
  }
41
35
 
42
36
  .algoliaLogo {
@@ -53,24 +47,18 @@
53
47
  }
54
48
 
55
49
  .searchResultItemHeading {
56
- font-size: var(--ifm-h2-font-size);
50
+ font-weight: 400;
51
+ margin-bottom: 0;
57
52
  }
58
53
 
59
54
  .searchResultItemPath {
60
55
  font-size: 0.8rem;
61
56
  color: var(--ifm-color-content-secondary);
62
- display: block;
63
- }
64
-
65
- .searchResultItemPathSeparator {
66
- margin: 0 0.5rem;
67
- font-size: 1rem;
68
- font-weight: 600;
69
- color: var(--ifm-color-emphasis-400);
57
+ --ifm-breadcrumb-separator-size-multiplier: 1;
70
58
  }
71
59
 
72
60
  .searchResultItemSummary {
73
- margin: 0.5rem 0 0 0;
61
+ margin: 0.5rem 0 0;
74
62
  font-style: italic;
75
63
  }
76
64
 
@@ -83,16 +71,11 @@
83
71
  max-width: 40% !important;
84
72
  }
85
73
 
86
- .algoliaLogo {
87
- width: 100%;
88
- }
89
-
90
74
  .searchResultsColumn {
91
75
  max-width: 60% !important;
92
76
  }
93
77
 
94
78
  .searchLogoColumn {
95
- overflow: hidden;
96
79
  max-width: 40% !important;
97
80
  padding-left: 0 !important;
98
81
  }
@@ -110,18 +93,16 @@
110
93
  }
111
94
 
112
95
  .loadingSpinner {
113
- pointer-events: none;
114
96
  width: 3rem;
115
97
  height: 3rem;
116
- border: 0.4em solid transparent;
117
- border-color: #eee;
98
+ border: 0.4em solid #eee;
118
99
  border-top-color: var(--ifm-color-primary);
119
100
  border-radius: 50%;
120
- animation: loadingspin 1s linear infinite;
101
+ animation: loading-spin 1s linear infinite;
121
102
  margin: 0 auto;
122
103
  }
123
104
 
124
- @keyframes loadingspin {
105
+ @keyframes loading-spin {
125
106
  100% {
126
107
  transform: rotate(360deg);
127
108
  }
@@ -133,6 +114,6 @@
133
114
 
134
115
  :global(.search-result-match) {
135
116
  color: var(--docsearch-hit-color);
136
- background: rgba(255, 215, 142, 0.25);
117
+ background: rgb(255 215 142 / 25%);
137
118
  padding: 0.09em 0;
138
119
  }