@docusaurus/theme-search-algolia 2.0.0-beta.0e652730d → 2.0.0-beta.10

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 (36) hide show
  1. package/copyUntypedFiles.js +20 -0
  2. package/lib/index.d.ts +11 -0
  3. package/lib/index.js +85 -0
  4. package/lib/templates/opensearch.d.ts +8 -0
  5. package/lib/templates/opensearch.js +23 -0
  6. package/lib/theme/SearchBar/index.d.ts +9 -0
  7. package/lib/theme/SearchBar/index.js +153 -0
  8. package/lib/theme/SearchBar/styles.css +21 -0
  9. package/lib/theme/SearchBar/styles.module.css +20 -0
  10. package/lib/theme/SearchMetadata/index.d.ts +10 -0
  11. package/lib/theme/SearchMetadata/index.js +20 -0
  12. package/lib/theme/SearchPage/index.d.ts +9 -0
  13. package/lib/theme/SearchPage/index.js +299 -0
  14. package/lib/theme/SearchPage/styles.module.css +119 -0
  15. package/lib/theme/hooks/useAlgoliaContextualFacetFilters.d.ts +8 -0
  16. package/lib/theme/hooks/useAlgoliaContextualFacetFilters.js +15 -0
  17. package/lib/theme/hooks/useSearchQuery.d.ts +9 -0
  18. package/lib/theme/hooks/useSearchQuery.js +43 -0
  19. package/lib/validateThemeConfig.d.ts +13 -0
  20. package/lib/validateThemeConfig.js +39 -0
  21. package/package.json +24 -12
  22. package/src/__tests__/validateThemeConfig.test.js +14 -0
  23. package/src/{index.js → index.ts} +37 -19
  24. package/src/templates/{opensearch.js → opensearch.ts} +4 -2
  25. package/src/theme/SearchBar/{index.js → index.tsx} +78 -26
  26. package/src/theme/{SearchMetadatas/index.js → SearchMetadata/index.tsx} +9 -2
  27. package/src/theme/SearchPage/{index.js → index.tsx} +81 -51
  28. package/src/theme/hooks/{useAlgoliaContextualFacetFilters.js → useAlgoliaContextualFacetFilters.ts} +4 -3
  29. package/src/theme/hooks/useSearchQuery.ts +63 -0
  30. package/src/theme-search-algolia.d.ts +47 -0
  31. package/src/types.d.ts +10 -0
  32. package/src/{validateThemeConfig.js → validateThemeConfig.ts} +10 -7
  33. package/tsconfig.browser.json +8 -0
  34. package/tsconfig.json +9 -0
  35. package/tsconfig.server.json +4 -0
  36. package/src/theme/hooks/useSearchQuery.js +0 -44
