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

+ 0 - 68
packages/app/src/components/SearchPage/DeleteSelectedPageGroup.tsx

@@ -1,68 +0,0 @@
-import React, {
-  ChangeEvent, FC, useEffect, useRef,
-} from 'react';
-import { useTranslation } from 'react-i18next';
-import { IndeterminateInputElement } from '~/interfaces/indeterminate-input-elm';
-import { CheckboxType } from '../../interfaces/search';
-
-type Props = {
-  selectAllCheckboxType: CheckboxType,
-  isSelectAllCheckboxDisabled?: boolean,
-  onClickDeleteAllButton?: () => void,
-  onClickSelectAllCheckbox?: (nextSelectAllCheckboxType: CheckboxType) => void,
-}
-
-const DeleteSelectedPageGroup:FC<Props> = (props:Props) => {
-  const { t } = useTranslation();
-  const {
-    onClickDeleteAllButton, onClickSelectAllCheckbox, selectAllCheckboxType,
-  } = props;
-
-  const checkboxChangedHandler = (e: ChangeEvent<HTMLInputElement>) => {
-    e.preventDefault();
-
-    if (onClickSelectAllCheckbox != null) {
-      const next = selectAllCheckboxType === CheckboxType.ALL_CHECKED ? CheckboxType.NONE_CHECKED : CheckboxType.ALL_CHECKED;
-      onClickSelectAllCheckbox(next);
-    }
-  };
-
-  const onClickDeleteButton = () => {
-    if (onClickDeleteAllButton != null) { onClickDeleteAllButton() }
-  };
-
-  const selectAllCheckboxElm = useRef<IndeterminateInputElement>(null);
-  useEffect(() => {
-    if (selectAllCheckboxElm.current != null) {
-      selectAllCheckboxElm.current.indeterminate = selectAllCheckboxType === CheckboxType.INDETERMINATE;
-    }
-  }, [selectAllCheckboxType]);
-
-  return (
-
-    <div className="d-flex align-items-center">
-      <input
-        id="check-all-pages"
-        type="checkbox"
-        name="check-all-pages"
-        className="grw-indeterminate-checkbox"
-        ref={selectAllCheckboxElm}
-        disabled={props.isSelectAllCheckboxDisabled}
-        onChange={checkboxChangedHandler}
-        checked={selectAllCheckboxType === CheckboxType.ALL_CHECKED}
-      />
-      <button
-        type="button"
-        className="btn text-danger font-weight-light p-0 ml-2"
-        disabled={selectAllCheckboxType === CheckboxType.NONE_CHECKED}
-        onClick={onClickDeleteButton}
-      >
-        <i className="icon-trash"></i>
-        {t('search_result.delete_all_selected_page')}
-      </button>
-    </div>
-  );
-
-};
-
-export default DeleteSelectedPageGroup;

+ 97 - 0
packages/app/src/components/SearchPage/OperateAllControl.tsx

