index.jsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import React, { useRef, useCallback, useState } from "react";
  2. import clsx from "clsx";
  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. import { HighlightSearchResults } from "./HighlightSearchResults";
  8. const Search = props => {
  9. const initialized = useRef(false);
  10. const searchBarRef = useRef(null);
  11. const [indexReady, setIndexReady] = useState(false);
  12. const history = useHistory();
  13. const { siteConfig = {} } = useDocusaurusContext();
  14. const pluginConfig = (siteConfig.plugins || []).find(plugin => Array.isArray(plugin) && typeof plugin[0] === "string" && plugin[0].includes("docusaurus-lunr-search"))
  15. const isBrowser = useIsBrowser();
  16. const { baseUrl } = siteConfig;
  17. const assetUrl = pluginConfig && pluginConfig[1]?.assetUrl || baseUrl;
  18. const initAlgolia = (searchDocs, searchIndex, DocSearch, options) => {
  19. new DocSearch({
  20. searchDocs,
  21. searchIndex,
  22. baseUrl,
  23. inputSelector: "#search_input_react",
  24. // Override algolia's default selection event, allowing us to do client-side
  25. // navigation and avoiding a full page refresh.
  26. handleSelected: (_input, _event, suggestion) => {
  27. const url = suggestion.url || "/";
  28. // Use an anchor tag to parse the absolute url into a relative url
  29. // Alternatively, we can use new URL(suggestion.url) but its not supported in IE
  30. const a = document.createElement("a");
  31. a.href = url;
  32. _input.setVal(''); // clear value
  33. _event.target.blur(); // remove focus
  34. // Get the highlight word from the suggestion.
  35. let wordToHighlight = '';
  36. if (options.highlightResult) {
  37. try {
  38. const matchedLine = suggestion.text || suggestion.subcategory || suggestion.title;
  39. const matchedWordResult = matchedLine.match(new RegExp('<span.+span>\\w*', 'g'));
  40. if (matchedWordResult && matchedWordResult.length > 0) {
  41. const tempDoc = document.createElement('div');
  42. tempDoc.innerHTML = matchedWordResult[0];
  43. wordToHighlight = tempDoc.textContent;
  44. }
  45. } catch (e) {
  46. console.log(e);
  47. }
  48. }
  49. history.push(url, {
  50. highlightState: { wordToHighlight },
  51. });
  52. }
  53. });
  54. };
  55. const pluginData = usePluginData('docusaurus-lunr-search');
  56. const getSearchDoc = () =>
  57. process.env.NODE_ENV === "production"
  58. ? fetch(`${assetUrl}${pluginData.fileNames.searchDoc}`).then((content) => content.json())
  59. : Promise.resolve({});
  60. const getLunrIndex = () =>
  61. process.env.NODE_ENV === "production"
  62. ? fetch(`${assetUrl}${pluginData.fileNames.lunrIndex}`).then((content) => content.json())
  63. : Promise.resolve([]);
  64. const loadAlgolia = () => {
  65. if (!initialized.current) {
  66. Promise.all([
  67. getSearchDoc(),
  68. getLunrIndex(),
  69. import("./DocSearch"),
  70. import("./algolia.css")
  71. ]).then(([searchDocFile, searchIndex, { default: DocSearch }]) => {
  72. const { searchDocs, options } = searchDocFile;
  73. if (!searchDocs || searchDocs.length === 0) {
  74. return;
  75. }
  76. initAlgolia(searchDocs, searchIndex, DocSearch, options);
  77. setIndexReady(true);
  78. });
  79. initialized.current = true;
  80. }
  81. };
  82. const toggleSearchIconClick = useCallback(
  83. e => {
  84. if (!searchBarRef.current.contains(e.target)) {
  85. searchBarRef.current.focus();
  86. }
  87. props.handleSearchBarToggle && props.handleSearchBarToggle(!props.isSearchBarExpanded);
  88. },
  89. [props.isSearchBarExpanded]
  90. );
  91. if (isBrowser) {
  92. loadAlgolia();
  93. }
  94. return (
  95. <div className="navbar__search" key="search-box">
  96. <span
  97. aria-label="expand searchbar"
  98. role="button"
  99. className={clsx("search-icon", {
  100. "search-icon-hidden": props.isSearchBarExpanded
  101. })}
  102. onClick={toggleSearchIconClick}
  103. onKeyDown={toggleSearchIconClick}
  104. tabIndex={0}
  105. />
  106. <input
  107. id="search_input_react"
  108. type="search"
  109. placeholder={indexReady ? 'Search Ctrl+K' : 'Loading...'}
  110. aria-label="Search"
  111. className={clsx(
  112. "navbar__search-input",
  113. { "search-bar-expanded": props.isSearchBarExpanded },
  114. { "search-bar": !props.isSearchBarExpanded }
  115. )}
  116. onClick={loadAlgolia}
  117. onMouseOver={loadAlgolia}
  118. onFocus={toggleSearchIconClick}
  119. onBlur={toggleSearchIconClick}
  120. ref={searchBarRef}
  121. disabled={!indexReady}
  122. />
  123. <HighlightSearchResults />
  124. </div>
  125. );
  126. };
  127. export default Search;