@@ -0,0 +1,20 @@
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
+ const path = require('path');
9
+ const fs = require('fs-extra');
10
+
11
+ /**
12
+ * Copy all untyped and static assets files to lib.
13
+ */
14
+ const srcDir = path.resolve(__dirname, 'src');
15
+ const libDir = path.resolve(__dirname, 'lib');
16
+ fs.copySync(srcDir, libDir, {
17
+ filter(filepath) {
18
+ return !/__tests__/.test(filepath) && !/\.tsx?$/.test(filepath);
19
+ },
20
+ });
package/lib/index.d.ts ADDED
@@ -0,0 +1,11 @@
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
+ import type { DocusaurusContext, Plugin } from '@docusaurus/types';
8
+ export default function theme(context: DocusaurusContext & {
9
+ baseUrl: string;
10
+ }): Plugin<void>;
11
+ export { validateThemeConfig } from './validateThemeConfig';
package/lib/index.js ADDED
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.validateThemeConfig = void 0;
10
+ const tslib_1 = require("tslib");
11
+ const path_1 = (0, tslib_1.__importDefault)(require("path"));
12
+ const fs_1 = (0, tslib_1.__importDefault)(require("fs"));
13
+ const eta_1 = require("eta");
14
+ const utils_1 = require("@docusaurus/utils");
15
+ const theme_translations_1 = require("@docusaurus/theme-translations");
16
+ const opensearch_1 = (0, tslib_1.__importDefault)(require("./templates/opensearch"));
17
+ const lodash_1 = require("lodash");
18
+ const getCompiledOpenSearchTemplate = (0, lodash_1.memoize)(() => (0, eta_1.compile)(opensearch_1.default.trim()));
19
+ function renderOpenSearchTemplate(data) {
20
+ const compiled = getCompiledOpenSearchTemplate();
21
+ return compiled(data, eta_1.defaultConfig);
22
+ }
23
+ const OPEN_SEARCH_FILENAME = 'opensearch.xml';
24
+ function theme(context) {
25
+ const { baseUrl, siteConfig: { title, url, favicon }, i18n: { currentLocale }, } = context;
26
+ const pageComponent = './theme/SearchPage/index.js';
27
+ const pagePath = (0, utils_1.getSwizzledComponent)(pageComponent) ||
28
+ path_1.default.resolve(__dirname, pageComponent);
29
+ return {
30
+ name: 'docusaurus-theme-search-algolia',
31
+ getPathsToWatch() {
32
+ return [pagePath];
33
+ },
34
+ getThemePath() {
35
+ return path_1.default.resolve(__dirname, './theme');
36
+ },
37
+ getTypeScriptThemePath() {
38
+ return path_1.default.resolve(__dirname, '..', 'src', 'theme');
39
+ },
40
+ getDefaultCodeTranslationMessages() {
41
+ return (0, theme_translations_1.readDefaultCodeTranslationMessages)({
42
+ locale: currentLocale,
43
+ name: 'theme-search-algolia',
44
+ });
45
+ },
46
+ async contentLoaded({ actions: { addRoute } }) {
47
+ addRoute({
48
+ path: (0, utils_1.normalizeUrl)([baseUrl, 'search']),
49
+ component: pagePath,
50
+ exact: true,
51
+ });
52
+ },
53
+ async postBuild({ outDir }) {
54
+ try {
55
+ fs_1.default.writeFileSync(path_1.default.join(outDir, OPEN_SEARCH_FILENAME), renderOpenSearchTemplate({
56
+ title,
57
+ url: url + baseUrl,
58
+ favicon: favicon ? (0, utils_1.normalizeUrl)([url, baseUrl, favicon]) : null,
59
+ }));
60
+ }
61
+ catch (err) {
62
+ console.error(err);
63
+ throw new Error(`Generating OpenSearch file failed: ${err}`);
64
+ }
65
+ },
66
+ injectHtmlTags() {
67
+ return {
68
+ headTags: [
69
+ {
70
+ tagName: 'link',
71
+ attributes: {
72
+ rel: 'search',
73
+ type: 'application/opensearchdescription+xml',
74
+ title,
75
+ href: (0, utils_1.normalizeUrl)([baseUrl, OPEN_SEARCH_FILENAME]),
76
+ },
77
+ },
78
+ ],
79
+ };
80
+ },
81
+ };
82
+ }
83
+ exports.default = theme;
84
+ var validateThemeConfig_1 = require("./validateThemeConfig");
85
+ Object.defineProperty(exports, "validateThemeConfig", { enumerable: true, get: function () { return validateThemeConfig_1.validateThemeConfig; } });
@@ -0,0 +1,8 @@
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
+ declare const _default: "\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<OpenSearchDescription xmlns=\"http://a9.com/-/spec/opensearch/1.1/\"\n xmlns:moz=\"http://www.mozilla.org/2006/browser/search/\">\n <ShortName><%= it.title %></ShortName>\n <Description>Search <%= it.title %></Description>\n <InputEncoding>UTF-8</InputEncoding>\n <% if (it.favicon) { _%>\n <Image width=\"16\" height=\"16\" type=\"image/x-icon\"><%= it.favicon %></Image>\n <% } _%>\n <Url type=\"text/html\" method=\"get\" template=\"<%= it.url %>search?q={searchTerms}\"/>\n <Url type=\"application/opensearchdescription+xml\" rel=\"self\" template=\"<%= it.url %>opensearch.xml\" />\n <moz:SearchForm><%= it.url %></moz:SearchForm>\n</OpenSearchDescription>\n";
8
+ export default _default;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.default = `
10
+ <?xml version="1.0" encoding="UTF-8"?>
11
+ <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
12
+ xmlns:moz="http://www.mozilla.org/2006/browser/search/">
13
+ <ShortName><%= it.title %></ShortName>
14
+ <Description>Search <%= it.title %></Description>
15
+ <InputEncoding>UTF-8</InputEncoding>
16
+ <% if (it.favicon) { _%>
17
+ <Image width="16" height="16" type="image/x-icon"><%= it.favicon %></Image>
18
+ <% } _%>
19
+ <Url type="text/html" method="get" template="<%= it.url %>search?q={searchTerms}"/>
20
+ <Url type="application/opensearchdescription+xml" rel="self" template="<%= it.url %>opensearch.xml" />
21
+ <moz:SearchForm><%= it.url %></moz:SearchForm>
22
+ </OpenSearchDescription>
23
+ `;
@@ -0,0 +1,9 @@
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
+ /// <reference types="react" />
8
+ declare function SearchBar(): JSX.Element;
9
+ export default SearchBar;
@@ -0,0 +1,153 @@
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
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
8
+ import React, { useState, useRef, useCallback, useMemo } from 'react';
9
+ import { createPortal } from 'react-dom';
10
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
11
+ import { useHistory } from '@docusaurus/router';
12
+ import { useBaseUrlUtils } from '@docusaurus/useBaseUrl';
13
+ import Link from '@docusaurus/Link';
14
+ import Head from '@docusaurus/Head';
15
+ import useSearchQuery from '@theme/hooks/useSearchQuery';
16
+ import { isRegexpStringMatch } from '@docusaurus/theme-common';
17
+ import { DocSearchButton, useDocSearchKeyboardEvents } from '@docsearch/react';
18
+ import useAlgoliaContextualFacetFilters from '@theme/hooks/useAlgoliaContextualFacetFilters';
19
+ import { translate } from '@docusaurus/Translate';
20
+ import styles from './styles.module.css';
21
+ let DocSearchModal = null;
22
+ function Hit({ hit, children, }) {
23
+ return <Link to={hit.url}>{children}</Link>;
24
+ }
25
+ function ResultsFooter({ state, onClose }) {
26
+ const { generateSearchPageLink } = useSearchQuery();
27
+ return (<Link to={generateSearchPageLink(state.query)} onClick={onClose}>
28
+ See all {state.context.nbHits} results
29
+ </Link>);
30
+ }
31
+ function DocSearch({ contextualSearch, externalUrlRegex, ...props }) {
32
+ var _a, _b;
33
+ const { siteMetadata } = useDocusaurusContext();
34
+ const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters();
35
+ const configFacetFilters = (_b = (_a = props.searchParameters) === null || _a === void 0 ? void 0 : _a.facetFilters) !== null && _b !== void 0 ? _b : [];
36
+ const facetFilters = contextualSearch
37
+ ? // Merge contextual search filters with config filters
38
+ [...contextualSearchFacetFilters, ...configFacetFilters]
39
+ : // ... or use config facetFilters
40
+ configFacetFilters;
41
+ // we let user override default searchParameters if he wants to
42
+ const searchParameters = {
43
+ ...props.searchParameters,
44
+ facetFilters,
45
+ };
46
+ const { withBaseUrl } = useBaseUrlUtils();
47
+ const history = useHistory();
48
+ const searchContainer = useRef(null);
49
+ const searchButtonRef = useRef(null);
50
+ const [isOpen, setIsOpen] = useState(false);
51
+ const [initialQuery, setInitialQuery] = useState(undefined);
52
+ const importDocSearchModalIfNeeded = useCallback(() => {
53
+ if (DocSearchModal) {
54
+ return Promise.resolve();
55
+ }
56
+ return Promise.all([
57
+ // @ts-ignore
58
+ import('@docsearch/react/modal'),
59
+ // @ts-ignore
60
+ import('@docsearch/react/style'),
61
+ import('./styles.css'),
62
+ ]).then(([{ DocSearchModal: Modal }]) => {
63
+ DocSearchModal = Modal;
64
+ });
65
+ }, []);
66
+ const onOpen = useCallback(() => {
67
+ importDocSearchModalIfNeeded().then(() => {
68
+ searchContainer.current = document.createElement('div');
69
+ document.body.insertBefore(searchContainer.current, document.body.firstChild);
70
+ setIsOpen(true);
71
+ });
72
+ }, [importDocSearchModalIfNeeded, setIsOpen]);
73
+ const onClose = useCallback(() => {
74
+ var _a;
75
+ setIsOpen(false);
76
+ (_a = searchContainer.current) === null || _a === void 0 ? void 0 : _a.remove();
77
+ }, [setIsOpen]);
78
+ const onInput = useCallback((event) => {
79
+ importDocSearchModalIfNeeded().then(() => {
80
+ setIsOpen(true);
81
+ setInitialQuery(event.key);
82
+ });
83
+ }, [importDocSearchModalIfNeeded, setIsOpen, setInitialQuery]);
84
+ const navigator = useRef({
85
+ navigate({ itemUrl }) {
86
+ // Algolia results could contain URL's from other domains which cannot
87
+ // be served through history and should navigate with window.location
88
+ if (isRegexpStringMatch(externalUrlRegex, itemUrl)) {
89
+ window.location.href = itemUrl;
90
+ }
91
+ else {
92
+ history.push(itemUrl);
93
+ }
94
+ },
95
+ }).current;
96
+ const transformItems = useRef((items) => items.map((item) => {
97
+ // If Algolia contains a external domain, we should navigate without relative URL
98
+ if (isRegexpStringMatch(externalUrlRegex, item.url)) {
99
+ return item;
100
+ }
101
+ // We transform the absolute URL into a relative URL.
102
+ const url = new URL(item.url);
103
+ return {
104
+ ...item,
105
+ url: withBaseUrl(`${url.pathname}${url.hash}`),
106
+ };
107
+ })).current;
108
+ const resultsFooterComponent = useMemo(
109
+ // eslint-disable-next-line react/no-unstable-nested-components
110
+ () => (footerProps) => <ResultsFooter {...footerProps} onClose={onClose}/>, [onClose]);
111
+ const transformSearchClient = useCallback((searchClient) => {
112
+ searchClient.addAlgoliaAgent('docusaurus', siteMetadata.docusaurusVersion);
113
+ return searchClient;
114
+ }, [siteMetadata.docusaurusVersion]);
115
+ useDocSearchKeyboardEvents({
116
+ isOpen,
117
+ onOpen,
118
+ onClose,
119
+ onInput,
120
+ searchButtonRef,
121
+ });
122
+ const translatedSearchLabel = translate({
123
+ id: 'theme.SearchBar.label',
124
+ message: 'Search',
125
+ description: 'The ARIA label and placeholder for search button',
126
+ });
127
+ return (<>
128
+ <Head>
129
+ {/* This hints the browser that the website will load data from Algolia,
130
+ and allows it to preconnect to the DocSearch cluster. It makes the first
131
+ query faster, especially on mobile. */}
132
+ <link rel="preconnect" href={`https://${props.appId}-dsn.algolia.net`} crossOrigin="anonymous"/>
133
+ </Head>
134
+
135
+ <div className={styles.searchBox}>
136
+ <DocSearchButton onTouchStart={importDocSearchModalIfNeeded} onFocus={importDocSearchModalIfNeeded} onMouseOver={importDocSearchModalIfNeeded} onClick={onOpen} ref={searchButtonRef} translations={{
137
+ buttonText: translatedSearchLabel,
138
+ buttonAriaLabel: translatedSearchLabel,
139
+ }}/>
140
+ </div>
141
+
142
+ {isOpen &&
143
+ DocSearchModal &&
144
+ searchContainer.current &&
145
+ createPortal(<DocSearchModal onClose={onClose} initialScrollY={window.scrollY} initialQuery={initialQuery} navigator={navigator} transformItems={transformItems} hitComponent={Hit} resultsFooterComponent={resultsFooterComponent} transformSearchClient={transformSearchClient} {...props} searchParameters={searchParameters}/>, searchContainer.current)}
146
+ </>);
147
+ }
148
+ function SearchBar() {
149
+ const { siteConfig } = useDocusaurusContext();
150
+ // @ts-ignore
151
+ return <DocSearch {...siteConfig.themeConfig.algolia}/>;
152
+ }
153
+ export default SearchBar;
@@ -0,0 +1,21 @@
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
+ :root {
9
+ --docsearch-primary-color: var(--ifm-color-primary);
10
+ --docsearch-text-color: var(--ifm-font-color-base);
11
+ }
12
+
13
+ .DocSearch-Button {
14
+ margin: 0;
15
+ transition: all var(--ifm-transition-fast)
16
+ var(--ifm-transition-timing-default);
17
+ }
18
+
19
+ .DocSearch-Container {
20
+ z-index: calc(var(--ifm-z-index-fixed) + 1);
21
+ }
@@ -0,0 +1,20 @@
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
+ @media (max-width: 996px) {
9
+ .searchBox {
10
+ position: absolute;
11
+ right: var(--ifm-navbar-padding-horizontal);
12
+ }
13
+ }
14
+
15
+ @media (min-width: 997px) {
16
+ .searchBox {
17
+ padding: var(--ifm-navbar-item-padding-vertical)
18
+ var(--ifm-navbar-item-padding-horizontal);
19
+ }
20
+ }
@@ -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
+ /// <reference types="react" />
8
+ import type { SearchMetadataProps } from '@theme/SearchMetadata';
9
+ declare function SearchMetadata({ locale, version, tag, }: SearchMetadataProps): JSX.Element;
10
+ export default SearchMetadata;
@@ -0,0 +1,20 @@
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
+ import React from 'react';
8
+ import Head from '@docusaurus/Head';
9
+ // Override default/agnostic SearchMetas to use Algolia-specific metadata
10
+ function SearchMetadata({ locale, version, tag, }) {
11
+ // Seems safe to consider here the locale is the language,
12
+ // as the existing docsearch:language filter is afaik a regular string-based filter
13
+ const language = locale;
14
+ return (<Head>
15
+ {language && <meta name="docsearch:language" content={language}/>}
16
+ {version && <meta name="docsearch:version" content={version}/>}
17
+ {tag && <meta name="docsearch:docusaurus_tag" content={tag}/>}
18
+ </Head>);
19
+ }
20
+ export default SearchMetadata;
@@ -0,0 +1,9 @@
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
+ /// <reference types="react" />
8
+ declare function SearchPage(): JSX.Element;
9
+ export default SearchPage;