@@ -0,0 +1,97 @@
+import React, {
+  ChangeEvent, FC, useEffect, useMemo, useRef, useState,
+} from 'react';
+import { useTranslation } from 'react-i18next';
+import { IndeterminateInputElement } from '~/interfaces/indeterminate-input-elm';
+import { CheckboxType } from '../../interfaces/search';
+
+
+export type SelectAllHook = {
+  isIndeterminate: boolean,
+  setSelectedCount: (selectedCount: number) => void,
+}
+
+export const useSelectAll = (totalItemsCount: number | undefined): SelectAllHook => {
+  const [selectedCount, setSelectedCount] = useState(0);
+
+  const isIndeterminate = useMemo(() => {
+    return selectedCount > 0 && selectedCount !== totalItemsCount;
+  }, [selectedCount, totalItemsCount]);
+
+  return {
+    isIndeterminate,
+    setSelectedCount,
+  };
+};
+
+
+type Props = {
+  // selectAllCheckboxType: CheckboxType,
+  isSelectedPageCountIndeterminate?: boolean,
+  isSelectAllCheckboxDisabled?: boolean,
+  isDeleteAllButtonDisabled?: boolean,
+  onClickDeleteAllButton?: () => void,
+  onClickSelectAllCheckbox?: (nextSelectAllCheckboxType: CheckboxType) => void,
+}
+
+export const OperateAllControl :FC<Props> = React.memo((props: Props) => {
+  const { t } = useTranslation();
+  const {
+    isSelectedPageCountIndeterminate,
+    isSelectAllCheckboxDisabled,
+    isDeleteAllButtonDisabled,
+    onClickDeleteAllButton, onClickSelectAllCheckbox,
+  } = props;
+
+  const checkboxChangedHandler = (e: ChangeEvent<HTMLInputElement>) => {
+    // e.preventDefault();
+
+    // if (onClickSelectAllCheckbox != null) {
+    //   const next = selectAllCheckboxType === CheckboxType.ALL_CHECKED ? CheckboxType.NONE_CHECKED : CheckboxType.ALL_CHECKED;
+    //   onClickSelectAllCheckbox(next);
+    // }
+  };
+
+  const onClickDeleteButton = () => {
+    if (onClickDeleteAllButton != null) { onClickDeleteAllButton() }
+  };
+
+  const selectAllCheckboxElm = useRef<IndeterminateInputElement>(null);
+  // useEffect(() => {
+  //   if (selectAllCheckboxElm.current != null) {
+  //     selectAllCheckboxElm.current.indeterminate = selectAllCheckboxType === CheckboxType.INDETERMINATE;
+  //   }
+  // }, [selectAllCheckboxType]);
+
+  useEffect(() => {
+    if (selectAllCheckboxElm.current != null && isSelectedPageCountIndeterminate) {
+      selectAllCheckboxElm.current.indeterminate = true;
+    }
+  }, [isSelectedPageCountIndeterminate]);
+
+  return (
+
+    <div className="d-flex align-items-center">
+      <input
+        id="check-all-pages"
+        type="checkbox"
+        name="check-all-pages"
+        className="grw-indeterminate-checkbox"
+        ref={selectAllCheckboxElm}
+        // disabled={props.isSelectAllCheckboxDisabled}
+        // onChange={checkboxChangedHandler}
+        // checked={selectAllCheckboxType === CheckboxType.ALL_CHECKED}
+      />
+      <button
+        type="button"
+        className="btn text-danger font-weight-light p-0 ml-2"
+        disabled={isDeleteAllButtonDisabled}
+        onClick={onClickDeleteButton}
+      >
+        <i className="icon-trash"></i>
+        {t('search_result.delete_all_selected_page')}
+      </button>
+    </div>
+  );
+
+});

+ 12 - 20
packages/app/src/components/SearchPage/SearchControl.tsx

@@ -3,36 +3,34 @@ import React, {
 } from 'react';
 import { useTranslation } from 'react-i18next';
 
-import { CheckboxType, SORT_AXIS, SORT_ORDER } from '~/interfaces/search';
+import { SORT_AXIS, SORT_ORDER } from '~/interfaces/search';
 import { ISearchConditions, ISearchConfigurations } from '~/stores/search';
 
 import SearchPageForm from './SearchPageForm';
