@docusaurus/theme-search-algolia 2.0.0-beta.2 → 2.0.0-beta.20

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 (34) 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 +107 -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 +57 -65
  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 +61 -90
  14. package/lib/theme/SearchPage/styles.module.css +119 -0
  15. package/lib/validateThemeConfig.d.ts +15 -0
  16. package/lib/validateThemeConfig.js +50 -0
  17. package/package.json +37 -13
  18. package/src/client/index.ts +8 -0
  19. package/src/{theme/hooks/useAlgoliaContextualFacetFilters.js → client/useAlgoliaContextualFacetFilters.ts} +3 -3
  20. package/src/deps.d.ts +10 -0
  21. package/src/index.ts +116 -0
  22. package/src/templates/{opensearch.js → opensearch.ts} +7 -5
  23. package/src/theme/SearchBar/index.tsx +274 -0
  24. package/src/theme/SearchPage/index.tsx +528 -0
  25. package/src/theme/SearchPage/styles.module.css +4 -4
  26. package/src/theme-search-algolia.d.ts +35 -0
  27. package/src/types.d.ts +10 -0
  28. package/src/validateThemeConfig.ts +53 -0
  29. package/src/__tests__/validateThemeConfig.test.js +0 -121
  30. package/src/index.js +0 -92
  31. package/src/theme/SearchBar/styles.module.css +0 -20
  32. package/src/theme/SearchMetadatas/index.js +0 -25
  33. package/src/theme/hooks/useSearchQuery.js +0 -44
  34. package/src/validateThemeConfig.js +0 -45
