Просмотр исходного кода

Merge pull request #5740 from weseek/feat/add-count-badge-in-toc

feat: Add count badge to Page List button and Comment button
Yuki Takei 4 лет назад
Родитель
Сommit
8a203645bd

+ 17 - 0
packages/app/src/components/Common/CountBadge.tsx

@@ -0,0 +1,17 @@
+import React, { FC } from 'react';
+
+type CountProps = {
+  count: number
+}
+
+const CountBadge: FC<CountProps> = (props:CountProps) => {
+  return (
+    <>
+      <span className="grw-count-badge px-2 badge badge-pill badge-light">
+        {props.count}
+      </span>
+    </>
+  );
+};
+
+export default CountBadge;

+ 16 - 14
packages/app/src/components/Page/DisplaySwitcher.tsx

@@ -1,27 +1,28 @@
 import React, { useMemo } from 'react';
 import React, { useMemo } from 'react';
+
+import { pagePathUtils } from '@growi/core';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { TabContent, TabPane } from 'reactstrap';
 import { TabContent, TabPane } from 'reactstrap';
 
 
-import { pagePathUtils } from '@growi/core';
 
 
-import { EditorMode, useEditorMode } from '~/stores/ui';
-import { useDescendantsPageListModal } from '~/stores/modal';
+import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import {
 import {
   useCurrentPagePath, useIsSharedUser, useIsEditable, useCurrentPageId, useIsUserPage, usePageUser,
   useCurrentPagePath, useIsSharedUser, useIsEditable, useCurrentPageId, useIsUserPage, usePageUser,
 } from '~/stores/context';
 } from '~/stores/context';
+import { useDescendantsPageListModal } from '~/stores/modal';
+import { useSWRxPageByPath } from '~/stores/page';
+import { EditorMode, useEditorMode } from '~/stores/ui';
 
 
-
-import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
-
+import CountBadge from '../Common/CountBadge';
+import ContentLinkButtons from '../ContentLinkButtons';
+import HashChanged from '../EventListeneres/HashChanged';
 import PageListIcon from '../Icons/PageListIcon';
 import PageListIcon from '../Icons/PageListIcon';
-import Editor from '../PageEditor';
 import Page from '../Page';
 import Page from '../Page';
-import UserInfo from '../User/UserInfo';
-import TableOfContents from '../TableOfContents';
-import ContentLinkButtons from '../ContentLinkButtons';
-import PageEditorByHackmd from '../PageEditorByHackmd';
+import Editor from '../PageEditor';
 import EditorNavbarBottom from '../PageEditor/EditorNavbarBottom';
 import EditorNavbarBottom from '../PageEditor/EditorNavbarBottom';
-import HashChanged from '../EventListeneres/HashChanged';
+import PageEditorByHackmd from '../PageEditorByHackmd';
+import TableOfContents from '../TableOfContents';
+import UserInfo from '../User/UserInfo';
 
 
 
 
 const WIKI_HEADER_LINK = 120;
 const WIKI_HEADER_LINK = 120;
@@ -43,6 +44,7 @@ const DisplaySwitcher = (): JSX.Element => {
   const { data: isUserPage } = useIsUserPage();
   const { data: isUserPage } = useIsUserPage();
   const { data: isEditable } = useIsEditable();
   const { data: isEditable } = useIsEditable();
   const { data: pageUser } = usePageUser();
   const { data: pageUser } = usePageUser();
+  const { data: currentPage } = useSWRxPageByPath(currentPath);
 
 
   const { data: editorMode } = useEditorMode();
   const { data: editorMode } = useEditorMode();
 
 
@@ -74,7 +76,7 @@ const DisplaySwitcher = (): JSX.Element => {
                           <PageListIcon />
                           <PageListIcon />
                         </div>
                         </div>
                         {t('page_list')}
                         {t('page_list')}
-                        <span></span> {/* for a count badge */}
+                        {currentPage?.descendantCount != null && <CountBadge count={currentPage.descendantCount + 1} />}
                       </button>
                       </button>
                     ) }
                     ) }
                   </div>
                   </div>
@@ -89,7 +91,7 @@ const DisplaySwitcher = (): JSX.Element => {
                       >
                       >
                         <i className="icon-fw icon-bubbles grw-page-accessories-control-icon"></i>
                         <i className="icon-fw icon-bubbles grw-page-accessories-control-icon"></i>
                         <span>Comments</span>
                         <span>Comments</span>
-                        <span></span> {/* for a count badge */}
+                        {currentPage?.commentCount != null && <CountBadge count={currentPage.commentCount} />}
                       </button>
                       </button>
                     </div>
                     </div>
                   ) }
                   ) }