-import DeleteSelectedPageGroup from './DeleteSelectedPageGroup';
 import SearchOptionModal from './SearchOptionModal';
 import SortControl from './SortControl';
 
 type Props = {
-  selectAllCheckboxType: CheckboxType,
-  disableSelectAllbutton?: boolean,
-  initialSearchConditions?: Partial<ISearchConditions>,
-  onClickDeleteAllButton?: () => void
-  onClickSelectAllCheckbox?: (nextSelectAllCheckboxType: CheckboxType) => void,
+  initialSearchConditions: Partial<ISearchConditions>,
+
   onSearchInvoked: (keyword: string, configurations: Partial<ISearchConfigurations>) => void,
+
+  deleteAllControl: React.ReactNode,
 }
 
 const SearchControl: FC <Props> = React.memo((props: Props) => {
 
   const {
-    disableSelectAllbutton,
     initialSearchConditions,
     onSearchInvoked,
+    deleteAllControl,
   } = 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(initialSearchConditions?.includeUserPages ?? false);
-  const [includeTrashPages, setIncludeTrashPages] = useState(initialSearchConditions?.includeTrashPages ?? false);
+  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(initialSearchConditions.includeUserPages ?? false);
+  const [includeTrashPages, setIncludeTrashPages] = useState(initialSearchConditions.includeTrashPages ?? false);
   const [isFileterOptionModalShown, setIsFileterOptionModalShown] = useState(false);
 
   const { t } = useTranslation('');
@@ -86,13 +84,7 @@ const SearchControl: FC <Props> = React.memo((props: Props) => {
       {/* TODO: replace the following elements deleteAll button , relevance button and include specificPath button component */}
       <div className="search-control d-flex align-items-center py-md-2 py-3 px-md-4 px-3 border-bottom border-gray">
         <div className="d-flex pl-md-2">
-          {/* Todo: design will be fixed in #80324. Function will be implemented in #77525 */}
-          <DeleteSelectedPageGroup
-            isSelectAllCheckboxDisabled={disableSelectAllbutton}
-            selectAllCheckboxType={props.selectAllCheckboxType}
-            onClickDeleteAllButton={props.onClickDeleteAllButton}
-            onClickSelectAllCheckbox={props.onClickSelectAllCheckbox}
-          />
+          {deleteAllControl}
         </div>
         {/* sort option: show when screen is smaller than lg */}
         <div className="mr-md-4 mr-2 d-flex d-lg-none ml-auto">

+ 76 - 67
packages/app/src/components/SearchPage2.tsx

@@ -1,11 +1,14 @@
-import React, { useCallback, useMemo, useState } from 'react';
+import React, {
+  useCallback, useMemo, useState,
+} from 'react';
 import { useTranslation } from 'react-i18next';
 
 import AppContainer from '~/client/services/AppContainer';
-import { CheckboxType, IFormattedSearchResult } from '~/interfaces/search';
+import { IFormattedSearchResult } from '~/interfaces/search';
 
 import { ISearchConditions, ISearchConfigurations, useSWRxFullTextSearch } from '~/stores/search';
 import PaginationWrapper from './PaginationWrapper';
+import { OperateAllControl, useSelectAll } from './SearchPage/OperateAllControl';
 import SearchControl from './SearchPage/SearchControl';
 
 import SearchPageBase from './SearchPage2/SearchPageBase';
@@ -27,7 +30,7 @@ type SearchResultListHeadProps = {
   onPagingSizeChanged: (size: number) => void,
 }
 
-const SearchResultListHead = (props: SearchResultListHeadProps): JSX.Element => {
+const SearchResultListHead = React.memo((props: SearchResultListHeadProps): JSX.Element => {
   const { t } = useTranslation();
 
   const {
@@ -64,7 +67,7 @@ const SearchResultListHead = (props: SearchResultListHeadProps): JSX.Element =>
       </div>
     </div>
   );
-};
+});
 
 
 /**
@@ -87,7 +90,6 @@ export const SearchPage = (props: Props): JSX.Element => {
   const [configurationsByPagination, setConfigurationsByPagination] = useState<Partial<ISearchConfigurations>>({
     limit: INITIAL_PAGIONG_SIZE,
   });
-  const [selectedPagesCount, setSelectedPagesCount] = useState(0);
 
   const { data, conditions } = useSWRxFullTextSearch(keyword, {
     limit: INITIAL_PAGIONG_SIZE,
@@ -95,6 +97,11 @@ export const SearchPage = (props: Props): JSX.Element => {
     ...configurationsByPagination,
   });
 
+  const {
+    isIndeterminate: isSelectedPageCountIndeterminate,
+    setSelectedCount: setSelectedPagesCount,
+  } = useSelectAll(data?.meta.hitsCount);
+
   const searchInvokedHandler = useCallback((_keyword: string, newConfigurations: Partial<ISearchConfigurations>) => {
     setKeyword(_keyword);
     setConfigurationsByControl(newConfigurations);
@@ -115,74 +122,76 @@ export const SearchPage = (props: Props): JSX.Element => {
     };
   }, []);
 
+  const hitsCount = data?.meta.hitsCount;
+  const { offset, limit } = conditions;
+
+  const deleteAllControl = useMemo(() => {
+    const disableSelectAllbutton = hitsCount === 0;
+
+    return (
+      <OperateAllControl
+        isSelectAllCheckboxDisabled={disableSelectAllbutton}
+        isSelectedPageCountIndeterminate={isSelectedPageCountIndeterminate}
+        onClickDeleteAllButton={() => null /* TODO implement */}
+        onClickSelectAllCheckbox={() => null /* TODO implement */}
+      />
+    );
+  }, [hitsCount, isSelectedPageCountIndeterminate]);
+
+  const searchControl = useMemo(() => {
+    return (
+      <SearchControl
+        initialSearchConditions={initialSearchConditions}
+        onSearchInvoked={searchInvokedHandler}
+        deleteAllControl={deleteAllControl}
+      >
+      </SearchControl>
+    );
+  }, [deleteAllControl, initialSearchConditions, searchInvokedHandler]);
+
+  const searchResultListHead = useMemo(() => {
+    if (data == null) {
+      return <></>;
+    }
+    return (
+      <SearchResultListHead
+        searchResult={data}
+        searchingKeyword={keyword}
+        offset={offset}
+        pagingSize={limit}
+        onPagingSizeChanged={() => {}}
+      />
+    );
+  }, [data, keyword, limit, offset]);
+
+  const searchPager = useMemo(() => {
+    // when pager is not needed
+    if (data == null || data.meta.hitsCount === data.meta.total) {
+      return <></>;
+    }
+
+    const { total } = data.meta;
+    const { offset, limit } = conditions;
+
+    return (
+      <PaginationWrapper
+        activePage={Math.floor(offset / limit) + 1}
+        totalItemsCount={total}
+        pagingLimit={configurationsByPagination?.limit}
+        changePage={pagingNumberChangedHandler}
+      />
+    );
+  }, [conditions, configurationsByPagination?.limit, data, pagingNumberChangedHandler]);
+
   return (
     <SearchPageBase
       appContainer={appContainer}
       pages={data?.data}
       onSelectedPagesByCheckboxesChanged={setSelectedPagesCount}
       // Components
-      SearchControl={useCallback(() => {
-        // 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;
-        //   }
-        // }
-
-        return (
-          <SearchControl
-            // selectAllCheckboxType={selectAllCheckboxType}
-            selectAllCheckboxType={CheckboxType.NONE_CHECKED}
-            // disableSelectAllbutton={disableSelectAllbutton}
-            initialSearchConditions={initialSearchConditions}
-            onClickDeleteAllButton={() => null /* TODO implement */}
-            onClickSelectAllCheckbox={() => null /* TODO implement */}
-            onSearchInvoked={searchInvokedHandler}
-          />
-        );
-      }, [initialSearchConditions, searchInvokedHandler])}
-      SearchResultListHead={useCallback(() => {
-        if (data == null) {
-          return <></>;
-        }
-
-        const { keyword, offset, limit } = conditions;
-
-        return (
-          <SearchResultListHead
-            searchResult={data}
-            searchingKeyword={keyword}
-            offset={offset}
-            pagingSize={limit}
-            onPagingSizeChanged={() => {}}
-          />
-        );
-      }, [conditions, data])}
-      SearchPager={useCallback(() => {
-        // when pager is not needed
-        if (data == null || data.meta.hitsCount === data.meta.total) {
-          return <></>;
-        }
-
-        const { total } = data.meta;
-        const { offset, limit } = conditions;
-
-        return (
-          <PaginationWrapper
-            activePage={Math.floor(offset / limit) + 1}
-            totalItemsCount={total}
-            pagingLimit={configurationsByPagination?.limit}
-            changePage={pagingNumberChangedHandler}
-          />
-        );
-      }, [conditions, configurationsByPagination?.limit, data, pagingNumberChangedHandler])}
+      searchControl={searchControl}
+      searchResultListHead={searchResultListHead}
+      searchPager={searchPager}
     />
   );
 };

