Yuki Takei 2 лет назад
Родитель
Сommit
d507b08779

+ 0 - 1
apps/app/src/components/Sidebar/ResizableArea/ResizableArea.module.scss

@@ -1,5 +1,4 @@
 .grw-resizable-area :global {
 .grw-resizable-area :global {
-  position: relative;
   will-change: width;
   will-change: width;
 }
 }
 
 

+ 40 - 26
apps/app/src/components/Sidebar/Sidebar.module.scss

@@ -2,6 +2,7 @@
 
 
 @use '~/styles/mixins';
 @use '~/styles/mixins';
 
 
+@use './variables' as var;
 
 
 .grw-sidebar :global {
 .grw-sidebar :global {
   top: 0;
   top: 0;
@@ -16,44 +17,57 @@
       font-size: 18px;
       font-size: 18px;
     }
     }
   }
   }
-}
-
 
 
-// Dock Mode
-@mixin dock() {
-  // sticky
-  position: sticky;
+  .sidebar-contents-container {
+    backdrop-filter: blur(20px);
+  }
 }
 }
 
 
-// Drawer Mode
-@mixin drawer() {
-  position: fixed;
-  z-index: bs.$zindex-fixed + 2;
-  width: 348px;
-
-  // apply transition
-  transition-property: transform;
-  @include mixins.apply-navigation-transition();
 
 
-  &:not(.open) {
-    transform: translateX(-100%);
-  }
-  &.open {
-    transform: translateX(0);
+// Dock Mode
+.grw-sidebar {
+  &:global {
+    &.grw-sidebar-dock {
+      position: sticky;
+    }
   }
   }
+}
 
 
-  .sidebar-contents-container {
-    backdrop-filter: blur(20px);
+// Collapsed Mode
+.grw-sidebar {
+  &:global {
+    &.grw-sidebar-collapsed {
+      position: sticky;
+
+      &.open {
+        .sidebar-contents-container {
+          position: absolute;
+          left: var.$grw-sidebar-nav-width;
+          max-height: calc(100vh - var.$grw-sidebar-nav-width * 2);
+        }
+      }
+    }
   }
   }
 }
 }
 
 
+// Drawer Mode
 .grw-sidebar {
 .grw-sidebar {
   &:global {
   &:global {
     &.grw-sidebar-drawer {
     &.grw-sidebar-drawer {
-      @include drawer();
-    }
-    &.grw-sidebar-dock {
-      @include dock();
+      position: fixed;
+      z-index: bs.$zindex-fixed + 2;
+      width: 348px;
+
+      // apply transition
+      transition-property: transform;
+      @include mixins.apply-navigation-transition();
+
+      &:not(.open) {
+        transform: translateX(-100%);
+      }
+      &.open {
+        transform: translateX(0);
+      }
     }
     }
   }
   }
 }
 }

+ 33 - 6
apps/app/src/components/Sidebar/Sidebar.tsx

@@ -69,6 +69,26 @@ export const SidebarSubstance = memo((): JSX.Element => {
     scheduleToPut({ preferCollapsedModeByUser: true });
     scheduleToPut({ preferCollapsedModeByUser: true });
   }, [mutateCollapsedContentsOpened, mutateCollapsedMode]);
   }, [mutateCollapsedContentsOpened, mutateCollapsedMode]);
 
 
+  // open menu when collapsed mode
+  const primaryItemHoverHandler = useCallback(() => {
+    // reject other than collapsed mode
+    if (!isCollapsedMode) {
+      return;
+    }
+
+    mutateCollapsedContentsOpened(true);
+  }, [isCollapsedMode, mutateCollapsedContentsOpened]);
+
+  // close menu when collapsed mode
+  const mouseLeaveHandler = useCallback(() => {
+    // reject other than collapsed mode
+    if (!isCollapsedMode) {
+      return;
+    }
+
+    mutateCollapsedContentsOpened(false);
+  }, [isCollapsedMode, mutateCollapsedContentsOpened]);
+
   useEffect(() => {
   useEffect(() => {
     toggleDrawerMode(isDrawerMode);
     toggleDrawerMode(isDrawerMode);
   }, [isDrawerMode, toggleDrawerMode]);
   }, [isDrawerMode, toggleDrawerMode]);
