HeaderSearchBox.jsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import { createSubscribedElement } from './UnstatedUtils';
  5. import AppContainer from '../services/AppContainer';
  6. import SearchForm from './SearchForm';
  7. class HeaderSearchBox extends React.Component {
  8. constructor(props) {
  9. super(props);
  10. this.state = {
  11. text: '',
  12. isScopeChildren: false,
  13. };
  14. this.onInputChange = this.onInputChange.bind(this);
  15. this.onClickAllPages = this.onClickAllPages.bind(this);
  16. this.onClickChildren = this.onClickChildren.bind(this);
  17. this.search = this.search.bind(this);
  18. }
  19. componentDidMount() {
  20. }
  21. componentWillUnmount() {
  22. }
  23. onInputChange(text) {
  24. this.setState({ text });
  25. }
  26. onClickAllPages() {
  27. this.setState({ isScopeChildren: false });
  28. }
  29. onClickChildren() {
  30. this.setState({ isScopeChildren: true });
  31. }
  32. search() {
  33. const url = new URL(window.location.href);
  34. url.pathname = '/_search';
  35. // construct search query
  36. let q = this.state.text;
  37. if (this.state.isScopeChildren) {
  38. q += ` prefix:${window.location.pathname}`;
  39. }
  40. url.searchParams.append('q', q);
  41. window.location.href = url.href;
  42. }
  43. render() {
  44. const { t, appContainer } = this.props;
  45. const scopeLabel = this.state.isScopeChildren
  46. ? t('header_search_box.label.This tree')
  47. : t('header_search_box.label.All pages');
  48. const config = appContainer.getConfig();
  49. const isReachable = config.isSearchServiceReachable;
  50. return (
  51. <div className={`form-group mb-0 ${isReachable ? '' : 'has-error'}`}>
  52. <div className="input-group flex-nowrap">
  53. <div className="input-group-prepend">
  54. <button className="btn btn-secondary dropdown-toggle py-0" type="button" data-toggle="dropdown" aria-haspopup="true">
  55. {scopeLabel}
  56. </button>
  57. <div className="dropdown-menu">
  58. <button className="dropdown-item" type="button" onClick={this.onClickAllPages}>{ t('header_search_box.item_label.All pages') }</button>
  59. <button className="dropdown-item" type="button" onClick={this.onClickChildren}>{ t('header_search_box.item_label.This tree') }</button>
  60. </div>
  61. </div>
  62. <SearchForm
  63. t={this.props.t}
  64. crowi={this.props.appContainer}
  65. onInputChange={this.onInputChange}
  66. onSubmit={this.search}
  67. placeholder="Search ..."
  68. />
  69. <div className="btn-group-submit-search">
  70. <span className="btn-link text-decoration-none" onClick={this.search}>
  71. <i className="icon-magnifier"></i>
  72. </span>
  73. </div>
  74. </div>
  75. </div>
  76. );
  77. }
  78. }
  79. /**
  80. * Wrapper component for using unstated
  81. */
  82. const HeaderSearchBoxWrapper = (props) => {
  83. return createSubscribedElement(HeaderSearchBox, props, [AppContainer]);
  84. };
  85. HeaderSearchBox.propTypes = {
  86. t: PropTypes.func.isRequired, // i18next
  87. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  88. };
  89. export default withTranslation()(HeaderSearchBoxWrapper);