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

Redefine the PrimaryItem component as an independent component to avoid circular references

Shun Miyazawa 2 лет назад
Родитель
Сommit
bc58e7a2d6

+ 3 - 11
apps/app/src/components/Sidebar/InAppNotification/PrimaryItemForNotification.tsx

@@ -6,19 +6,16 @@ import { useSWRxInAppNotificationStatus } from '~/stores/in-app-notification';
 import { useDefaultSocket } from '~/stores/socket-io';
 import { useDefaultSocket } from '~/stores/socket-io';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
-// eslint-disable-next-line import/no-cycle
-import { PrimaryItem, type PrimaryItemProps } from '../SidebarNav/PrimaryItems';
-
+import { PrimaryItem, type Props } from '../SidebarNav/PrimaryItem';
 
 
 const logger = loggerFactory('growi:PrimaryItemsForNotification');
 const logger = loggerFactory('growi:PrimaryItemsForNotification');
 
 
-type PrimaryItemForNotification = Omit<PrimaryItemProps, 'onClick' | 'label' | 'iconName' | 'contents' | 'badgeContents' >
+type PrimaryItemForNotificationProps = Omit<Props, 'onClick' | 'label' | 'iconName' | 'contents' | 'badgeContents' >
 
 
 // TODO(after v7 release): https://redmine.weseek.co.jp/issues/138463
 // TODO(after v7 release): https://redmine.weseek.co.jp/issues/138463
-export const PrimaryItemForNotification = memo((props: PrimaryItemForNotification) => {
+export const PrimaryItemForNotification = memo((props: PrimaryItemForNotificationProps) => {
   const { sidebarMode, onHover } = props;
   const { sidebarMode, onHover } = props;
 
 
-
   const { data: socket } = useDefaultSocket();
   const { data: socket } = useDefaultSocket();
 
 
   const { data: notificationCount, mutate: mutateNotificationCount } = useSWRxInAppNotificationStatus();
   const { data: notificationCount, mutate: mutateNotificationCount } = useSWRxInAppNotificationStatus();
@@ -53,11 +50,6 @@ export const PrimaryItemForNotification = memo((props: PrimaryItemForNotificatio
     }
     }
   }, [mutateNotificationCount, socket]);
   }, [mutateNotificationCount, socket]);
 
 
