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

Replace navtab with reactstrap

satof3 2 лет назад
Родитель
Сommit
88c3432533

+ 39 - 0
apps/app/src/components/CustomNavigation/CustomNavButton.module.scss

@@ -0,0 +1,39 @@
+@use '@growi/core/scss/bootstrap/init' as bs;
+
+@include bs.color-mode(light) {
+  .grw-custom-nav-tab :global {
+    .nav-title {
+      background-color: bs.$gray-100;
+    }
+
+    .active {
+      background-color: bs.$gray-500;
+    }
+    .passive {
+      background-color: white;
+      &:hover {
+        background-color: bs.$gray-100;
+      }
+    }
+  }
+}
+
+
+@include bs.color-mode(dark) {
+  .grw-custom-nav-tab :global {
+    .nav-title {
+      border: 2px solid bs.$gray-800;
+    }
+
+    .active {
+      background-color: bs.$gray-800;
+    }
+    .passive {
+      background-color: bs.$gray-600;
+      &:hover {
+        background-color: rgba(bs.$gray-600, 0.1);
+      }
+    }
+  }
+}
+

+ 74 - 0
apps/app/src/components/CustomNavigation/CustomNavButton.tsx

@@ -0,0 +1,74 @@
+import React, {
+  useRef, useMemo, useCallback,
+} from 'react';
+
+import {
+  Nav, NavItem, NavLink,
+} from 'reactstrap';
+
+import type { ICustomNavTabMappings } from '~/interfaces/ui';
+
+import styles from './CustomNavButton.module.scss';
+
+type CustomNavTabProps = {
+  activeTab: string,
+  navTabMapping: ICustomNavTabMappings,
+  onNavSelected?: (selectedTabKey: string) => void,
+};
+
+export const CustomNavTab = (props: CustomNavTabProps): JSX.Element => {
+
+  const {
+    activeTab, navTabMapping, onNavSelected,
+  } = props;
+
+  const navContainerRef = useRef<HTMLDivElement>(null);
+
+  const navTabRefs: { [key: string]: HTMLAnchorElement } = useMemo(() => {
+    const obj = {};
+    Object.keys(navTabMapping).forEach((key) => {
+      obj[key] = React.createRef();
+    });
+    return obj;
+  }, [navTabMapping]);
+
+  const navLinkClickHandler = useCallback((key) => {
+    if (onNavSelected != null) {
+      onNavSelected(key);
+    }
+  }, [onNavSelected]);
+
+  function registerNavLink(key: string, anchorElem: HTMLAnchorElement | null) {
+    if (anchorElem != null) {
+      navTabRefs[key] = anchorElem;
+    }
+  }
+
+  return (
+    <div className={`grw-custom-nav-tab ${styles['grw-custom-nav-tab']}`}>
+      <div ref={navContainerRef} className="d-flex justify-content-between">
+        <Nav className="nav-title rounded">
+          {Object.entries(navTabMapping).map(([key, value]) => {
+
+            const isActive = activeTab === key;
+            const _isLinkEnabled = value.isLinkEnabled ?? true;
+            const isLinkEnabled = typeof _isLinkEnabled === 'boolean' ? _isLinkEnabled : _isLinkEnabled(value);
+            const { Icon, i18n } = value;
+
+            return (
+              <NavItem
+                key={key}
+                className={`${isActive ? 'active' : 'passive'}`}
+              >
+                <NavLink type="button" key={key} innerRef={elm => registerNavLink(key, elm)} disabled={!isLinkEnabled} onClick={() => navLinkClickHandler(key)}>
+                  { Icon != null && <span className="me-1"><Icon /></span> } {i18n}
+                </NavLink>
+              </NavItem>
+            );
+          })}
+        </Nav>
+      </div>
+    </div>
+  );
+
+};

+ 0 - 41
apps/app/src/components/PageComment/CommentEditor.module.scss

@@ -26,45 +26,4 @@
       padding-top: 0.5em;
     }
   }
-  .comment-write {
-    .nav {
-      --bs-nav-link-padding-x: 12px;
-      --bs-nav-link-padding-y: 6px;
-    }
-  }
-}
-
-@include bs.color-mode(light) {
-  .comment-editor-styles :global {
-    .comment-write {
-      .nav.nav-pills {
-        background-color: white;
-        &:hover {
-          background-color: bs.$gray-100;
-        }
-        border: 2px solid bs.$gray-500;
-        --bs-nav-link-color: #{ bs.$gray-500 };
-        --bs-nav-link-hover-color: #{ bs.$gray-500 };
-        --bs-nav-pills-link-active-bg: #{ bs.$gray-500 };
-      }
-    }
-  }
-}
-
-@include bs.color-mode(dark) {
-  .comment-editor-styles :global {
-    .comment-write {
-      .nav.nav-pills {
-        // background-color: bs.$gray-600;
-        &:hover {
-          background-color: rgba(bs.$gray-600, 0.1);
-        }
-        border: 2px solid bs.$gray-800;
-        --bs-nav-pills-link-active-color: #{ bs.$gray-400 };
-        --bs-nav-link-color: #{ bs.$gray-400 };
-        --bs-nav-link-hover-color: #{ bs.$gray-400 };
-        --bs-nav-pills-link-active-bg: #{ bs.$gray-800 };
-      }
-    }
-  }
 }