+ 15 - 28
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -1,32 +1,33 @@
 import React, {
 import React, {
   useCallback, useState, FC, useEffect,
   useCallback, useState, FC, useEffect,
 } from 'react';
 } from 'react';
-import { DropdownToggle } from 'reactstrap';
-import { useTranslation } from 'react-i18next';
-
-import { useDrag, useDrop } from 'react-dnd';
 
 
 import nodePath from 'path';
 import nodePath from 'path';
 
 
 import { pathUtils, pagePathUtils } from '@growi/core';
 import { pathUtils, pagePathUtils } from '@growi/core';
+import { useDrag, useDrop } from 'react-dnd';
+import { useTranslation } from 'react-i18next';
+import { DropdownToggle } from 'reactstrap';
 
 
-import loggerFactory from '~/utils/logger';
 
 
+import { bookmark, unbookmark } from '~/client/services/page-operation';
 import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotification';
 import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotification';
-
-import { useSWRxPageChildren } from '~/stores/page-listing';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
+import TriangleIcon from '~/components/Icons/TriangleIcon';
+import {
+  IPageHasId, IPageInfoAll, IPageToDeleteWithMeta,
+} from '~/interfaces/page';
 import { IPageForPageDuplicateModal } from '~/stores/modal';
 import { IPageForPageDuplicateModal } from '~/stores/modal';
+import { useSWRxPageChildren } from '~/stores/page-listing';
+import { usePageTreeDescCountMap } from '~/stores/ui';
+import loggerFactory from '~/utils/logger';
+
 
 
-import TriangleIcon from '~/components/Icons/TriangleIcon';
-import { bookmark, unbookmark } from '~/client/services/page-operation';
 import ClosableTextInput, { AlertInfo, AlertType } from '../../Common/ClosableTextInput';
 import ClosableTextInput, { AlertInfo, AlertType } from '../../Common/ClosableTextInput';
+import CountBadge from '../../Common/CountBadge';
 import { PageItemControl } from '../../Common/Dropdown/PageItemControl';
 import { PageItemControl } from '../../Common/Dropdown/PageItemControl';
+
 import { ItemNode } from './ItemNode';
 import { ItemNode } from './ItemNode';
-import { usePageTreeDescCountMap } from '~/stores/ui';
-import {
-  IPageHasId, IPageInfoAll, IPageToDeleteWithMeta,
-} from '~/interfaces/page';
 
 
 
 
 const logger = loggerFactory('growi:cli:Item');
 const logger = loggerFactory('growi:cli:Item');
@@ -94,20 +95,6 @@ const isDroppable = (fromPage?: Partial<IPageHasId>, newParentPage?: Partial<IPa
 };
 };
 
 
 
 
-type ItemCountProps = {
-  descendantCount: number
-}
-
-const ItemCount: FC<ItemCountProps> = (props:ItemCountProps) => {
-  return (
-    <>
-      <span className="grw-pagetree-count badge badge-pill badge-light">
-        {props.descendantCount}
-      </span>
-    </>
-  );
-};
-
 const Item: FC<ItemProps> = (props: ItemProps) => {
 const Item: FC<ItemProps> = (props: ItemProps) => {
   const { t } = useTranslation();
   const { t } = useTranslation();
   const {
   const {
@@ -465,7 +452,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
           )}
           )}
         {descendantCount > 0 && !isRenameInputShown && (
         {descendantCount > 0 && !isRenameInputShown && (
           <div className="grw-pagetree-count-wrapper">
           <div className="grw-pagetree-count-wrapper">
-            <ItemCount descendantCount={descendantCount} />
+            <CountBadge count={descendantCount} />
           </div>
           </div>
         )}
         )}
         <div className="grw-pagetree-control d-flex">
         <div className="grw-pagetree-control d-flex">

+ 3 - 3
packages/app/src/stores/page.tsx

@@ -4,11 +4,11 @@ import useSWRImmutable from 'swr/immutable';
 
 
 import { apiv3Get } from '~/client/util/apiv3-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import { HasObjectId } from '~/interfaces/has-object-id';
 import { HasObjectId } from '~/interfaces/has-object-id';
