@docusaurus/theme-search-algolia 2.0.0-beta.1ec2c95e3 → 2.0.0-beta.21

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