index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import React, { useRef, useCallback, useState } from "react";
  2. import classnames from "classnames";
  3. import { useHistory } from "@docusaurus/router";
  4. import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
  5. import { usePluginData } from '@docusaurus/useGlobalData';
  6. import useIsBrowser from "@docusaurus/useIsBrowser";
  7. const Search = props => {
  8. const initialized = useRef(false);
  9. const searchBarRef = useRef(null);
  10. const [indexReady, setIndexReady] = useState(false);
  11. const history = useHistory();
  12. const { siteConfig = {} } = useDocusaurusContext();
  13. const isBrowser = useIsBrowser();
  14. const { baseUrl } = siteConfig;
  15. const initAlgolia = (searchDocs, searchIndex, DocSearch) => {
  16. new DocSearch({
  17. searchDocs,
  18. searchIndex,
  19. baseUrl,
  20. inputSelector: "#search_input_react",
  21. // Override algolia's default selection event, allowing us to do client-side
  22. // navigation and avoiding a full page refresh.
  23. handleSelected: (_input, _event, suggestion) => {
  24. const url = suggestion.url || "/";
  25. // Use an anchor tag to parse the absolute url into a relative url
  26. // Alternatively, we can use new URL(suggestion.url) but its not supported in IE
  27. const a = document.createElement("a");
  28. a.href = url;
  29. // Algolia use closest parent element id #__docusaurus when a h1 page title does not have an id
  30. // So, we can safely remove it. See https://github.com/facebook/docusaurus/issues/1828 for more details.
  31. history.push(url);
  32. }
  33. });
  34. };
  35. const pluginData = usePluginData('docusaurus-lunr-search');
  36. const getSearchDoc = () =>
  37. process.env.NODE_ENV === "production"
  38. ? fetch(`${baseUrl}${pluginData.fileNames.searchDoc}`).then((content) => content.json())
  39. : Promise.resolve([]);
  40. const getLunrIndex = () =>
  41. process.env.NODE_ENV === "production"
  42. ? fetch(`${baseUrl}${pluginData.fileNames.lunrIndex}`).then((content) => content.json())
  43. : Promise.resolve([]);
  44. const loadAlgolia = () => {
  45. if (!initialized.current) {
  46. Promise.all([
  47. getSearchDoc(),
  48. getLunrIndex(),
  49. import("./DocSearch"),
  50. import("./algolia.css")
  51. ]).then(([searchDocs, searchIndex, { default: DocSearch }]) => {
  52. if (searchDocs.length === 0) {
  53. return;
  54. }
  55. initAlgolia(searchDocs, searchIndex, DocSearch);
  56. setIndexReady(true);
  57. });
  58. initialized.current = true;
  59. }
  60. };
  61. const toggleSearchIconClick = useCallback(
  62. e => {
  63. if (!searchBarRef.current.contains(e.target)) {
  64. searchBarRef.current.focus();
  65. }
  66. props.handleSearchBarToggle && props.handleSearchBarToggle(!props.isSearchBarExpanded);
  67. },
  68. [props.isSearchBarExpanded]
  69. );
  70. if (isBrowser) {
  71. loadAlgolia();
  72. }
  73. return (
  74. <div className="navbar__search" key="search-box">
  75. <span
  76. aria-label="expand searchbar"
  77. role="button"
  78. className={classnames("search-icon", {
  79. "search-icon-hidden": props.isSearchBarExpanded
  80. })}
  81. onClick={toggleSearchIconClick}
  82. onKeyDown={toggleSearchIconClick}
  83. tabIndex={0}
  84. />
  85. <input
  86. id="search_input_react"
  87. type="search"
  88. placeholder={indexReady ? 'Search' : 'Loading...'}
  89. aria-label="Search"
  90. className={classnames(
  91. "navbar__search-input",
  92. { "search-bar-expanded": props.isSearchBarExpanded },
  93. { "search-bar": !props.isSearchBarExpanded }
  94. )}
  95. onClick={loadAlgolia}
  96. onMouseOver={loadAlgolia}
  97. onFocus={toggleSearchIconClick}
  98. onBlur={toggleSearchIconClick}
  99. ref={searchBarRef}
  100. disabled={!indexReady}
  101. />
  102. </div>
  103. );
  104. };
  105. export default Search;