-
-  if (sidebarMode == null) {
-    return <></>;
-  }
-
   return (
   return (
     <PrimaryItem
     <PrimaryItem
       sidebarMode={sidebarMode}
       sidebarMode={sidebarMode}

+ 80 - 0
apps/app/src/components/Sidebar/SidebarNav/PrimaryItem.tsx

@@ -0,0 +1,80 @@
+import { FC, useCallback } from 'react';
+
+import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
+import { useCollapsedContentsOpened, useCurrentSidebarContents } from '~/stores/ui';
+
+
+const useIndicator = (sidebarMode: SidebarMode, isSelected: boolean): string => {
+  const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
+
+  if (sidebarMode === SidebarMode.COLLAPSED && !isCollapsedContentsOpened) {
+    return '';
+  }
+
+  return isSelected ? 'active' : '';
+};
+
+export type Props = {
+  contents: SidebarContentsType,
+  label: string,
+  iconName: string,
+  sidebarMode: SidebarMode,
+  badgeContents?: number,
+  onHover?: (contents: SidebarContentsType) => void,
+  onClick?: () => void,
+}
+
+export const PrimaryItem: FC<Props> = (props: Props) => {
+  const {
+    contents, label, iconName, sidebarMode, badgeContents,
+    onClick, onHover,
+  } = props;
+
+  const { data: currentContents, mutateAndSave: mutateContents } = useCurrentSidebarContents();
+
+  const indicatorClass = useIndicator(sidebarMode, contents === currentContents);
+
+  const selectThisItem = useCallback(() => {
+    mutateContents(contents, false);
+  }, [contents, mutateContents]);
+
+  const itemClickedHandler = useCallback(() => {
+    // do nothing ONLY WHEN the collapse mode
+    if (sidebarMode === SidebarMode.COLLAPSED) {
+      return;
+    }
+
+    selectThisItem();
+    onClick?.();
+  }, [onClick, selectThisItem, sidebarMode]);
+
+  const mouseEnteredHandler = useCallback(() => {
+    // ignore other than collapsed mode
+    if (sidebarMode !== SidebarMode.COLLAPSED) {
+      return;
+    }
+
+    selectThisItem();
+    onHover?.(contents);
+  }, [contents, onHover, selectThisItem, sidebarMode]);
+
+
+  const labelForTestId = label.toLowerCase().replace(' ', '-');
+
+  return (
+    <button
+      type="button"
+      data-testid={`grw-sidebar-nav-primary-${labelForTestId}`}
+      className={`btn btn-primary ${indicatorClass}`}
+      onClick={itemClickedHandler}
+      onMouseEnter={mouseEnteredHandler}
+    >
+      <div className="position-relative">
+        { badgeContents != null && (
+          <span className="position-absolute badge rounded-pill bg-primary">{badgeContents}</span>
+        )}
+        <span className="material-symbols-outlined">{iconName}</span>
+      </div>
+    </button>
+  );
+};

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

@@ -1,98 +1,18 @@
-import {
-  FC, memo, useCallback,
-} from 'react';
+import { memo } from 'react';
 
 
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 
 
-import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
-import { useCollapsedContentsOpened, useCurrentSidebarContents, useSidebarMode } from '~/stores/ui';
+import { SidebarContentsType } from '~/interfaces/ui';
+import { useSidebarMode } from '~/stores/ui';
+
+import { PrimaryItem } from './PrimaryItem';
 
 
 import styles from './PrimaryItems.module.scss';
 import styles from './PrimaryItems.module.scss';
 
 
 const PrimaryItemForNotification = dynamic(
 const PrimaryItemForNotification = dynamic(
-  // eslint-disable-next-line import/no-cycle
   () => import('../InAppNotification/PrimaryItemForNotification').then(mod => mod.PrimaryItemForNotification), { ssr: false },
   () => import('../InAppNotification/PrimaryItemForNotification').then(mod => mod.PrimaryItemForNotification), { ssr: false },
 );
 );
 
 
-/**
- * @returns String for className to switch the indicator is active or not
- */
-const useIndicator = (sidebarMode: SidebarMode, isSelected: boolean): string => {
-  const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
-
-  if (sidebarMode === SidebarMode.COLLAPSED && !isCollapsedContentsOpened) {
-    return '';
-  }
-
-  return isSelected ? 'active' : '';
-};
-
-export type PrimaryItemProps = {
-  contents: SidebarContentsType,
-  label: string,
-  iconName: string,
-  sidebarMode: SidebarMode,
-  badgeContents?: number,
-  onHover?: (contents: SidebarContentsType) => void,
-  onClick?: () => void,
-}
-
-export const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
-  const {
-    contents, label, iconName, sidebarMode, badgeContents,
-    onClick, onHover,
-  } = props;
-
-  const { data: currentContents, mutateAndSave: mutateContents } = useCurrentSidebarContents();
-
-  const indicatorClass = useIndicator(sidebarMode, contents === currentContents);
-
-  const selectThisItem = useCallback(() => {
-    mutateContents(contents, false);
-  }, [contents, mutateContents]);
-
-  const itemClickedHandler = useCallback(() => {
-    // do nothing ONLY WHEN the collapse mode
-    if (sidebarMode === SidebarMode.COLLAPSED) {
-      return;
-    }
-
-    selectThisItem();
-    onClick?.();
-  }, [onClick, selectThisItem, sidebarMode]);
-
-  const mouseEnteredHandler = useCallback(() => {
-    // ignore other than collapsed mode
-    if (sidebarMode !== SidebarMode.COLLAPSED) {
-      return;
-    }
-
-    selectThisItem();
-    onHover?.(contents);
-  }, [contents, onHover, selectThisItem, sidebarMode]);
-
-
-  const labelForTestId = label.toLowerCase().replace(' ', '-');
-
-  return (
-    <button
-      type="button"
-      data-testid={`grw-sidebar-nav-primary-${labelForTestId}`}
-      className={`btn btn-primary ${indicatorClass}`}
-      onClick={itemClickedHandler}
-      onMouseEnter={mouseEnteredHandler}
-    >
-      <div className="position-relative">
-        { badgeContents != null && (
-          <span className="position-absolute badge rounded-pill bg-primary">{badgeContents}</span>
-        )}
-        <span className="material-symbols-outlined">{iconName}</span>
-      </div>
-    </button>
-  );
-};
-
-
 type Props = {
 type Props = {
   onItemHover?: (contents: SidebarContentsType) => void,
   onItemHover?: (contents: SidebarContentsType) => void,
 }
 }