PrimaryItems.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import { FC, memo, useCallback } from 'react';
  2. import dynamic from 'next/dynamic';
  3. import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
  4. import { useCollapsedContentsOpened, useCurrentSidebarContents, useSidebarMode } from '~/stores/ui';
  5. import styles from './PrimaryItems.module.scss';
  6. const InAppNotificationDropdown = dynamic(() => import('../../InAppNotification/InAppNotificationDropdown')
  7. .then(mod => mod.InAppNotificationDropdown), { ssr: false });
  8. /**
  9. * @returns String for className to switch the indicator is active or not
  10. */
  11. const useIndicator = (sidebarMode: SidebarMode, isSelected: boolean): string => {
  12. const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
  13. if (sidebarMode === SidebarMode.COLLAPSED && !isCollapsedContentsOpened) {
  14. return '';
  15. }
  16. return isSelected ? 'active' : '';
  17. };
  18. type PrimaryItemProps = {
  19. contents: SidebarContentsType,
  20. label: string,
  21. iconName: string,
  22. sidebarMode: SidebarMode,
  23. onHover?: (contents: SidebarContentsType) => void,
  24. }
  25. const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
  26. const {
  27. contents, label, iconName, sidebarMode,
  28. onHover,
  29. } = props;
  30. const { data: currentContents, mutateAndSave: mutateContents } = useCurrentSidebarContents();
  31. const indicatorClass = useIndicator(sidebarMode, contents === currentContents);
  32. const selectThisItem = useCallback(() => {
  33. mutateContents(contents, false);
  34. }, [contents, mutateContents]);
  35. const itemClickedHandler = useCallback(() => {
  36. // do nothing ONLY WHEN the collapse mode
  37. if (sidebarMode === SidebarMode.COLLAPSED) {
  38. return;
  39. }
  40. selectThisItem();
  41. }, [selectThisItem, sidebarMode]);
  42. const mouseEnteredHandler = useCallback(() => {
  43. // ignore other than collapsed mode
  44. if (sidebarMode !== SidebarMode.COLLAPSED) {
  45. return;
  46. }
  47. selectThisItem();
  48. onHover?.(contents);
  49. }, [contents, onHover, selectThisItem, sidebarMode]);
  50. const labelForTestId = label.toLowerCase().replace(' ', '-');
  51. return (
  52. <button
  53. type="button"
  54. data-testid={`grw-sidebar-nav-primary-${labelForTestId}`}
  55. className={`btn btn-primary ${indicatorClass}`}
  56. onClick={itemClickedHandler}
  57. onMouseEnter={mouseEnteredHandler}
  58. >
  59. <span className="material-symbols-outlined">{iconName}</span>
  60. </button>
  61. );
  62. };
  63. type Props = {
  64. onItemHover?: (contents: SidebarContentsType) => void,
  65. }
  66. export const PrimaryItems = memo((props: Props) => {
  67. const { onItemHover } = props;
  68. const { data: sidebarMode } = useSidebarMode();
  69. if (sidebarMode == null) {
  70. return <></>;
  71. }
  72. return (
  73. <div className={styles['grw-primary-items']}>
  74. <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onHover={onItemHover} />
  75. <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.CUSTOM} label="Custom Sidebar" iconName="code" onHover={onItemHover} />
  76. <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" onHover={onItemHover} />
  77. <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmarks" onHover={onItemHover} />
  78. <PrimaryItem sidebarMode={sidebarMode} contents={SidebarContentsType.TAG} label="Tags" iconName="local_offer" onHover={onItemHover} />
  79. <InAppNotificationDropdown />
  80. </div>
  81. );
  82. });