-
 import {
 import {
   IPageInfo, IPageHasId, IPageInfoForOperation, IPageInfoForListing, IDataWithMeta,
   IPageInfo, IPageHasId, IPageInfoForOperation, IPageInfoForListing, IDataWithMeta,
 } from '~/interfaces/page';
 } from '~/interfaces/page';
 import { IPagingResult } from '~/interfaces/paging-result';
 import { IPagingResult } from '~/interfaces/paging-result';
+
 import { apiGet } from '../client/util/apiv1-client';
 import { apiGet } from '../client/util/apiv1-client';
 import { IPageTagsInfo } from '../interfaces/pageTagsInfo';
 import { IPageTagsInfo } from '../interfaces/pageTagsInfo';
 
 
@@ -16,9 +16,9 @@ import { useCurrentPagePath } from './context';
 import { ITermNumberManagerUtil, useTermNumberManager } from './use-static-swr';
 import { ITermNumberManagerUtil, useTermNumberManager } from './use-static-swr';
 
 
 
 
-export const useSWRxPageByPath = (path: string, initialData?: IPageHasId): SWRResponse<IPageHasId, Error> => {
+export const useSWRxPageByPath = (path: string | null | undefined, initialData?: IPageHasId): SWRResponse<IPageHasId, Error> => {
   return useSWR(
   return useSWR(
-    ['/page', path],
+    path != null ? ['/page', path] : null,
     (endpoint, path) => apiv3Get(endpoint, { path }).then(result => result.data.page),
     (endpoint, path) => apiv3Get(endpoint, { path }).then(result => result.data.page),
     {
     {
       fallbackData: initialData,
       fallbackData: initialData,

+ 2 - 2
packages/app/src/styles/_page-tree.scss

@@ -21,7 +21,7 @@ $grw-pagetree-item-padding-left: 10px;
         display: block;
         display: block;
       }
       }
 
 
-      .grw-pagetree-count {
+      .grw-count-badge {
         display: none;
         display: none;
       }
       }
     }
     }
@@ -49,7 +49,7 @@ $grw-pagetree-item-padding-left: 10px;
         display: none;
         display: none;
       }
       }
 
 
-      .grw-pagetree-count {
+      .grw-count-badge {
         min-width: 28px;
         min-width: 28px;
         padding: 0.1rem 0.5rem;
         padding: 0.1rem 0.5rem;
         font-size: 12px;
         font-size: 12px;

+ 6 - 1
packages/app/src/styles/theme/_apply-colors-dark.scss

@@ -287,7 +287,7 @@ ul.pagination {
     .grw-pagetree-triangle-btn {
     .grw-pagetree-triangle-btn {
       @include button-outline-svg-icon-variant($secondary, $gray-200);
       @include button-outline-svg-icon-variant($secondary, $gray-200);
     }
     }
-    .grw-pagetree-count {
+    .grw-count-badge {
       color: $gray-400;
       color: $gray-400;
       background: lighten($bgcolor-sidebar-context, 15%);
       background: lighten($bgcolor-sidebar-context, 15%);
     }
     }
@@ -481,6 +481,11 @@ ul.pagination {
 * grw-side-contents
 * grw-side-contents
 */
 */
 .grw-side-contents-sticky-container {
 .grw-side-contents-sticky-container {
+  .grw-count-badge {
+    color: $gray-400;
+    background: $gray-700;
+  }
+
   .grw-border-vr {
   .grw-border-vr {
     border-color: $border-color-toc;
     border-color: $border-color-toc;
   }
   }

+ 6 - 1
packages/app/src/styles/theme/_apply-colors-light.scss

@@ -192,7 +192,7 @@ $dropdown-link-active-bg: $bgcolor-dropdown-link-active;
     .grw-pagetree-triangle-btn {
     .grw-pagetree-triangle-btn {
       @include button-outline-svg-icon-variant($gray-400, $primary);
       @include button-outline-svg-icon-variant($gray-400, $primary);
     }
     }
-    .grw-pagetree-count {
+    .grw-count-badge {
       color: $gray-500;
       color: $gray-500;
       background: $gray-200;
       background: $gray-200;
     }
     }
@@ -357,6 +357,11 @@ $dropdown-link-active-bg: $bgcolor-dropdown-link-active;
 * grw-side-contents
 * grw-side-contents
 */
 */
 .grw-side-contents-sticky-container {
 .grw-side-contents-sticky-container {
+  .grw-count-badge {
+    color: $primary;
+    background: $gray-200;
+  }
+
   .grw-border-vr {
   .grw-border-vr {
     border-color: $border-color-toc;
     border-color: $border-color-toc;
   }
   }