@@ -0,0 +1,528 @@
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
+
12
+ import algoliaSearch from 'algoliasearch/lite';
13
+ import algoliaSearchHelper from 'algoliasearch-helper';
14
+ import clsx from 'clsx';
15
+
16
+ import Head from '@docusaurus/Head';
17
+ import Link from '@docusaurus/Link';
18
+ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
19
+ import {
20
+ HtmlClassNameProvider,
21
+ useTitleFormatter,
22
+ usePluralForm,
23
+ isRegexpStringMatch,
24
+ useDynamicCallback,
25
+ useSearchPage,
26
+ } from '@docusaurus/theme-common';
27
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
28
+ import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
29
+ import Layout from '@theme/Layout';
30
+ import Translate, {translate} from '@docusaurus/Translate';
31
+ import styles from './styles.module.css';
32
+ import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
33
+
34
+ // Very simple pluralization: probably good enough for now
35
+ function useDocumentsFoundPlural() {
36
+ const {selectMessage} = usePluralForm();
37
+ return (count: number) =>
38
+ selectMessage(
39
+ count,
40
+ translate(
41
+ {
42
+ id: 'theme.SearchPage.documentsFound.plurals',
43
+ description:
44
+ '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)',
45
+ message: 'One document found|{count} documents found',
46
+ },
47
+ {count},
48
+ ),
49
+ );
50
+ }
51
+
52
+ function useDocsSearchVersionsHelpers() {
53
+ const allDocsData = useAllDocsData();
54
+
55
+ // State of the version select menus / algolia facet filters
56
+ // docsPluginId -> versionName map
57
+ const [searchVersions, setSearchVersions] = useState<{
58
+ [pluginId: string]: string;
59
+ }>(() =>
60
+ Object.entries(allDocsData).reduce(
61
+ (acc, [pluginId, pluginData]) => ({
62
+ ...acc,
63
+ [pluginId]: pluginData.versions[0]!.name,
64
+ }),
65
+ {},
66
+ ),
67
+ );
68
+
69
+ // Set the value of a single select menu
70
+ const setSearchVersion = (pluginId: string, searchVersion: string) =>
71
+ setSearchVersions((s) => ({...s, [pluginId]: searchVersion}));
72
+
73
+ const versioningEnabled = Object.values(allDocsData).some(
74
+ (docsData) => docsData.versions.length > 1,
75
+ );
76
+
77
+ return {
78
+ allDocsData,
79
+ versioningEnabled,
80
+ searchVersions,
81
+ setSearchVersion,
82
+ };
83
+ }
84
+
85
+ // We want to display one select per versioned docs plugin instance
86
+ function SearchVersionSelectList({
87
+ docsSearchVersionsHelpers,
88
+ }: {
89
+ docsSearchVersionsHelpers: ReturnType<typeof useDocsSearchVersionsHelpers>;
90
+ }) {
91
+ const versionedPluginEntries = Object.entries(
92
+ docsSearchVersionsHelpers.allDocsData,
93
+ )
94
+ // Do not show a version select for unversioned docs plugin instances
95
+ .filter(([, docsData]) => docsData.versions.length > 1);
96
+
97
+ return (
98
+ <div
99
+ className={clsx(
100
+ 'col',
101
+ 'col--3',
102
+ 'padding-left--none',
103
+ styles.searchVersionColumn,
104
+ )}>
105
+ {versionedPluginEntries.map(([pluginId, docsData]) => {
106
+ const labelPrefix =
107
+ versionedPluginEntries.length > 1 ? `${pluginId}: ` : '';
108
+ return (
109
+ <select
110
+ key={pluginId}
111
+ onChange={(e) =>
112
+ docsSearchVersionsHelpers.setSearchVersion(
113
+ pluginId,
114
+ e.target.value,
115
+ )
116
+ }
117
+ defaultValue={docsSearchVersionsHelpers.searchVersions[pluginId]}
118
+ className={styles.searchVersionInput}>
119
+ {docsData.versions.map((version, i) => (
120
+ <option
121
+ key={i}
122
+ label={`${labelPrefix}${version.label}`}
123
+ value={version.name}
124
+ />
125
+ ))}
126
+ </select>
127
+ );
128
+ })}
129
+ </div>
130
+ );
131
+ }
132
+
133
+ type ResultDispatcherState = {
134
+ items: {
135
+ title: string;
136
+ url: string;
137
+ summary: string;
138
+ breadcrumbs: string[];
139
+ }[];
140
+ query: string | null;
141
+ totalResults: number | null;
142
+ totalPages: number | null;
143
+ lastPage: number | null;
144
+ hasMore: boolean | null;
145
+ loading: boolean | null;
146
+ };
147
+
148
+ type ResultDispatcher =
149
+ | {type: 'reset'; value?: undefined}
150
+ | {type: 'loading'; value?: undefined}
151
+ | {type: 'update'; value: ResultDispatcherState}
152
+ | {type: 'advance'; value?: undefined};
153
+
154
+ function SearchPageContent(): JSX.Element {
155
+ const {
156
+ siteConfig: {themeConfig},
157
+ i18n: {currentLocale},
158
+ } = useDocusaurusContext();
159
+ const {
160
+ algolia: {appId, apiKey, indexName, externalUrlRegex},
161
+ } = themeConfig as ThemeConfig;
162
+ const documentsFoundPlural = useDocumentsFoundPlural();
163
+
164
+ const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
165
+ const {searchQuery, setSearchQuery} = useSearchPage();
166
+ const initialSearchResultState: ResultDispatcherState = {
167
+ items: [],
168
+ query: null,
169
+ totalResults: null,
170
+ totalPages: null,
171
+ lastPage: null,
172
+ hasMore: null,
173
+ loading: null,
174
+ };
175
+ const [searchResultState, searchResultStateDispatcher] = useReducer(
176
+ (prevState: ResultDispatcherState, data: ResultDispatcher) => {
177
+ switch (data.type) {
178
+ case 'reset': {
179
+ return initialSearchResultState;
180
+ }
181
+ case 'loading': {
182
+ return {...prevState, loading: true};
183
+ }
184
+ case 'update': {
185
+ if (searchQuery !== data.value.query) {
186
+ return prevState;
187
+ }
188
+
189
+ return {
190
+ ...data.value,
191
+ items:
192
+ data.value.lastPage === 0
193
+ ? data.value.items
194
+ : prevState.items.concat(data.value.items),
195
+ };
196
+ }
197
+ case 'advance': {
198
+ const hasMore = prevState.totalPages! > prevState.lastPage! + 1;
199
+
200
+ return {
201
+ ...prevState,
202
+ lastPage: hasMore ? prevState.lastPage! + 1 : prevState.lastPage,
203
+ hasMore,
204
+ };
205
+ }
206
+ default:
207
+ return prevState;
208
+ }
209
+ },
210
+ initialSearchResultState,
211
+ );
212
+
213
+ const algoliaClient = algoliaSearch(appId, apiKey);
214
+ const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
215
+ hitsPerPage: 15,
216
+ advancedSyntax: true,
217
+ disjunctiveFacets: ['language', 'docusaurus_tag'],
218
+ });
219
+
220
+ algoliaHelper.on(
221
+ 'result',
222
+ ({results: {query, hits, page, nbHits, nbPages}}) => {
223
+ if (query === '' || !Array.isArray(hits)) {
224
+ searchResultStateDispatcher({type: 'reset'});
225
+ return;
226
+ }
227
+
228
+ const sanitizeValue = (value: string) =>
229
+ value.replace(
230
+ /algolia-docsearch-suggestion--highlight/g,
231
+ 'search-result-match',
232
+ );
233
+
234
+ const items = hits.map(
235
+ ({
236
+ url,
237
+ _highlightResult: {hierarchy},
238
+ _snippetResult: snippet = {},
239
+ }) => {
240
+ const parsedURL = new URL(url);
241
+ const titles = Object.keys(hierarchy).map((key) =>
242
+ sanitizeValue(hierarchy[key].value),
243
+ );
244
+
245
+ return {
246
+ title: titles.pop()!,
247
+ url: isRegexpStringMatch(externalUrlRegex, parsedURL.href)
248
+ ? parsedURL.href
249
+ : parsedURL.pathname + parsedURL.hash,
250
+ summary: snippet.content
251
+ ? `${sanitizeValue(snippet.content.value)}...`
252
+ : '',
253
+ breadcrumbs: titles,
254
+ };
255
+ },
256
+ );
257
+
258
+ searchResultStateDispatcher({
259
+ type: 'update',
260
+ value: {
261
+ items,
262
+ query,
263
+ totalResults: nbHits,
264
+ totalPages: nbPages,
265
+ lastPage: page,
266
+ hasMore: nbPages > page + 1,
267
+ loading: false,
268
+ },
269
+ });
270
+ },
271
+ );
272
+
273
+ const [loaderRef, setLoaderRef] = useState<HTMLDivElement | null>(null);
274
+ const prevY = useRef(0);
275
+ const observer = useRef(
276
+ ExecutionEnvironment.canUseDOM &&
277
+ new IntersectionObserver(
278
+ (entries) => {
279
+ const {
280
+ isIntersecting,
281
+ boundingClientRect: {y: currentY},
282
+ } = entries[0]!;
283
+
284
+ if (isIntersecting && prevY.current > currentY) {
285
+ searchResultStateDispatcher({type: 'advance'});
286
+ }
287
+
288
+ prevY.current = currentY;
289
+ },
290
+ {threshold: 1},
291
+ ),
292
+ );
293
+
294
+ const getTitle = () =>
295
+ searchQuery
296
+ ? translate(
297
+ {
298
+ id: 'theme.SearchPage.existingResultsTitle',
299
+ message: 'Search results for "{query}"',
300
+ description: 'The search page title for non-empty query',
301
+ },
302
+ {
303
+ query: searchQuery,
304
+ },
305
+ )
306
+ : translate({
307
+ id: 'theme.SearchPage.emptyResultsTitle',
308
+ message: 'Search the documentation',
309
+ description: 'The search page title for empty query',
310
+ });
311
+
312
+ const makeSearch = useDynamicCallback((page: number = 0) => {
313
+ algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
314
+ algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
315
+
316
+ Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
317
+ ([pluginId, searchVersion]) => {
318
+ algoliaHelper.addDisjunctiveFacetRefinement(
319
+ 'docusaurus_tag',
320
+ `docs-${pluginId}-${searchVersion}`,
321
+ );
322
+ },
323
+ );
324
+
325
+ algoliaHelper.setQuery(searchQuery).setPage(page).search();
326
+ });
327
+
328
+ useEffect(() => {
329
+ if (!loaderRef) {
330
+ return undefined;
331
+ }
332
+ const currentObserver = observer.current;
333
+ if (currentObserver) {
334
+ currentObserver.observe(loaderRef);
335
+ return () => currentObserver.unobserve(loaderRef);
336
+ }
337
+ return () => true;
338
+ }, [loaderRef]);
339
+
340
+ useEffect(() => {
341
+ searchResultStateDispatcher({type: 'reset'});
342
+
343
+ if (searchQuery) {
344
+ searchResultStateDispatcher({type: 'loading'});
345
+
346
+ setTimeout(() => {
347
+ makeSearch();
348
+ }, 300);
349
+ }
350
+ }, [searchQuery, docsSearchVersionsHelpers.searchVersions, makeSearch]);
351
+
352
+ useEffect(() => {
353
+ if (!searchResultState.lastPage || searchResultState.lastPage === 0) {
354
+ return;
355
+ }
356
+
357
+ makeSearch(searchResultState.lastPage);
358
+ }, [makeSearch, searchResultState.lastPage]);
359
+
360
+ return (
361
+ <Layout>
362
+ <Head>
363
+ <title>{useTitleFormatter(getTitle())}</title>
364
+ {/*
365
+ We should not index search pages
366
+ See https://github.com/facebook/docusaurus/pull/3233
367
+ */}
368
+ <meta property="robots" content="noindex, follow" />
369
+ </Head>
370
+
371
+ <div className="container margin-vert--lg">
372
+ <h1>{getTitle()}</h1>
373
+
374
+ <form className="row" onSubmit={(e) => e.preventDefault()}>
375
+ <div
376
+ className={clsx('col', styles.searchQueryColumn, {
377
+ 'col--9': docsSearchVersionsHelpers.versioningEnabled,
378
+ 'col--12': !docsSearchVersionsHelpers.versioningEnabled,
379
+ })}>
380
+ <input
381
+ type="search"
382
+ name="q"
383
+ className={styles.searchQueryInput}
384
+ placeholder={translate({
385
+ id: 'theme.SearchPage.inputPlaceholder',
386
+ message: 'Type your search here',
387
+ description: 'The placeholder for search page input',
388
+ })}
389
+ aria-label={translate({
390
+ id: 'theme.SearchPage.inputLabel',
391
+ message: 'Search',
392
+ description: 'The ARIA label for search page input',
393
+ })}
394
+ onChange={(e) => setSearchQuery(e.target.value)}
395
+ value={searchQuery}
396
+ autoComplete="off"
397
+ autoFocus
398
+ />
399
+ </div>
400
+
401
+ {docsSearchVersionsHelpers.versioningEnabled && (
402
+ <SearchVersionSelectList
403
+ docsSearchVersionsHelpers={docsSearchVersionsHelpers}
404
+ />
405
+ )}
406
+ </form>
407
+
408
+ <div className="row">
409
+ <div className={clsx('col', 'col--8', styles.searchResultsColumn)}>
410
+ {!!searchResultState.totalResults &&
411
+ documentsFoundPlural(searchResultState.totalResults)}
412
+ </div>
413
+
414
+ <div
415
+ className={clsx(
416
+ 'col',
417
+ 'col--4',
418
+ 'text--right',
419
+ styles.searchLogoColumn,
420
+ )}>
421
+ <a
422
+ target="_blank"
423
+ rel="noopener noreferrer"
424
+ href="https://www.algolia.com/"
425
+ aria-label={translate({
426
+ id: 'theme.SearchPage.algoliaLabel',
427
+ message: 'Search by Algolia',
428
+ description: 'The ARIA label for Algolia mention',
429
+ })}>
430
+ <svg viewBox="0 0 168 24" className={styles.algoliaLogo}>
431
+ <g fill="none">
432
+ <path
433
+ className={styles.algoliaLogoPathFill}
434
+ 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"
435
+ />
436
+ <path
437
+ fill="#5468FF"
438
+ 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"
439
+ />
440
+ <path
441
+ fill="white"
442
+ 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"
443
+ />
444
+ </g>
445
+ </svg>
446
+ </a>
447
+ </div>
448
+ </div>
449
+
450
+ {searchResultState.items.length > 0 ? (
451
+ <main>
452
+ {searchResultState.items.map(
453
+ ({title, url, summary, breadcrumbs}, i) => (
454
+ <article key={i} className={styles.searchResultItem}>
455
+ <h2 className={styles.searchResultItemHeading}>
456
+ <Link to={url} dangerouslySetInnerHTML={{__html: title}} />
457
+ </h2>
458
+
459
+ {breadcrumbs.length > 0 && (
460
+ <nav aria-label="breadcrumbs">
461
+ <ul
462
+ className={clsx(
463
+ 'breadcrumbs',
464
+ styles.searchResultItemPath,
465
+ )}>
466
+ {breadcrumbs.map((html, index) => (
467
+ <li
468
+ key={index}
469
+ className="breadcrumbs__item"
470
+ // Developer provided the HTML, so assume it's safe.
471
+ // eslint-disable-next-line react/no-danger
472
+ dangerouslySetInnerHTML={{__html: html}}
473
+ />
474
+ ))}
475
+ </ul>
476
+ </nav>
477
+ )}
478
+
479
+ {summary && (
480
+ <p
481
+ className={styles.searchResultItemSummary}
482
+ // Developer provided the HTML, so assume it's safe.
483
+ // eslint-disable-next-line react/no-danger
484
+ dangerouslySetInnerHTML={{__html: summary}}
485
+ />
486
+ )}
487
+ </article>
488
+ ),
489
+ )}
490
+ </main>
491
+ ) : (
492
+ [
493
+ searchQuery && !searchResultState.loading && (
494
+ <p key="no-results">
495
+ <Translate
496
+ id="theme.SearchPage.noResultsText"
497
+ description="The paragraph for empty search result">
498
+ No results were found
499
+ </Translate>
500
+ </p>
501
+ ),
502
+ !!searchResultState.loading && (
503
+ <div key="spinner" className={styles.loadingSpinner} />
504
+ ),
505
+ ]
506
+ )}
507
+
508
+ {searchResultState.hasMore && (
509
+ <div className={styles.loader} ref={setLoaderRef}>
510
+ <Translate
511
+ id="theme.SearchPage.fetchingNewResults"
512
+ description="The paragraph for fetching new search results">
513
+ Fetching new results...
514
+ </Translate>
515
+ </div>
516
+ )}
517
+ </div>
518
+ </Layout>
519
+ );
520
+ }
521
+
522
+ export default function SearchPage(): JSX.Element {
523
+ return (
524
+ <HtmlClassNameProvider className="search-page-wrapper">
525
+ <SearchPageContent />
526
+ </HtmlClassNameProvider>
527
+ );
528
+ }
@@ -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
  }
