Explorar o código

Merge pull request #4757 from weseek/feat/79926-add-selectbox-for-paginglimit

Feat/79926 add selectbox for paginglimit
Yohei Shiina %!s(int64=4) %!d(string=hai) anos
pai
achega
b4e563e4e9

+ 4 - 1
packages/app/resource/locales/en_US/translation.json

@@ -576,7 +576,10 @@
     "deletion_modal_header": "Delete page",
     "delete_completely": "Delete completely",
     "include_certain_path" : "Include {{pathToInclude}} path ",
-    "delete_all_selected_page" : "Delete All"
+    "delete_all_selected_page" : "Delete All",
+    "number_of_list_to_display" : "Display",
+    "page_number_unit" : "pages"
+
   },
   "security_setting": {
     "Guest Users Access": "Guest users access",

+ 3 - 1
packages/app/resource/locales/ja_JP/translation.json

@@ -576,7 +576,9 @@
     "deletion_modal_header": "以下のページを削除",
     "delete_completely": "完全に削除する",
     "include_certain_path": "{{pathToInclude}}下を含む ",
-    "delete_all_selected_page" : "一括削除"
+    "delete_all_selected_page" : "一括削除",
+    "number_of_list_to_display" : "表示件数",
+    "page_number_unit" : "件"
   },
   "security_setting": {
     "Guest Users Access": "ゲストユーザーのアクセス",

+ 3 - 1
packages/app/resource/locales/zh_CN/translation.json

@@ -849,7 +849,9 @@
 		"deletion_modal_header": "删除页",
 		"delete_completely": "完全删除",
     "include_certain_path": "包含 {{pathToInclude}} 路径 ",
-    "delete_all_selected_page": "删除所有"
+    "delete_all_selected_page": "删除所有",
+    "number_of_list_to_display" : "显示器的数量",
+    "page_number_unit" : "例"
 	},
 	"to_cloud_settings": "進入 GROWI.cloud 的管理界面",
 	"login": {

+ 14 - 10
packages/app/src/components/SearchPage.jsx

@@ -6,7 +6,6 @@ import { withTranslation } from 'react-i18next';
 
 import { withUnstatedContainers } from './UnstatedUtils';
 import AppContainer from '~/client/services/AppContainer';
-
 import { toastError } from '~/client/util/apiNotification';
 import SearchPageLayout from './SearchPage/SearchPageLayout';
 import SearchResultContent from './SearchPage/SearchResultContent';
@@ -34,19 +33,20 @@ class SearchPage extends React.Component {
       selectedPages: new Set(),
       searchResultCount: 0,
       activePage: 1,
-      pagingLimit: 10, // change to an appropriate limit number
+      pagingLimit: this.props.appContainer.config.pageLimitationL,
       excludeUsersHome: true,
       excludeTrash: true,
     };
 
     this.changeURL = this.changeURL.bind(this);
     this.search = this.search.bind(this);
-    this.searchHandler = this.searchHandler.bind(this);
+    this.onSearchInvoked = this.onSearchInvoked.bind(this);
     this.selectPage = this.selectPage.bind(this);
     this.toggleCheckBox = this.toggleCheckBox.bind(this);
     this.onExcludeUsersHome = this.onExcludeUsersHome.bind(this);
     this.onExcludeTrash = this.onExcludeTrash.bind(this);
     this.onPagingNumberChanged = this.onPagingNumberChanged.bind(this);
+    this.onPagingLimitChanged = this.onPagingLimitChanged.bind(this);
   }
 
   componentDidMount() {
@@ -105,20 +105,23 @@ class SearchPage extends React.Component {
    * this method is called when user changes paging number
    */
   async onPagingNumberChanged(activePage) {
-    // this.setState does not change the state immediately and following calls of this.search outside of this.setState will have old activePage state.
-    // To prevent above, pass this.search as a callback function to make sure this.search will have the latest activePage state.
     this.setState({ activePage }, () => this.search({ keyword: this.state.searchedKeyword }));
   }
 
   /**
    * this method is called when user searches by pressing Enter or using searchbox
    */
-  async searchHandler(data) {
-    // this.setState does not change the state immediately and following calls of this.search outside of this.setState will have old activePage state.
-    // To prevent above, pass this.search as a callback function to make sure this.search will have the latest activePage state.
+  async onSearchInvoked(data) {
     this.setState({ activePage: 1 }, () => this.search(data));
   }
 
+  /**
+   * change number of pages to display per page and execute search method after.
+   */
+  async onPagingLimitChanged(limit) {
+    this.setState({ pagingLimit: limit }, () => this.search({ keyword: this.state.searchedKeyword }));
+  }
+
   async search(data) {
     const keyword = data.keyword;
     if (keyword === '') {
@@ -223,7 +226,7 @@ class SearchPage extends React.Component {
       <SearchControl
         searchingKeyword={this.state.searchingKeyword}
         appContainer={this.props.appContainer}
-        onSearchInvoked={this.searchHandler}
+        onSearchInvoked={this.onSearchInvoked}
         onExcludeUsersHome={this.onExcludeUsersHome}
         onExcludeTrash={this.onExcludeTrash}
       >
@@ -240,6 +243,8 @@ class SearchPage extends React.Component {
           SearchResultContent={this.renderSearchResultContent}
           searchResultMeta={this.state.searchResultMeta}
           searchingKeyword={this.state.searchedKeyword}
+          onPagingLimitChanged={this.onPagingLimitChanged}
+          initialPagingLimit={this.props.appContainer.config.pageLimitationL || 50}
         >
         </SearchPageLayout>
       </div>
@@ -256,7 +261,6 @@ const SearchPageWrapper = withUnstatedContainers(SearchPage, [AppContainer]);
 SearchPage.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-
   query: PropTypes.object,
 };
 SearchPage.defaultProps = {

+ 15 - 3
packages/app/src/components/SearchPage/SearchPageLayout.tsx

@@ -12,7 +12,9 @@ type Props = {
   SearchResultList: React.FunctionComponent,
   SearchResultContent: React.FunctionComponent,
   searchResultMeta: SearchResultMeta,
-  searchingKeyword: string
+  searchingKeyword: string,
+  initialPagingLimit: number,
+  onPagingLimitChanged: (limit: number) => void
 }
 
 const SearchPageLayout: FC<Props> = (props: Props) => {
@@ -27,13 +29,23 @@ const SearchPageLayout: FC<Props> = (props: Props) => {
         <div className="col-lg-6  page-list border boder-gray search-result-list px-0" id="search-result-list">
 
           <nav><SearchControl></SearchControl></nav>
-          <div className="d-flex align-items-start justify-content-between mt-1">
-            <div className="search-result-meta">
+          <div className="d-flex align-items-center justify-content-between mt-1 mb-3">
+            <div className="search-result-meta text-nowrap mr-3">
               <span className="font-weight-light">{t('search_result.result_meta')} </span>
               <span className="h5">{`"${searchingKeyword}"`}</span>
               {/* Todo: replace "1-10" to the appropriate value */}
               <span className="ml-3">1-10 / {searchResultMeta.total || 0}</span>
             </div>
+            <div className="input-group search-result-select-group">
+              <div className="input-group-prepend">
+                <label className="input-group-text text-secondary" htmlFor="inputGroupSelect01">{t('search_result.number_of_list_to_display')}</label>
+              </div>
+              <select className="custom-select" id="inputGroupSelect01" onChange={(e) => { props.onPagingLimitChanged(Number(e.target.value)) }}>
+                {[20, 50, 100, 200].map((limit) => {
+                  return <option selected={limit === props.initialPagingLimit} value={limit}>{limit}{t('search_result.page_number_unit')}</option>;
+                })}
+              </select>
+            </div>
           </div>
 
           <div className="page-list">

+ 1 - 0
packages/app/src/server/models/config.ts

@@ -235,6 +235,7 @@ schema.statics.getLocalconfig = function(crowi) {
     isSearchServiceReachable: crowi.searchService.isReachable,
     isMailerSetup: crowi.mailService.isMailerSetup,
     globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
+    pageLimitationL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
   };
 
   return localConfig;

+ 5 - 2
packages/app/src/styles/_search.scss

@@ -207,10 +207,13 @@
     }
 
     .search-result-meta {
-      margin-bottom: 10px;
       font-weight: bold;
     }
-
+    .search-result-select-group {
+      > select {
+        max-width: 8rem;
+      }
+    }
     .search-result-list-delete-checkbox {
       margin: 0 10px 0 0;
       vertical-align: middle;