@@ -100,8 +120,8 @@ export const SidebarSubstance = memo((): JSX.Element => {
       onCollapsed={collapsedByResizableAreaHandler}
       onCollapsed={collapsedByResizableAreaHandler}
     >
     >
       <SidebarHead />
       <SidebarHead />
-      <div className="grw-sidebar-inner flex-expand-horiz">
-        <SidebarNav />
+      <div className="grw-sidebar-inner flex-expand-horiz" onMouseLeave={mouseLeaveHandler}>
+        <SidebarNav onPrimaryItemHover={primaryItemHoverHandler} />
         <div className="sidebar-contents-container flex-grow-1 overflow-y-auto">
         <div className="sidebar-contents-container flex-grow-1 overflow-y-auto">
           <SidebarContents />
           <SidebarContents />
         </div>
         </div>
@@ -115,14 +135,21 @@ export const Sidebar = (): JSX.Element => {
 
 
   const { data: isDrawerMode } = useDrawerMode();
   const { data: isDrawerMode } = useDrawerMode();
   const { data: isDrawerOpened } = useDrawerOpened();
   const { data: isDrawerOpened } = useDrawerOpened();
+  const { data: isCollapsedMode } = useCollapsedMode();
+  const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
 
 
   // css styles
   // css styles
-  const grwSidebarClass = `grw-sidebar ${styles['grw-sidebar']}`;
-  const sidebarModeClass = `${isDrawerMode ? 'grw-sidebar-drawer' : 'grw-sidebar-dock'}`;
-  const isOpenClass = `${isDrawerOpened ? 'open' : ''}`;
+  const grwSidebarClass = styles['grw-sidebar'];
+  // eslint-disable-next-line no-nested-ternary
+  const modeClass = isDrawerMode
+    ? 'grw-sidebar-drawer'
+    : isCollapsedMode
+      ? 'grw-sidebar-collapsed'
+      : 'grw-sidebar-dock';
+  const openClass = `${isDrawerOpened || isCollapsedContentsOpened ? 'open' : ''}`;
 
 
   return (
   return (
-    <div className={`${grwSidebarClass} ${sidebarModeClass} ${isOpenClass} vh-100`} data-testid="grw-sidebar">
+    <div className={`${grwSidebarClass} ${modeClass} ${openClass} vh-100`} data-testid="grw-sidebar">
       <SidebarSubstance />
       <SidebarSubstance />
     </div>
     </div>
   );
   );

+ 19 - 21
apps/app/src/components/Sidebar/SidebarNav/PrimaryItems.tsx

@@ -5,7 +5,7 @@ import dynamic from 'next/dynamic';
 import { scheduleToPut } from '~/client/services/user-ui-settings';
 import { scheduleToPut } from '~/client/services/user-ui-settings';
 import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
 import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
 import {
 import {
-  useCurrentSidebarContents, useCollapsedMode, useDrawerMode, useCollapsedContentsOpened,
+  useCurrentSidebarContents, useCollapsedMode, useDrawerMode,
 } from '~/stores/ui';
 } from '~/stores/ui';
 
 
 import styles from './PrimaryItems.module.scss';
 import styles from './PrimaryItems.module.scss';
@@ -20,15 +20,16 @@ type PrimaryItemProps = {
   label: string,
   label: string,
   iconName: string,
   iconName: string,
   sidebarMode: SidebarMode,
   sidebarMode: SidebarMode,
+  onHover?: (contents: SidebarContentsType) => void,
 }
 }
 
 
 const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
 const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
   const {
   const {
     contents, label, iconName, sidebarMode,
     contents, label, iconName, sidebarMode,
+    onHover,
   } = props;
   } = props;
 
 
   const { data: currentContents, mutate: mutateContents } = useCurrentSidebarContents();
   const { data: currentContents, mutate: mutateContents } = useCurrentSidebarContents();
-  const { mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened();
 
 
   const isSelected = contents === currentContents;
   const isSelected = contents === currentContents;
 
 
@@ -47,23 +48,14 @@ const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
   }, [selectThisItem, sidebarMode]);
   }, [selectThisItem, sidebarMode]);
 
 
   const mouseEnteredHandler = useCallback(() => {
   const mouseEnteredHandler = useCallback(() => {
-    // do nothing BUT the collapsed mode
+    // ignore other than collapsed mode
     if (sidebarMode !== SidebarMode.COLLAPSED) {
     if (sidebarMode !== SidebarMode.COLLAPSED) {
       return;
       return;
     }
     }
 
 
     selectThisItem();
     selectThisItem();
-    mutateCollapsedContentsOpened(true);
-  }, [mutateCollapsedContentsOpened, selectThisItem, sidebarMode]);
-
-  const mouseLeavedHandler = useCallback(() => {
-    // do nothing BUT the collapsed mode
-    if (sidebarMode !== SidebarMode.COLLAPSED) {
-      return;
-    }
-
-    mutateCollapsedContentsOpened(false);
-  }, [mutateCollapsedContentsOpened, sidebarMode]);
+    onHover?.(contents);
+  }, [contents, onHover, selectThisItem, sidebarMode]);
 
 
 
 
   const labelForTestId = label.toLowerCase().replace(' ', '-');
   const labelForTestId = label.toLowerCase().replace(' ', '-');
