index.js 3.5 KB

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