@docusaurus/theme-search-algolia 2.0.0-beta.15a2b59f9 → 2.0.0-beta.17

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