SearchControl.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import React, {
  2. FC, useCallback, useEffect, useState,
  3. } from 'react';
  4. import { useTranslation } from 'react-i18next';
  5. import { SORT_AXIS, SORT_ORDER } from '~/interfaces/search';
  6. import { ISearchConditions, ISearchConfigurations } from '~/stores/search';
  7. import SearchPageForm from './SearchPageForm';
  8. import SearchOptionModal from './SearchOptionModal';
  9. import SortControl from './SortControl';
  10. type Props = {
  11. initialSearchConditions: Partial<ISearchConditions>,
  12. onSearchInvoked: (keyword: string, configurations: Partial<ISearchConfigurations>) => void,
  13. deleteAllControl: React.ReactNode,
  14. }
  15. const SearchControl: FC <Props> = React.memo((props: Props) => {
  16. const {
  17. initialSearchConditions,
  18. onSearchInvoked,
  19. deleteAllControl,
  20. } = props;
  21. const [keyword, setKeyword] = useState(initialSearchConditions.keyword ?? '');
  22. const [sort, setSort] = useState<SORT_AXIS>(initialSearchConditions.sort ?? SORT_AXIS.RELATION_SCORE);
  23. const [order, setOrder] = useState<SORT_ORDER>(initialSearchConditions.order ?? SORT_ORDER.DESC);
  24. const [includeUserPages, setIncludeUserPages] = useState(initialSearchConditions.includeUserPages ?? false);
  25. const [includeTrashPages, setIncludeTrashPages] = useState(initialSearchConditions.includeTrashPages ?? false);
  26. const [isFileterOptionModalShown, setIsFileterOptionModalShown] = useState(false);
  27. const { t } = useTranslation('');
  28. const invokeSearch = useCallback(() => {
  29. if (onSearchInvoked == null) {
  30. return;
  31. }
  32. onSearchInvoked(keyword, {
  33. sort, order, includeUserPages, includeTrashPages,
  34. });
  35. }, [keyword, sort, order, includeTrashPages, includeUserPages, onSearchInvoked]);
  36. const searchFormChangedHandler = useCallback(({ keyword }) => {
  37. setKeyword(keyword as string);
  38. }, []);
  39. const changeSortHandler = useCallback((nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => {
  40. setSort(nextSort);
  41. setOrder(nextOrder);
  42. }, []);
  43. useEffect(() => {
  44. invokeSearch();
  45. }, [invokeSearch]);
  46. return (
  47. <div className="position-sticky fixed-top shadow-sm">
  48. <div className="grw-search-page-nav d-flex py-3 align-items-center">
  49. <div className="flex-grow-1 mx-4">
  50. <SearchPageForm
  51. keyword={keyword}
  52. onSearchFormChanged={searchFormChangedHandler}
  53. />
  54. </div>
  55. {/* sort option: show when screen is larger than lg */}
  56. <div className="mr-4 d-lg-flex d-none">
  57. <SortControl
  58. sort={sort}
  59. order={order}
  60. onChange={changeSortHandler}
  61. />
  62. </div>
  63. </div>
  64. {/* TODO: replace the following elements deleteAll button , relevance button and include specificPath button component */}
  65. <div className="search-control d-flex align-items-center py-md-2 py-3 px-md-4 px-3 border-bottom border-gray">
  66. <div className="d-flex pl-md-2">
  67. {deleteAllControl}
  68. </div>
  69. {/* sort option: show when screen is smaller than lg */}
  70. <div className="mr-md-4 mr-2 d-flex d-lg-none ml-auto">
  71. <SortControl
  72. sort={sort}
  73. order={order}
  74. onChange={changeSortHandler}
  75. />
  76. </div>
  77. {/* filter option */}
  78. <div className="d-lg-none">
  79. <button
  80. type="button"
  81. className="btn"
  82. onClick={() => setIsFileterOptionModalShown(true)}
  83. >
  84. <i className="icon-equalizer"></i>
  85. </button>
  86. </div>
  87. <div className="d-none d-lg-flex align-items-center ml-auto search-control-include-options">
  88. <div className="card mr-3 mb-0">
  89. <div className="card-body">
  90. <label className="search-include-label mb-0 d-flex align-items-center text-secondary with-no-font-weight" htmlFor="flexCheckDefault">
  91. <input
  92. className="mr-2"
  93. type="checkbox"
  94. id="flexCheckDefault"
  95. defaultChecked={includeUserPages}
  96. onChange={e => setIncludeUserPages(e.target.checked)}
  97. />
  98. {t('Include Subordinated Target Page', { target: '/user' })}
  99. </label>
  100. </div>
  101. </div>
  102. <div className="card mb-0">
  103. <div className="card-body">
  104. <label className="search-include-label mb-0 d-flex align-items-center text-secondary with-no-font-weight" htmlFor="flexCheckChecked">
  105. <input
  106. className="mr-2"
  107. type="checkbox"
  108. id="flexCheckChecked"
  109. defaultChecked={includeTrashPages}
  110. onChange={e => setIncludeTrashPages(e.target.checked)}
  111. />
  112. {t('Include Subordinated Target Page', { target: '/trash' })}
  113. </label>
  114. </div>
  115. </div>
  116. </div>
  117. </div>
  118. <SearchOptionModal
  119. isOpen={isFileterOptionModalShown || false}
  120. onClose={() => setIsFileterOptionModalShown(false)}
  121. includeUserPages={includeUserPages}
  122. includeTrashPages={includeTrashPages}
  123. onIncludeUserPagesSwitched={setIncludeUserPages}
  124. onIncludeTrashPagesSwitched={setIncludeTrashPages}
  125. />
  126. </div>
  127. );
  128. });
  129. export default SearchControl;