Yuki Takei 4 лет назад
Родитель
Сommit
9e8745f39e

+ 7 - 3
packages/app/src/components/Page/RevisionRenderer.jsx

@@ -62,7 +62,11 @@ class LegacyRevisionRenderer extends React.PureComponent {
    * @param {string} body html strings
    * @param {string} body html strings
    * @param {string} keywords
    * @param {string} keywords
    */
    */
-  getHighlightedBody(body, keywords) {
+  getHighlightedBody(body, _keywords) {
+    const keywords = Array.isArray(_keywords)
+      ? _keywords
+      : [_keywords];
+
     const normalizedKeywordsArray = [];
     const normalizedKeywordsArray = [];
     // !!TODO!!: add test code refs: https://redmine.weseek.co.jp/issues/86841
     // !!TODO!!: add test code refs: https://redmine.weseek.co.jp/issues/86841
     // Separate keywords
     // Separate keywords
@@ -168,7 +172,7 @@ LegacyRevisionRenderer.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   markdown: PropTypes.string.isRequired,
   markdown: PropTypes.string.isRequired,
-  highlightKeywords: PropTypes.string,
+  highlightKeywords: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
   additionalClassName: PropTypes.string,
   additionalClassName: PropTypes.string,
 };
 };
 
 
@@ -186,7 +190,7 @@ const RevisionRenderer = (props) => {
 RevisionRenderer.propTypes = {
 RevisionRenderer.propTypes = {
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   markdown: PropTypes.string.isRequired,
   markdown: PropTypes.string.isRequired,
-  highlightKeywords: PropTypes.arrayOf(PropTypes.string),
+  highlightKeywords: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
   additionalClassName: PropTypes.string,
   additionalClassName: PropTypes.string,
 };
 };
 
 

+ 8 - 4
packages/app/src/components/SearchPage/DeleteSelectedPageGroup.tsx

@@ -1,11 +1,13 @@
-import React, { FC, useEffect, useRef } from 'react';
+import React, {
+  ChangeEvent, FC, useEffect, useRef,
+} from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { IndeterminateInputElement } from '~/interfaces/indeterminate-input-elm';
 import { IndeterminateInputElement } from '~/interfaces/indeterminate-input-elm';
 import { CheckboxType } from '../../interfaces/search';
 import { CheckboxType } from '../../interfaces/search';
 
 
 type Props = {
 type Props = {
-  isSelectAllCheckboxDisabled: boolean,
   selectAllCheckboxType: CheckboxType,
   selectAllCheckboxType: CheckboxType,
+  isSelectAllCheckboxDisabled?: boolean,
   onClickDeleteAllButton?: () => void,
   onClickDeleteAllButton?: () => void,
   onClickSelectAllCheckbox?: (nextSelectAllCheckboxType: CheckboxType) => void,
   onClickSelectAllCheckbox?: (nextSelectAllCheckboxType: CheckboxType) => void,
 }
 }
@@ -16,7 +18,9 @@ const DeleteSelectedPageGroup:FC<Props> = (props:Props) => {
     onClickDeleteAllButton, onClickSelectAllCheckbox, selectAllCheckboxType,
     onClickDeleteAllButton, onClickSelectAllCheckbox, selectAllCheckboxType,
   } = props;
   } = props;
 
 
-  const onClickCheckbox = () => {
+  const checkboxChangedHandler = (e: ChangeEvent<HTMLInputElement>) => {
+    e.preventDefault();
+
     if (onClickSelectAllCheckbox != null) {
     if (onClickSelectAllCheckbox != null) {
       const next = selectAllCheckboxType === CheckboxType.ALL_CHECKED ? CheckboxType.NONE_CHECKED : CheckboxType.ALL_CHECKED;
       const next = selectAllCheckboxType === CheckboxType.ALL_CHECKED ? CheckboxType.NONE_CHECKED : CheckboxType.ALL_CHECKED;
       onClickSelectAllCheckbox(next);
       onClickSelectAllCheckbox(next);
@@ -44,7 +48,7 @@ const DeleteSelectedPageGroup:FC<Props> = (props:Props) => {
         className="grw-indeterminate-checkbox"
         className="grw-indeterminate-checkbox"
         ref={selectAllCheckboxElm}
         ref={selectAllCheckboxElm}
         disabled={props.isSelectAllCheckboxDisabled}
         disabled={props.isSelectAllCheckboxDisabled}
-        onClick={onClickCheckbox}
+        onChange={checkboxChangedHandler}
         checked={selectAllCheckboxType === CheckboxType.ALL_CHECKED}
         checked={selectAllCheckboxType === CheckboxType.ALL_CHECKED}
       />
       />
       <button
       <button

+ 70 - 77
packages/app/src/components/SearchPage/SearchControl.tsx

@@ -1,108 +1,87 @@
-import React, { FC, useState } from 'react';
+import React, { FC, useCallback, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
+
+import { CheckboxType, SORT_AXIS, SORT_ORDER } from '~/interfaces/search';
+import { ISearchConditions, ISearchConfigurations } from '~/stores/search';
+
 import SearchPageForm from './SearchPageForm';
 import SearchPageForm from './SearchPageForm';
-import AppContainer from '../../client/services/AppContainer';
 import DeleteSelectedPageGroup from './DeleteSelectedPageGroup';
 import DeleteSelectedPageGroup from './DeleteSelectedPageGroup';
 import SearchOptionModal from './SearchOptionModal';
 import SearchOptionModal from './SearchOptionModal';
 import SortControl from './SortControl';
 import SortControl from './SortControl';
-import { CheckboxType, SORT_AXIS, SORT_ORDER } from '../../interfaces/search';
 
 
 type Props = {
 type Props = {
-  searchingKeyword: string,
-  sort: SORT_AXIS,
-  order: SORT_ORDER,
-  appContainer: AppContainer,
-  searchResultCount: number,
   selectAllCheckboxType: CheckboxType,
   selectAllCheckboxType: CheckboxType,
+  disableSelectAllbutton?: boolean,
+  initialSearchConditions?: Partial<ISearchConditions>,
   onClickDeleteAllButton?: () => void
   onClickDeleteAllButton?: () => void
   onClickSelectAllCheckbox?: (nextSelectAllCheckboxType: CheckboxType) => void,
   onClickSelectAllCheckbox?: (nextSelectAllCheckboxType: CheckboxType) => void,
-  excludeUserPages: boolean,
-  excludeTrashPages: boolean,
-  onSearchInvoked: (data: {keyword: string}) => boolean,
-  onExcludeUserPagesSwitched?: () => void,
-  onExcludeTrashPagesSwitched?: () => void,
-  onChangeSortInvoked?: (nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => void,
+  onSearchInvoked?: (keyword: string, configurations: Partial<ISearchConfigurations>) => void,
 }
 }
 
 
 const SearchControl: FC <Props> = (props: Props) => {
 const SearchControl: FC <Props> = (props: Props) => {
 
 
+  const {
+    disableSelectAllbutton,
+    initialSearchConditions,
+    onSearchInvoked,
+  } = props;
+
+  const [keyword, setKeyword] = useState(initialSearchConditions?.keyword ?? '');
+  const [sort, setSort] = useState<SORT_AXIS>(initialSearchConditions?.sort ?? SORT_AXIS.RELATION_SCORE);
+  const [order, setOrder] = useState<SORT_ORDER>(initialSearchConditions?.order ?? SORT_ORDER.DESC);
+  const [includeUserPages, setIncludeUserPages] = useState(true);
+  const [includeTrashPages, setIncludeTrashPages] = useState(true);
   const [isFileterOptionModalShown, setIsFileterOptionModalShown] = useState(false);
   const [isFileterOptionModalShown, setIsFileterOptionModalShown] = useState(false);
-  // Temporaly workaround for lint error
-  // later needs to be fixed: SearchControl to typescript componet
-  const SearchPageFormTypeAny : any = SearchPageForm;
+
   const { t } = useTranslation('');
   const { t } = useTranslation('');
-  const { searchResultCount } = props;
 
 
-  const switchExcludeUserPagesHandler = () => {
-    if (props.onExcludeUserPagesSwitched != null) {
-      props.onExcludeUserPagesSwitched();
+  const invokeSearch = useCallback(() => {
+    if (onSearchInvoked == null) {
+      return;
     }
     }
-  };
 
 
-  const switchExcludeTrashPagesHandler = () => {
-    if (props.onExcludeTrashPagesSwitched != null) {
-      props.onExcludeTrashPagesSwitched();
-    }
-  };
+    onSearchInvoked(keyword, {
+      sort, order, includeUserPages, includeTrashPages,
+    });
+  }, [includeTrashPages, includeUserPages, keyword, onSearchInvoked, order, sort]);
 
 
-  const onChangeSortInvoked = (nextSort: SORT_AXIS, nextOrder:SORT_ORDER) => {
-    if (props.onChangeSortInvoked != null) {
-      props.onChangeSortInvoked(nextSort, nextOrder);
-    }
-  };
+  const searchFormChangedHandler = useCallback((keyword: string) => {
+    setKeyword(keyword);
 
 
-  const openSearchOptionModalHandler = () => {
-    setIsFileterOptionModalShown(true);
-  };
+    // invoke search
+    invokeSearch();
+  }, [invokeSearch]);
 
 
-  const closeSearchOptionModalHandler = () => {
-    setIsFileterOptionModalShown(false);
-  };
+  const changeSortHandler = useCallback((nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => {
+    setSort(nextSort);
+    setOrder(nextOrder);
 
 
-  const onRetrySearchInvoked = () => {
-    if (props.onSearchInvoked != null) {
-      props.onSearchInvoked({ keyword: props.searchingKeyword });
-    }
-  };
+    // invoke search
+    invokeSearch();
+  }, [invokeSearch]);
 
 
-  const rednerSearchOptionModal = () => {
-    return (
-      <SearchOptionModal
-        isOpen={isFileterOptionModalShown || false}
-        onClickFilteringSearchResult={onRetrySearchInvoked}
-        onClose={closeSearchOptionModalHandler}
-        onExcludeUserPagesSwitched={switchExcludeUserPagesHandler}
-        onExcludeTrashPagesSwitched={switchExcludeTrashPagesHandler}
-        excludeUserPages={props.excludeUserPages}
-        excludeTrashPages={props.excludeTrashPages}
-      />
-    );
-  };
-
-  const renderSortControl = () => {
-    return (
-      <SortControl
-        sort={props.sort}
-        order={props.order}
-        onChangeSortInvoked={onChangeSortInvoked}
-      />
-    );
-  };
+  const clickSearchBySearchOptionModalHandler = useCallback(() => {
+    // invoke search
+    invokeSearch();
+  }, [invokeSearch]);
 
 
   return (
   return (
     <div className="position-sticky fixed-top shadow-sm">
     <div className="position-sticky fixed-top shadow-sm">
       <div className="grw-search-page-nav d-flex py-3 align-items-center">
       <div className="grw-search-page-nav d-flex py-3 align-items-center">
         <div className="flex-grow-1 mx-4">
         <div className="flex-grow-1 mx-4">
-          <SearchPageFormTypeAny
-            keyword={props.searchingKeyword}
-            appContainer={props.appContainer}
-            onSearchFormChanged={props.onSearchInvoked}
+          <SearchPageForm
+            keyword={keyword}
+            onSearchFormChanged={searchFormChangedHandler}
           />
           />
         </div>
         </div>
 
 
         {/* sort option: show when screen is larger than lg */}
         {/* sort option: show when screen is larger than lg */}
         <div className="mr-4 d-lg-flex d-none">
         <div className="mr-4 d-lg-flex d-none">
-          {renderSortControl()}
+          <SortControl
+            sort={sort}
+            order={order}
+            onChange={changeSortHandler}
+          />
         </div>
         </div>
       </div>
       </div>
       {/* TODO: replace the following elements deleteAll button , relevance button and include specificPath button component */}
       {/* TODO: replace the following elements deleteAll button , relevance button and include specificPath button component */}
@@ -110,7 +89,7 @@ const SearchControl: FC <Props> = (props: Props) => {
         <div className="d-flex pl-md-2">
         <div className="d-flex pl-md-2">
           {/* Todo: design will be fixed in #80324. Function will be implemented in #77525 */}
           {/* Todo: design will be fixed in #80324. Function will be implemented in #77525 */}
           <DeleteSelectedPageGroup
           <DeleteSelectedPageGroup
-            isSelectAllCheckboxDisabled={searchResultCount === 0}
+            isSelectAllCheckboxDisabled={disableSelectAllbutton}
             selectAllCheckboxType={props.selectAllCheckboxType}
             selectAllCheckboxType={props.selectAllCheckboxType}
             onClickDeleteAllButton={props.onClickDeleteAllButton}
             onClickDeleteAllButton={props.onClickDeleteAllButton}
             onClickSelectAllCheckbox={props.onClickSelectAllCheckbox}
             onClickSelectAllCheckbox={props.onClickSelectAllCheckbox}
@@ -118,14 +97,18 @@ const SearchControl: FC <Props> = (props: Props) => {
         </div>
         </div>
         {/* sort option: show when screen is smaller than lg */}
         {/* sort option: show when screen is smaller than lg */}
         <div className="mr-md-4 mr-2 d-flex d-lg-none ml-auto">
         <div className="mr-md-4 mr-2 d-flex d-lg-none ml-auto">
-          {renderSortControl()}
+          <SortControl
+            sort={sort}
+            order={order}
+            onChange={changeSortHandler}
+          />
         </div>
         </div>
         {/* filter option */}
         {/* filter option */}
         <div className="d-lg-none">
         <div className="d-lg-none">
           <button
           <button
             type="button"
             type="button"
             className="btn"
             className="btn"
-            onClick={openSearchOptionModalHandler}
+            onClick={() => setIsFileterOptionModalShown(true)}
           >
           >
             <i className="icon-equalizer"></i>
             <i className="icon-equalizer"></i>
           </button>
           </button>
@@ -138,7 +121,7 @@ const SearchControl: FC <Props> = (props: Props) => {
                   className="mr-2"
                   className="mr-2"
                   type="checkbox"
                   type="checkbox"
                   id="flexCheckDefault"
                   id="flexCheckDefault"
-                  onClick={switchExcludeUserPagesHandler}
+                  onChange={e => setIncludeUserPages(e.target.checked)}
                 />
                 />
                 {t('Include Subordinated Target Page', { target: '/user' })}
                 {t('Include Subordinated Target Page', { target: '/user' })}
               </label>
               </label>
@@ -151,7 +134,7 @@ const SearchControl: FC <Props> = (props: Props) => {
                   className="mr-2"
                   className="mr-2"
                   type="checkbox"
                   type="checkbox"
                   id="flexCheckChecked"
                   id="flexCheckChecked"
-                  onClick={switchExcludeTrashPagesHandler}
+                  onChange={e => setIncludeTrashPages(e.target.checked)}
                 />
                 />
                 {t('Include Subordinated Target Page', { target: '/trash' })}
                 {t('Include Subordinated Target Page', { target: '/trash' })}
               </label>
               </label>
@@ -159,7 +142,17 @@ const SearchControl: FC <Props> = (props: Props) => {
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
-      {rednerSearchOptionModal()}
+
+      <SearchOptionModal
+        isOpen={isFileterOptionModalShown || false}
+        onClose={() => setIsFileterOptionModalShown(false)}
+        includeUserPages={includeUserPages}
+        includeTrashPages={includeTrashPages}
+        onClickSearch={clickSearchBySearchOptionModalHandler}
+        onIncludeUserPagesSwitched={setIncludeUserPages}
+        onIncludeTrashPagesSwitched={setIncludeTrashPages}
+      />
+
     </div>
     </div>
   );
   );
 };
 };

+ 31 - 15
packages/app/src/components/SearchPage/SearchOptionModal.tsx

@@ -8,12 +8,12 @@ import {
 
 
 type Props = {
 type Props = {
   isOpen: boolean,
   isOpen: boolean,
-  excludeUserPages: boolean,
-  excludeTrashPages: boolean,
+  includeUserPages: boolean,
+  includeTrashPages: boolean,
   onClose?: () => void,
   onClose?: () => void,
-  onExcludeUserPagesSwitched?: () => void,
-  onExcludeTrashPagesSwitched?: () => void,
-  onClickFilteringSearchResult?: () => void,
+  onIncludeUserPagesSwitched?: (isChecked: boolean) => void,
+  onIncludeTrashPagesSwitched?: (isChecked: boolean) => void,
+  onClickSearch?: () => void,
 }
 }
 
 
 const SearchOptionModal: FC<Props> = (props: Props) => {
 const SearchOptionModal: FC<Props> = (props: Props) => {
@@ -21,7 +21,11 @@ const SearchOptionModal: FC<Props> = (props: Props) => {
   const { t } = useTranslation('');
   const { t } = useTranslation('');
 
 
   const {
   const {
-    isOpen, onClose, excludeUserPages, excludeTrashPages,
+    isOpen, includeUserPages, includeTrashPages,
+    onClose,
+    onIncludeUserPagesSwitched,
+    onIncludeTrashPagesSwitched,
+    onClickSearch,
   } = props;
   } = props;
 
 
   const onCloseModal = () => {
   const onCloseModal = () => {
@@ -30,13 +34,25 @@ const SearchOptionModal: FC<Props> = (props: Props) => {
     }
     }
   };
   };
 
 
-  const onClickFilteringSearchResult = () => {
-    if (props.onClickFilteringSearchResult != null) {
-      props.onClickFilteringSearchResult();
-      onCloseModal();
+  const includeUserPagesChangeHandler = (isChecked: boolean) => {
+    if (onIncludeUserPagesSwitched != null) {
+      onIncludeUserPagesSwitched(isChecked);
     }
     }
   };
   };
 
 
+  const includeTrashPagesChangeHandler = (isChecked: boolean) => {
+    if (onIncludeTrashPagesSwitched != null) {
+      onIncludeTrashPagesSwitched(isChecked);
+    }
+  };
+
+  const clickSearchAgainHandler = () => {
+    if (onClickSearch != null) {
+      onClickSearch();
+    }
+    onCloseModal();
+  };
+
   return (
   return (
     <Modal size="lg" isOpen={isOpen} toggle={onCloseModal} autoFocus={false}>
     <Modal size="lg" isOpen={isOpen} toggle={onCloseModal} autoFocus={false}>
       <ModalHeader tag="h4" toggle={onCloseModal} className="bg-primary text-light">
       <ModalHeader tag="h4" toggle={onCloseModal} className="bg-primary text-light">
@@ -49,8 +65,8 @@ const SearchOptionModal: FC<Props> = (props: Props) => {
               <input
               <input
                 className="mr-2"
                 className="mr-2"
                 type="checkbox"
                 type="checkbox"
-                onChange={props.onExcludeUserPagesSwitched}
-                checked={!excludeUserPages}
+                onChange={e => includeUserPagesChangeHandler(e.target.checked)}
+                checked={includeUserPages}
               />
               />
               {t('Include Subordinated Target Page', { target: '/user' })}
               {t('Include Subordinated Target Page', { target: '/user' })}
             </label>
             </label>
@@ -60,8 +76,8 @@ const SearchOptionModal: FC<Props> = (props: Props) => {
               <input
               <input
                 className="mr-2"
                 className="mr-2"
                 type="checkbox"
                 type="checkbox"
-                onChange={props.onExcludeTrashPagesSwitched}
-                checked={!excludeTrashPages}
+                onChange={e => includeTrashPagesChangeHandler(e.target.checked)}
+                checked={includeTrashPages}
               />
               />
               {t('Include Subordinated Target Page', { target: '/trash' })}
               {t('Include Subordinated Target Page', { target: '/trash' })}
             </label>
             </label>
@@ -72,7 +88,7 @@ const SearchOptionModal: FC<Props> = (props: Props) => {
         <button
         <button
           type="button"
           type="button"
           className="btn btn-secondary"
           className="btn btn-secondary"
-          onClick={onClickFilteringSearchResult}
+          onClick={clickSearchAgainHandler}
         >{t('search_result.search_again')}
         >{t('search_result.search_again')}
         </button>
         </button>
       </ModalFooter>
       </ModalFooter>

+ 9 - 7
packages/app/src/components/SearchPage/SortControl.tsx

@@ -7,20 +7,22 @@ const { DESC, ASC } = SORT_ORDER;
 type Props = {
 type Props = {
   sort: SORT_AXIS,
   sort: SORT_AXIS,
   order: SORT_ORDER,
   order: SORT_ORDER,
-  onChangeSortInvoked?: (nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => void,
+  onChange?: (nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => void,
 }
 }
 
 
 const SortControl: FC <Props> = (props: Props) => {
 const SortControl: FC <Props> = (props: Props) => {
 
 
   const { t } = useTranslation('');
   const { t } = useTranslation('');
 
 
+  const { sort, order, onChange } = props;
+
   const onClickChangeSort = (nextSortAxis: SORT_AXIS, nextSortOrder: SORT_ORDER) => {
   const onClickChangeSort = (nextSortAxis: SORT_AXIS, nextSortOrder: SORT_ORDER) => {
-    if (props.onChangeSortInvoked != null) {
-      props.onChangeSortInvoked(nextSortAxis, nextSortOrder);
+    if (onChange != null) {
+      onChange(nextSortAxis, nextSortOrder);
     }
     }
   };
   };
 
 
-  const renderOrderIcon = (order: SORT_ORDER) => {
+  const renderOrderIcon = () => {
     const iconClassName = ASC === order ? 'fa fa-sort-amount-asc' : 'fa fa-sort-amount-desc';
     const iconClassName = ASC === order ? 'fa fa-sort-amount-asc' : 'fa fa-sort-amount-desc';
     return <i className={iconClassName} aria-hidden="true" />;
     return <i className={iconClassName} aria-hidden="true" />;
   };
   };
@@ -30,7 +32,7 @@ const SortControl: FC <Props> = (props: Props) => {
       <div className="input-group">
       <div className="input-group">
         <div className="input-group-prepend">
         <div className="input-group-prepend">
           <div className="input-group-text border text-muted" id="btnGroupAddon">
           <div className="input-group-text border text-muted" id="btnGroupAddon">
-            {renderOrderIcon(props.order)}
+            {renderOrderIcon()}
           </div>
           </div>
         </div>
         </div>
         <div className="border rounded-right">
         <div className="border rounded-right">
@@ -39,11 +41,11 @@ const SortControl: FC <Props> = (props: Props) => {
             className="btn dropdown-toggle search-sort-option-btn"
             className="btn dropdown-toggle search-sort-option-btn"
             data-toggle="dropdown"
             data-toggle="dropdown"
           >
           >
-            <span className="mr-4 text-secondary">{t(`search_result.sort_axis.${props.sort}`)}</span>
+            <span className="mr-4 text-secondary">{t(`search_result.sort_axis.${sort}`)}</span>
           </button>
           </button>
           <div className="dropdown-menu dropdown-menu-right">
           <div className="dropdown-menu dropdown-menu-right">
             {Object.values(SORT_AXIS).map((sortAxis) => {
             {Object.values(SORT_AXIS).map((sortAxis) => {
-              const nextOrder = (props.sort !== sortAxis || props.order === ASC) ? DESC : ASC;
+              const nextOrder = (sort !== sortAxis || order === ASC) ? DESC : ASC;
               return (
               return (
                 <button
                 <button
                   key={sortAxis}
                   key={sortAxis}

+ 56 - 9
packages/app/src/components/SearchPage2.tsx

@@ -1,15 +1,20 @@
-import React, { useCallback } from 'react';
+import React, { useCallback, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
 import AppContainer from '~/client/services/AppContainer';
 import AppContainer from '~/client/services/AppContainer';
-import { IFormattedSearchResult } from '~/interfaces/search';
+import { CheckboxType, IFormattedSearchResult } from '~/interfaces/search';
 
 
-import { useSWRxFullTextSearch } from '~/stores/search';
+import { ISearchConditions, ISearchConfigurations, useSWRxFullTextSearch } from '~/stores/search';
 import PaginationWrapper from './PaginationWrapper';
 import PaginationWrapper from './PaginationWrapper';
+import SearchControl from './SearchPage/SearchControl';
 
 
 import SearchPageBase from './SearchPage2/SearchPageBase';
 import SearchPageBase from './SearchPage2/SearchPageBase';
 
 
 
 
+// TODO: replace with "customize:showPageLimitationS"
+const INITIAL_PAGIONG_SIZE = 20;
+
+
 type SearchResultListHeadProps = {
 type SearchResultListHeadProps = {
   searchResult: IFormattedSearchResult,
   searchResult: IFormattedSearchResult,
   searchingKeyword: string,
   searchingKeyword: string,
@@ -61,15 +66,28 @@ type Props = {
   appContainer: AppContainer,
   appContainer: AppContainer,
 }
 }
 
 
+
 export const SearchPage = (props: Props): JSX.Element => {
 export const SearchPage = (props: Props): JSX.Element => {
 
 
   const {
   const {
     appContainer,
     appContainer,
   } = props;
   } = props;
 
 
-  const { data, conditions } = useSWRxFullTextSearch('sand', {
-    limit: 5,
+  const [keyword, setKeyword] = useState<string>('sand');
+  const [searchConfigurations, setSearchConfigurations] = useState<ISearchConfigurations>({
+    limit: INITIAL_PAGIONG_SIZE,
   });
   });
+  const [selectedPagesCount, setSelectedPagesCount] = useState(0);
+
+  const { data, conditions } = useSWRxFullTextSearch(keyword, searchConfigurations);
+
+  const searchInvokedHandler = useCallback((_keyword: string, newConfigurations: Partial<ISearchConfigurations>) => {
+    setKeyword(_keyword);
+    setSearchConfigurations({
+      ...searchConfigurations,
+      ...newConfigurations,
+    });
+  }, [searchConfigurations]);
 
 
   const pagingNumberChangedHandler = useCallback((activePage: number) => {
   const pagingNumberChangedHandler = useCallback((activePage: number) => {
     // TODO implement
     // TODO implement
@@ -79,9 +97,38 @@ export const SearchPage = (props: Props): JSX.Element => {
     <SearchPageBase
     <SearchPageBase
       appContainer={appContainer}
       appContainer={appContainer}
       pages={data?.data}
       pages={data?.data}
-      SearchControl={() => (
-        <></>
-      )}
+      onSelectedPagesByCheckboxesChanged={setSelectedPagesCount}
+      // Components
+      SearchControl={() => {
+        let disableSelectAllbutton = true;
+        let selectAllCheckboxType = CheckboxType.NONE_CHECKED;
+
+        // determine checkbox state
+        if (data != null && data.data.length > 0) {
+          disableSelectAllbutton = false;
+
+          if (selectedPagesCount > 0) {
+            selectAllCheckboxType = data.data.length === selectedPagesCount
+              ? CheckboxType.ALL_CHECKED
+              : CheckboxType.INDETERMINATE;
+          }
+        }
+
+        const initialSearchConditions: Partial<ISearchConditions> = data === null
+          ? { keyword: 'sand' } // TODO get keyword from GET params
+          : { ...conditions };
+
+        return (
+          <SearchControl
+            selectAllCheckboxType={selectAllCheckboxType}
+            disableSelectAllbutton={disableSelectAllbutton}
+            initialSearchConditions={initialSearchConditions}
+            onClickDeleteAllButton={() => null /* TODO implement */}
+            onClickSelectAllCheckbox={() => null /* TODO implement */}
+            onSearchInvoked={searchInvokedHandler}
+          />
+        );
+      }}
       SearchResultListHead={() => {
       SearchResultListHead={() => {
         if (data == null) {
         if (data == null) {
           return <></>;
           return <></>;
@@ -110,7 +157,7 @@ export const SearchPage = (props: Props): JSX.Element => {
           <PaginationWrapper
           <PaginationWrapper
             activePage={Math.floor(offset / limit) + 1}
             activePage={Math.floor(offset / limit) + 1}
             totalItemsCount={total}
             totalItemsCount={total}
-            pagingLimit={limit}
+            pagingLimit={searchConfigurations?.limit}
             changePage={pagingNumberChangedHandler}
             changePage={pagingNumberChangedHandler}
           />
           />
         );
         );

+ 18 - 1
packages/app/src/components/SearchPage2/SearchPageBase.tsx

@@ -12,16 +12,18 @@ type Props = {
 
 
   pages?: IPageWithMeta<IPageSearchMeta>[],
   pages?: IPageWithMeta<IPageSearchMeta>[],
 
 
+  onSelectedPagesByCheckboxesChanged?: (selectedCount: number) => void,
+
   SearchControl: React.FunctionComponent,
   SearchControl: React.FunctionComponent,
   SearchResultListHead: React.FunctionComponent,
   SearchResultListHead: React.FunctionComponent,
   SearchPager: React.FunctionComponent,
   SearchPager: React.FunctionComponent,
-
 }
 }
 
 
 const SearchPageBase: FC<Props> = (props: Props) => {
 const SearchPageBase: FC<Props> = (props: Props) => {
   const {
   const {
     appContainer,
     appContainer,
     pages,
     pages,
+    onSelectedPagesByCheckboxesChanged,
     SearchControl, SearchResultListHead, SearchPager,
     SearchControl, SearchResultListHead, SearchPager,
   } = props;
   } = props;
 
 
@@ -31,8 +33,22 @@ const SearchPageBase: FC<Props> = (props: Props) => {
   // ref: RevisionRenderer
   // ref: RevisionRenderer
   //   [...keywords.match(/"[^"]+"|[^\u{20}\u{3000}]+/ug)].forEach((keyword, i) => {
   //   [...keywords.match(/"[^"]+"|[^\u{20}\u{3000}]+/ug)].forEach((keyword, i) => {
   const [highlightKeywords, setHightlightKeywords] = useState<string[]>([]);
   const [highlightKeywords, setHightlightKeywords] = useState<string[]>([]);
+  const [selectedPageIdsByCheckboxes] = useState<Set<string>>(new Set());
   const [selectedPageWithMeta, setSelectedPageWithMeta] = useState<IPageWithMeta<IPageSearchMeta> | undefined>();
   const [selectedPageWithMeta, setSelectedPageWithMeta] = useState<IPageWithMeta<IPageSearchMeta> | undefined>();
 
 
+  const checkboxClickedHandler = (pageId: string) => {
+    if (selectedPageIdsByCheckboxes.has(pageId)) {
+      selectedPageIdsByCheckboxes.delete(pageId);
+    }
+    else {
+      selectedPageIdsByCheckboxes.add(pageId);
+    }
+
+    if (onSelectedPagesByCheckboxesChanged != null) {
+      onSelectedPagesByCheckboxesChanged(selectedPageIdsByCheckboxes.size);
+    }
+  };
+
   // select first item on load
   // select first item on load
   useEffect(() => {
   useEffect(() => {
     if (selectedPageWithMeta == null && pages != null && pages.length > 0) {
     if (selectedPageWithMeta == null && pages != null && pages.length > 0) {
@@ -68,6 +84,7 @@ const SearchPageBase: FC<Props> = (props: Props) => {
                     pages={pages}
                     pages={pages}
                     selectedPageId={selectedPageWithMeta?.pageData._id}
                     selectedPageId={selectedPageWithMeta?.pageData._id}
                     onPageSelected={page => setSelectedPageWithMeta(page)}
                     onPageSelected={page => setSelectedPageWithMeta(page)}
+                    onClickCheckbox={checkboxClickedHandler}
                   />
                   />
                 </div>
                 </div>
                 <div className="my-4 d-flex justify-content-center">
                 <div className="my-4 d-flex justify-content-center">