+ 28 - 21
apps/app/src/components/PageComment/CommentEditor.tsx

@@ -9,6 +9,9 @@ import { UserPicture } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import { useRouter } from 'next/router';
+import {
+  TabContent, TabPane,
+} from 'reactstrap';
 
 import { apiv3Get, apiv3PostForm } from '~/client/util/apiv3-client';
 import { toastError } from '~/client/util/toastr';
@@ -24,6 +27,7 @@ import { useCurrentPagePath } from '~/stores/page';
 import { useNextThemes } from '~/stores/use-next-themes';
 import loggerFactory from '~/utils/logger';
 
+import { CustomNavTab } from '../CustomNavigation/CustomNavButton';
 import { NotAvailableForGuest } from '../NotAvailableForGuest';
 import { NotAvailableForReadOnlyUser } from '../NotAvailableForReadOnlyUser';
 
@@ -38,6 +42,16 @@ const logger = loggerFactory('growi:components:CommentEditor');
 
 const SlackNotification = dynamic(() => import('../SlackNotification').then(mod => mod.SlackNotification), { ssr: false });
 
+const navTabMapping = {
+  comment_editor: {
+    Icon: () => <span className="material-symbols-outlined">edit_square</span>,
+    i18n: 'Write',
+  },
+  comment_preview: {
+    Icon: () => <span className="material-symbols-outlined">play_arrow</span>,
+    i18n: 'Preview',
+  },
+};
 
 export type CommentEditorProps = {
   pageId: string,
@@ -78,6 +92,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
 
   const [isReadyToUse, setIsReadyToUse] = useState(!isForNewComment);
   const [comment, setComment] = useState(commentBody ?? '');
+  const [activeTab, setActiveTab] = useState('comment_editor');
   const [error, setError] = useState();
   const [slackChannels, setSlackChannels] = useState<string>('');
   const [incremented, setIncremented] = useState(false);
@@ -100,6 +115,10 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
     };
   }, [onRouterChangeComplete, router.events]);
 
+  const handleSelect = useCallback((activeTab: string) => {
+    setActiveTab(activeTab);
+  }, []);
+
   // DO NOT dependent on slackChannelsData directly: https://github.com/weseek/growi/pull/7332
   const slackChannelsDataString = slackChannelsData?.toString();
   const initializeSlackEnabled = useCallback(() => {
@@ -123,6 +142,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
     const editingCommentsNum = comment !== '' ? await decrementEditingCommentsNum() : undefined;
 
     setComment('');
+    setActiveTab('comment_editor');
     setError(undefined);
     initializeSlackEnabled();
     // reset value
@@ -309,29 +329,16 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
 
     return (
       <>
-        <div className="comment-write px-4 pt-3 pb-1">
+        <div className="px-4 pt-3 pb-1">
           <div className="d-flex justify-content-between align-items-center mb-2">
             <div className="d-flex">
               <UserPicture user={currentUser} noLink noTooltip />
               <p className="ms-2 mb-0">Add a comment</p>
             </div>
-            <ul className="nav nav-pills rounded-2">
-              <li className="nav-item">
-                <a className="nav-link rounded-1 rounded-end-0" href="#comment_preview" data-bs-toggle="tab">
-                  <span className="material-symbols-outlined">play_arrow</span>
-                  <small className="d-none d-sm-inline-block">{t('Preview')}</small>
-                </a>
-              </li>
-              <li className="nav-item">
-                <a className="nav-link active rounded-1 rounded-start-0" aria-current="page" href="#comment_editor" data-bs-toggle="tab">
-                  <span className="material-symbols-outlined me-1 fs-5">edit_square</span>
-                  <small className="d-none d-sm-inline-block">{t('Write')}</small>
-                </a>
-              </li>
-            </ul>
+            <CustomNavTab activeTab={activeTab} navTabMapping={navTabMapping} onNavSelected={handleSelect} />
           </div>
-          <div className="tab-content">
-            <div id="comment_editor" className="tab-pane active">
+          <TabContent activeTab={activeTab}>
+            <TabPane tabId="comment_editor">
               <CodeMirrorEditorComment
                 acceptedUploadFileType={acceptedUploadFileType}
                 onChange={onChangeHandler}
@@ -339,13 +346,13 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
                 onUpload={uploadHandler}
                 editorSettings={editorSettings}
               />
-            </div>
-            <div id="comment_preview" className="tab-pane">
+            </TabPane>
+            <TabPane tabId="comment_preview">
               <div className="comment-form-preview">
                 {commentPreview}
               </div>
-            </div>
-          </div>
+            </TabPane>
+          </TabContent>
         </div>
 
         <div className="comment-submit px-4 pb-3 mb-2">