+ 7 - 7
packages/app/src/components/SearchPage2/SearchPageBase.tsx

@@ -16,9 +16,9 @@ type Props = {
 
   onSelectedPagesByCheckboxesChanged?: (selectedCount: number) => void,
 
-  SearchControl: React.FunctionComponent,
-  SearchResultListHead: React.FunctionComponent,
-  SearchPager: React.FunctionComponent,
+  searchControl: React.ReactNode,
+  searchResultListHead: React.ReactNode,
+  searchPager: React.ReactNode,
 }
 
 const SearchPageBase: FC<Props> = (props: Props) => {
@@ -26,7 +26,7 @@ const SearchPageBase: FC<Props> = (props: Props) => {
     appContainer,
     pages,
     onSelectedPagesByCheckboxesChanged,
-    SearchControl, SearchResultListHead, SearchPager,
+    searchControl, searchResultListHead, searchPager,
   } = props;
 
   const { data: isGuestUser } = useIsGuestUser();
@@ -66,7 +66,7 @@ const SearchPageBase: FC<Props> = (props: Props) => {
 
         <div className="mw-0 flex-grow-1 flex-basis-0 border boder-gray search-result-list" id="search-result-list">
 
-          <SearchControl></SearchControl>
+          {searchControl}
 
           <div className="search-result-list-scroll">
 
@@ -79,7 +79,7 @@ const SearchPageBase: FC<Props> = (props: Props) => {
             { !isLoading && (
               <>
                 <div className="my-3 px-md-4">
-                  <SearchResultListHead />
+                  {searchResultListHead}
                 </div>
                 <div className="page-list px-md-4">
                   <SearchResultList
@@ -90,7 +90,7 @@ const SearchPageBase: FC<Props> = (props: Props) => {
                   />
                 </div>
                 <div className="my-4 d-flex justify-content-center">
-                  <SearchPager />
+                  {searchPager}
                 </div>
               </>
             ) }