@@ -75,14 +67,20 @@ const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
       className={`d-block btn btn-primary ${isSelected ? 'active' : ''}`}
       className={`d-block btn btn-primary ${isSelected ? 'active' : ''}`}
       onClick={itemClickedHandler}
       onClick={itemClickedHandler}
       onMouseEnter={mouseEnteredHandler}
       onMouseEnter={mouseEnteredHandler}
-      onMouseLeave={mouseLeavedHandler}
     >
     >
       <i className="material-icons">{iconName}</i>
       <i className="material-icons">{iconName}</i>
     </button>
     </button>
   );
   );
 };
 };
 
 
-export const PrimaryItems: FC = memo(() => {
+
+type Props = {
+  onItemHover?: (contents: SidebarContentsType) => void,
+}
+
+export const PrimaryItems = memo((props: Props) => {
+  const { onItemHover } = props;
+
   const { data: isCollapsedMode } = useCollapsedMode();
   const { data: isCollapsedMode } = useCollapsedMode();
   const { data: isDrawerMode } = useDrawerMode();
   const { data: isDrawerMode } = useDrawerMode();
 
 
@@ -93,11 +91,11 @@ export const PrimaryItems: FC = memo(() => {
 
 
   return (
   return (
     <div className={styles['grw-primary-items']}>
     <div className={styles['grw-primary-items']}>
-      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" />
-      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.CUSTOM} label="Custom Sidebar" iconName="code" />
-      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" />
-      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmark" />
-      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.TAG} label="Tags" iconName="local_offer" />
+      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onHover={onItemHover} />
+      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.CUSTOM} label="Custom Sidebar" iconName="code" onHover={onItemHover} />
+      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" onHover={onItemHover} />
+      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmark" onHover={onItemHover} />
+      <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.TAG} label="Tags" iconName="local_offer" onHover={onItemHover} />
       <InAppNotificationDropdown />
       <InAppNotificationDropdown />
     </div>
     </div>
   );
   );

+ 10 - 5
apps/app/src/components/Sidebar/SidebarNav/SidebarNav.tsx

@@ -1,6 +1,6 @@
-import React, {
-  FC, memo,
-} from 'react';
+import React, { memo } from 'react';
+
+import { SidebarContentsType } from '~/interfaces/ui';
 
 
 import { PageCreateButton } from '../PageCreateButton';
 import { PageCreateButton } from '../PageCreateButton';
 
 
@@ -9,14 +9,19 @@ import { SecondaryItems } from './SecondaryItems';
 
 
 import styles from './SidebarNav.module.scss';
 import styles from './SidebarNav.module.scss';
 
 
+type Props = {
+  onPrimaryItemHover?: (contents: SidebarContentsType) => void,
+}
+
+export const SidebarNav = memo((props: Props) => {
+  const { onPrimaryItemHover } = props;
 
 
-export const SidebarNav: FC = memo(() => {
   return (
   return (
     <div className={`grw-sidebar-nav ${styles['grw-sidebar-nav']}`}>
     <div className={`grw-sidebar-nav ${styles['grw-sidebar-nav']}`}>
       <PageCreateButton />
       <PageCreateButton />
 
 
       <div className="grw-sidebar-nav-primary-container" data-vrt-blackout-sidebar-nav>
       <div className="grw-sidebar-nav-primary-container" data-vrt-blackout-sidebar-nav>
-        <PrimaryItems />
+        <PrimaryItems onItemHover={onPrimaryItemHover} />
       </div>
       </div>
       <div className="grw-sidebar-nav-secondary-container">
       <div className="grw-sidebar-nav-secondary-container">
         <SecondaryItems />
         <SecondaryItems />