@@ -0,0 +1,35 @@
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
+ declare module '@docusaurus/theme-search-algolia' {
9
+ import type {DeepPartial} from 'utility-types';
10
+
11
+ export type ThemeConfig = {
12
+ algolia: {
13
+ contextualSearch: boolean;
14
+ externalUrlRegex?: string;
15
+ appId: string;
16
+ apiKey: string;
17
+ indexName: string;
18
+ searchParameters: {[key: string]: unknown};
19
+ searchPagePath: string | false | null;
20
+ };
21
+ };
22
+ export type UserThemeConfig = DeepPartial<ThemeConfig>;
23
+ }
24
+
25
+ declare module '@docusaurus/theme-search-algolia/client' {
26
+ export function useAlgoliaContextualFacetFilters(): [string, string[]];
27
+ }
28
+
29
+ declare module '@theme/SearchPage' {
30
+ export default function SearchPage(): JSX.Element;
31
+ }
32
+
33
+ declare module '@theme/SearchBar' {
34
+ export default function SearchBar(): JSX.Element;
35
+ }
package/src/types.d.ts ADDED
@@ -0,0 +1,10 @@
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
+ /// <reference types="@docusaurus/module-type-aliases" />
9
+ /// <reference types="@docusaurus/theme-common" />
10
+ /// <reference types="@docusaurus/theme-classic" />
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {Joi} from '@docusaurus/utils-validation';
9
+ import type {
10
+ ThemeConfig,
11
+ ThemeConfigValidationContext,
12
+ } from '@docusaurus/types';
13
+
14
+ export const DEFAULT_CONFIG = {
15
+ // Enabled by default, as it makes sense in most cases
16
+ // see also https://github.com/facebook/docusaurus/issues/5880
17
+ contextualSearch: true,
18
+
19
+ searchParameters: {},
20
+ searchPagePath: 'search',
21
+ };
22
+
23
+ export const Schema = Joi.object({
24
+ algolia: Joi.object({
25
+ // Docusaurus attributes
26
+ contextualSearch: Joi.boolean().default(DEFAULT_CONFIG.contextualSearch),
27
+ externalUrlRegex: Joi.string().optional(),
28
+ // Algolia attributes
29
+ appId: Joi.string().required().messages({
30
+ 'any.required':
31
+ '"algolia.appId" is required. If you haven\'t migrated to the new DocSearch infra, please refer to the blog post for instructions: https://docusaurus.io/blog/2021/11/21/algolia-docsearch-migration',
32
+ }),
33
+ apiKey: Joi.string().required(),
34
+ indexName: Joi.string().required(),
35
+ searchParameters: Joi.object()
36
+ .default(DEFAULT_CONFIG.searchParameters)
37
+ .unknown(),
38
+ searchPagePath: Joi.alternatives()
39
+ .try(Joi.boolean().invalid(true), Joi.string())
40
+ .allow(null)
41
+ .default(DEFAULT_CONFIG.searchPagePath),
42
+ })
43
+ .label('themeConfig.algolia')
44
+ .required()
45
+ .unknown(), // DocSearch 3 is still alpha: don't validate the rest for now
46
+ });
47
+
48
+ export function validateThemeConfig({
49
+ validate,
50
+ themeConfig,
51
+ }: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
52
+ return validate(Schema, themeConfig);
53
+ }