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

Merge pull request #8710 from weseek/support/144273-view-responsive

imprv: Responsive layout
Yuki Takei 2 лет назад
Родитель
Сommit
18334ec85a

+ 2 - 1
apps/app/package.json

@@ -177,7 +177,7 @@
     "react-markdown": "^8.0.7",
     "react-multiline-clamp": "^2.0.0",
     "react-scroll": "^1.8.7",
-    "react-stickynode": "^4.1.0",
+    "react-stickynode": "^4.1.1",
     "react-syntax-highlighter": "^15.5.0",
     "react-toastify": "^9.1.3",
     "react-use-ripple": "^1.5.2",
@@ -234,6 +234,7 @@
     "@types/express": "^4.17.11",
     "@types/jest": "^29.5.2",
     "@types/react-scroll": "^1.8.4",
+    "@types/react-stickynode": "^4.0.3",
     "@types/throttle-debounce": "^5.0.1",
     "@types/unzip-stream": "^0.3.4",
     "@types/url-join": "^4.0.2",

+ 1 - 0
apps/app/src/components/Common/PagePathNav/PagePathNav.module.scss

@@ -14,6 +14,7 @@
     z-index: bs.$zindex-sticky;
   }
 
+  // TODO:Responsive font size
   // set smaller font-size when sticky
   .sticky-inner-wrapper.active {
     h1 {

+ 1 - 1
apps/app/src/components/Common/PagePathNav/PagePathNav.tsx

@@ -149,7 +149,7 @@ export const PagePathNavSticky = (props: PagePathNavStickyProps): JSX.Element =>
     //  1. disable pointer-events with 'pe-none'
     <div ref={pagePathNavRef}>
       <Sticky className={`${styles['grw-page-path-nav-sticky']} mb-4`} innerClass="mt-1 pe-none" innerActiveClass="active">
-        {({ status }: { status: boolean }) => {
+        {({ status }) => {
           const isCollapseParents = status === Sticky.STATUS_FIXED;
           return (
           // Controlling pointer-events

+ 9 - 0
apps/app/src/components/Navbar/GrowiContextualSubNavigation.module.scss

@@ -1,4 +1,13 @@
 @use '~/styles/mixins';
+@use '@growi/core/scss/bootstrap/init' as bs;
+
+.grw-contextual-sub-navigation {
+  @include bs.media-breakpoint-down(md) {
+    // set min-height to keep the height
+    //  even if the item is empty on the share link page
+    min-height: 46px;
+  }
+}
 
 @include mixins.editing() {
   .grw-contextual-sub-navigation {

+ 68 - 46
apps/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -1,4 +1,5 @@
-import React, { useState, useCallback } from 'react';
+import React, { useState, useCallback, useMemo } from 'react';
+
 
 import { isPopulated } from '@growi/core';
 import type {
@@ -10,6 +11,7 @@ import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import Link from 'next/link';
 import { useRouter } from 'next/router';
+import Sticky from 'react-stickynode';
 import { DropdownItem } from 'reactstrap';
 
 import { useShouldExpandContent } from '~/client/services/layout';
@@ -30,6 +32,7 @@ import { mutatePageTree } from '~/stores/page-listing';
 import {
   useEditorMode, useIsAbleToShowPageManagement,
   useIsAbleToChangeEditorMode,
+  useIsDeviceLargerThanMd,
 } from '~/stores/ui';
 
 import { CreateTemplateModal } from '../CreateTemplateModal';
@@ -194,12 +197,16 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
 
   const { data: isAbleToShowPageManagement } = useIsAbleToShowPageManagement();
   const { data: isAbleToChangeEditorMode } = useIsAbleToChangeEditorMode();
+  const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
 
   const { open: openDuplicateModal } = usePageDuplicateModal();
   const { open: openRenameModal } = usePageRenameModal();
   const { open: openDeleteModal } = usePageDeleteModal();
   const { mutate: mutatePageInfo } = useSWRxPageInfo(pageId);
 
+  const [isStickyActive, setStickyActive] = useState(false);
+
+
   const path = currentPage?.path ?? currentPathname;
   // const grant = currentPage?.grant ?? grantData?.grant;
   // const grantUserGroupId = currentPage?.grantedGroup?._id ?? grantData?.grantedGroup?.id;
@@ -288,53 +295,68 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
     );
   }, [isLinkSharingDisabled, isReadOnlyUser, pageId, revisionId]);
 
+  // hide sub controls when sticky on mobile device
+  const hideSubControls = useMemo(() => {
+    return !isDeviceLargerThanMd && isStickyActive;
+  }, [isDeviceLargerThanMd, isStickyActive]);
+
   return (
     <>
-      <GroundGlassBar
-        className={`${styles['grw-contextual-sub-navigation']}
-          d-flex align-items-center justify-content-end px-2 px-sm-3 px-md-4 py-1 gap-2 gap-md-4 d-print-none
-        `}
-        data-testid="grw-contextual-sub-nav"
-        id="grw-contextual-sub-nav"
-      >
-        {pageId != null && (
-          <PageControls
-            pageId={pageId}
-            revisionId={revisionId}
-            shareLinkId={shareLinkId}
-            path={path ?? currentPathname} // If the page is empty, "path" is undefined
-            expandContentWidth={shouldExpandContent}
-            disableSeenUserInfoPopover={isSharedUser}
-            showPageControlDropdown={isAbleToShowPageManagement}
-            additionalMenuItemRenderer={additionalMenuItemsRenderer}
-            onClickDuplicateMenuItem={duplicateItemClickedHandler}
-            onClickRenameMenuItem={renameItemClickedHandler}
-            onClickDeleteMenuItem={deleteItemClickedHandler}
-            onClickSwitchContentWidth={switchContentWidthHandler}
-          />
-        )}
-
-        {isAbleToChangeEditorMode && (
-          <PageEditorModeManager
-            editorMode={editorMode}
-            isBtnDisabled={!!isGuestUser || !!isReadOnlyUser}
-            path={path}
-            // grant={grant}
-            // grantUserGroupId={grantUserGroupId}
-          />
-        )}
-
-        { isGuestUser && (
-          <div className="mt-2">
-            <Link href="/login#register" className="btn me-2" prefetch={false}>
-              <span className="material-symbols-outlined me-1">person_add</span>{t('Sign up')}
-            </Link>
-            <Link href="/login#login" className="btn btn-primary" prefetch={false}>
-              <span className="material-symbols-outlined me-1">login</span>{t('Sign in')}
-            </Link>
-          </div>
-        ) }
-      </GroundGlassBar>
+      <GroundGlassBar className="py-4 d-block d-md-none d-print-none border-bottom" />
+
+      <Sticky className="z-1" onStateChange={status => setStickyActive(status.status === Sticky.STATUS_FIXED)}>
+        <GroundGlassBar>
+
+          <nav
+            className={`${styles['grw-contextual-sub-navigation']}
+              d-flex align-items-center justify-content-end px-2 px-sm-3 px-md-4 py-1 gap-2 gap-md-4 d-print-none
+            `}
+            data-testid="grw-contextual-sub-nav"
+            id="grw-contextual-sub-nav"
+          >
+
+            {pageId != null && (
+              <PageControls
+                pageId={pageId}
+                revisionId={revisionId}
+                shareLinkId={shareLinkId}
+                path={path ?? currentPathname} // If the page is empty, "path" is undefined
+                expandContentWidth={shouldExpandContent}
+                disableSeenUserInfoPopover={isSharedUser}
+                hideSubControls={hideSubControls}
+                showPageControlDropdown={isAbleToShowPageManagement}
+                additionalMenuItemRenderer={additionalMenuItemsRenderer}
+                onClickDuplicateMenuItem={duplicateItemClickedHandler}
+                onClickRenameMenuItem={renameItemClickedHandler}
+                onClickDeleteMenuItem={deleteItemClickedHandler}
+                onClickSwitchContentWidth={switchContentWidthHandler}
+              />
+            )}
+
+            {isAbleToChangeEditorMode && (
+              <PageEditorModeManager
+                editorMode={editorMode}
+                isBtnDisabled={!!isGuestUser || !!isReadOnlyUser}
+                path={path}
+                // grant={grant}
+                // grantUserGroupId={grantUserGroupId}
+              />
+            )}
+
+            { isGuestUser && (
+              <div className="mt-2">
+                <Link href="/login#register" className="btn me-2" prefetch={false}>
+                  <span className="material-symbols-outlined me-1">person_add</span>{t('Sign up')}
+                </Link>
+                <Link href="/login#login" className="btn btn-primary" prefetch={false}>
+                  <span className="material-symbols-outlined me-1">login</span>{t('Sign in')}
+                </Link>
+              </div>
+            ) }
+          </nav>
+
+        </GroundGlassBar>
+      </Sticky>
 
       {path != null && currentUser != null && !isReadOnlyUser && (
         <CreateTemplateModal

+ 48 - 52
apps/app/src/components/PageControls/PageControls.tsx

@@ -54,10 +54,10 @@ const Tags = (props: TagsProps): JSX.Element => {
     <div className="grw-tag-labels-container d-flex align-items-center">
       <button
         type="button"
-        className="btn btn-link btn-edit-tags text-muted border border-secondary p-1 d-flex align-items-center"
+        className="btn btn-sm btn-outline-neutral-secondary"
         onClick={onClickEditTagsButton}
       >
-        <span className="material-symbols-outlined me-2">local_offer</span>
+        <span className="material-symbols-outlined me-1">local_offer</span>
         Tags
       </button>
     </div>
@@ -99,7 +99,13 @@ const WideViewMenuItem = (props: WideViewMenuItemProps): JSX.Element => {
 
 
 type CommonProps = {
+  pageId: string,
+  shareLinkId?: string | null,
+  revisionId?: string | null,
+  path?: string | null,
+  expandContentWidth?: boolean,
   disableSeenUserInfoPopover?: boolean,
+  hideSubControls?: boolean,
   showPageControlDropdown?: boolean,
   forceHideMenuItems?: ForceHideMenuItems,
   additionalMenuItemRenderer?: React.FunctionComponent<AdditionalMenuItemsRendererProps>,
@@ -110,12 +116,7 @@ type CommonProps = {
 }
 
 type PageControlsSubstanceProps = CommonProps & {
-  pageId: string,
-  shareLinkId?: string | null,
-  revisionId?: string | null,
-  path?: string | null,
   pageInfo: IPageInfoForOperation,
-  expandContentWidth?: boolean,
   onClickEditTagsButton: () => void,
 }
 
@@ -123,7 +124,7 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
   const {
     pageInfo,
     pageId, revisionId, path, shareLinkId, expandContentWidth,
-    disableSeenUserInfoPopover, showPageControlDropdown, forceHideMenuItems, additionalMenuItemRenderer,
+    disableSeenUserInfoPopover, hideSubControls, showPageControlDropdown, forceHideMenuItems, additionalMenuItemRenderer,
     onClickEditTagsButton, onClickDuplicateMenuItem, onClickRenameMenuItem, onClickDeleteMenuItem, onClickSwitchContentWidth,
   } = props;
 
@@ -271,43 +272,50 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
   const isViewMode = editorMode === EditorMode.View;
 
   return (
-    <div className={`grw-page-controls ${styles['grw-page-controls']} d-flex`} style={{ gap: '2px' }} ref={pageControlsRef}>
+    <div className={`${styles['grw-page-controls']} hstack gap-2`} ref={pageControlsRef}>
       { isDeviceLargerThanMd && (
         <SearchButton />
       )}
+
       {revisionId != null && !isViewMode && _isIPageInfoForOperation && (
         <Tags
           onClickEditTagsButton={onClickEditTagsButton}
         />
       )}
-      {revisionId != null && _isIPageInfoForOperation && (
-        <SubscribeButton
-          status={pageInfo.subscriptionStatus}
-          onClick={subscribeClickhandler}
-        />
-      )}
-      {revisionId != null && _isIPageInfoForOperation && (
-        <LikeButtons
-          onLikeClicked={likeClickhandler}
-          sumOfLikers={sumOfLikers}
-          isLiked={isLiked}
-          likers={likers}
-        />
-      )}
-      {revisionId != null && _isIPageInfoForOperation && (
-        <BookmarkButtons
-          pageId={pageId}
-          isBookmarked={pageInfo.isBookmarked}
-          bookmarkCount={pageInfo.bookmarkCount}
-        />
-      )}
-      {revisionId != null && (
-        <SeenUserInfo
-          seenUsers={seenUsers}
-          sumOfSeenUsers={sumOfSeenUsers}
-          disabled={disableSeenUserInfoPopover}
-        />
+
+      { !hideSubControls && (
+        <div className="hstack gap-1">
+          {revisionId != null && _isIPageInfoForOperation && (
+            <SubscribeButton
+              status={pageInfo.subscriptionStatus}
+              onClick={subscribeClickhandler}
+            />
+          )}
+          {revisionId != null && _isIPageInfoForOperation && (
+            <LikeButtons
+              onLikeClicked={likeClickhandler}
+              sumOfLikers={sumOfLikers}
+              isLiked={isLiked}
+              likers={likers}
+            />
+          )}
+          {revisionId != null && _isIPageInfoForOperation && (
+            <BookmarkButtons
+              pageId={pageId}
+              isBookmarked={pageInfo.isBookmarked}
+              bookmarkCount={pageInfo.bookmarkCount}
+            />
+          )}
+          {revisionId != null && (
+            <SeenUserInfo
+              seenUsers={seenUsers}
+              sumOfSeenUsers={sumOfSeenUsers}
+              disabled={disableSeenUserInfoPopover}
+            />
+          ) }
+        </div>
       ) }
+
       { showPageControlDropdown && _isIPageInfoForOperation && (
         <PageItemControl
           pageId={pageId}
@@ -326,18 +334,12 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
   );
 };
 
-type PageControlsProps = CommonProps & {
-  pageId: string,
-  shareLinkId?: string | null,
-  revisionId?: string | null,
-  path?: string | null,
-  expandContentWidth?: boolean,
-};
+type PageControlsProps = CommonProps;
 
 export const PageControls = memo((props: PageControlsProps): JSX.Element => {
   const {
-    pageId, revisionId, path, shareLinkId, expandContentWidth,
-    onClickDuplicateMenuItem, onClickRenameMenuItem, onClickDeleteMenuItem, onClickSwitchContentWidth,
+    pageId, revisionId, shareLinkId,
+    ...rest
   } = props;
 
   const { data: pageInfo, error } = useSWRxPageInfo(pageId ?? null, shareLinkId);
@@ -361,17 +363,11 @@ export const PageControls = memo((props: PageControlsProps): JSX.Element => {
 
   return (
     <PageControlsSubstance
-      {...props}
       pageInfo={pageInfo}
       pageId={pageId}
       revisionId={revisionId}
-      path={path}
       onClickEditTagsButton={onClickEditTagsButton}
-      onClickDuplicateMenuItem={onClickDuplicateMenuItem}
-      onClickRenameMenuItem={onClickRenameMenuItem}
-      onClickDeleteMenuItem={onClickDeleteMenuItem}
-      onClickSwitchContentWidth={onClickSwitchContentWidth}
-      expandContentWidth={expandContentWidth}
+      {...rest}
     />
   );
 });

+ 1 - 1
apps/app/src/components/PageControls/SearchButton.tsx

@@ -17,7 +17,7 @@ const SearchButton = (): JSX.Element => {
   return (
     <button
       type="button"
-      className={`me-3 btn btn-search ${styles['btn-search']}`}
+      className={`btn btn-search ${styles['btn-search']}`}
       onClick={searchButtonClickHandler}
       data-testid="open-search-modal-button"
     >

+ 4 - 1
apps/app/src/components/Sidebar/AppTitle/AppTitle.module.scss

@@ -46,7 +46,10 @@
   $grw-page-editor-mode-manager-width: 90px;
   $grw-contextual-subnavigation-padding-right: 12px;
   $gap: 8px;
-  width: calc(100vw - #{$grw-page-controls-width + $grw-page-editor-mode-manager-width + $grw-contextual-subnavigation-padding-right + $gap * 2});
+
+  @include bs.media-breakpoint-up(sm) {
+    width: calc(100vw - #{$grw-page-controls-width + $grw-page-editor-mode-manager-width + $grw-contextual-subnavigation-padding-right + $gap * 2});
+  }
 
   @include bs.media-breakpoint-up(md) {
     $grw-page-editor-mode-manager-width: 140px;

+ 2 - 3
apps/app/src/pages/[[...path]].page.tsx

@@ -316,9 +316,8 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
         <title>{title}</title>
       </Head>
       <div className="dynamic-layout-root justify-content-between">
-        <nav className="sticky-top">
-          <GrowiContextualSubNavigation isLinkSharingDisabled={props.disableLinkSharing} />
-        </nav>
+
+        <GrowiContextualSubNavigation isLinkSharingDisabled={props.disableLinkSharing} />
 
         <DisplaySwitcher
           pageView={(

+ 3 - 3
apps/app/src/pages/share/[[...path]].page.tsx

@@ -32,6 +32,7 @@ import {
   getServerSideCommonProps, generateCustomTitleForPage, getNextI18NextConfig, skipSSR, addActivity,
 } from '../utils/commons';
 
+
 const logger = loggerFactory('growi:next-page:share');
 
 type Props = CommonProps & {
@@ -120,9 +121,8 @@ const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
       </Head>
 
       <div className="dynamic-layout-root justify-content-between">
-        <nav className="sticky-top">
-          <GrowiContextualSubNavigationForSharedPage page={currentPage ?? props.shareLinkRelatedPage} isLinkSharingDisabled={props.disableLinkSharing} />
-        </nav>
+
+        <GrowiContextualSubNavigationForSharedPage page={currentPage ?? props.shareLinkRelatedPage} isLinkSharingDisabled={props.disableLinkSharing} />
 
         <ShareLinkPageView
           pagePath={pagePath}

+ 11 - 4
yarn.lock

@@ -4229,6 +4229,13 @@
   dependencies:
     "@types/react" "*"
 
+"@types/react-stickynode@^4.0.3":
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/@types/react-stickynode/-/react-stickynode-4.0.3.tgz#77e592cf84590319648678117424f921e5d1b4da"
+  integrity sha512-K7YkwdhXQE4YVxIVweix4nkpdG4onm/dcnKK+qCj0vgUrNiKng+09zOfjF5AlOcC1HQkg5yxVLwp/0AzT84R0w==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react@*", "@types/react@>=16.0.0", "@types/react@>=16.9.11", "@types/react@^18.2.14":
   version "18.2.19"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.19.tgz#f77cb2c8307368e624d464a25b9675fa35f95a8b"
@@ -14804,10 +14811,10 @@ react-scroll@^1.8.7:
     lodash.throttle "^4.1.1"
     prop-types "^15.7.2"
 
-react-stickynode@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/react-stickynode/-/react-stickynode-4.1.0.tgz#ecd80987f64b98f999c589cd4b992eee6bed9562"
-  integrity sha512-zylWgfad75jLfh/gYIayDcDWIDwO4weZrsZqDpjZ/axhF06zRjdCWFBgUr33Pvv2+htKWqPSFksWTyB6aMQ1ZQ==
+react-stickynode@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/react-stickynode/-/react-stickynode-4.1.1.tgz#ea63509a1d83195a7846d8f39be1e4e9ccbf1d3e"
+  integrity sha512-+Xp3xantrxbFjqNiSbpvsZwCqZYiPq0njKTA+QsIZdmEHih1H/lOV9/LpS37d+v92iSydJJTZMeRaENWeqGeIA==
   dependencies:
     classnames "^2.0.0"
     core-js "^3.6.5"