SidebarNav.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import React, {
  2. FC, memo, useCallback,
  3. } from 'react';
  4. import dynamic from 'next/dynamic';
  5. import Link from 'next/link';
  6. import { useUserUISettings } from '~/client/services/user-ui-settings';
  7. import { SidebarContentsType } from '~/interfaces/ui';
  8. import {
  9. useIsAdmin, useGrowiCloudUri, useIsDefaultLogo, useIsGuestUser,
  10. } from '~/stores/context';
  11. import { useCurrentSidebarContents } from '~/stores/ui';
  12. import DrawerToggler from '../Navbar/DrawerToggler';
  13. import { SidebarBrandLogo } from './SidebarBrandLogo';
  14. import styles from './SidebarNav.module.scss';
  15. const PersonalDropdown = dynamic(() => import('./PersonalDropdown').then(mod => mod.PersonalDropdown), { ssr: false });
  16. const InAppNotificationDropdown = dynamic(() => import('../InAppNotification/InAppNotificationDropdown')
  17. .then(mod => mod.InAppNotificationDropdown), { ssr: false });
  18. const AppearanceModeDropdown = dynamic(() => import('./AppearanceModeDropdown').then(mod => mod.AppearanceModeDropdown), { ssr: false });
  19. type PrimaryItemProps = {
  20. contents: SidebarContentsType,
  21. label: string,
  22. iconName: string,
  23. onItemSelected: (contents: SidebarContentsType) => void,
  24. }
  25. const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
  26. const {
  27. contents, label, iconName, onItemSelected,
  28. } = props;
  29. const { data: currentContents, mutate } = useCurrentSidebarContents();
  30. const { scheduleToPut } = useUserUISettings();
  31. const isSelected = contents === currentContents;
  32. const itemSelectedHandler = useCallback(() => {
  33. if (onItemSelected != null) {
  34. onItemSelected(contents);
  35. }
  36. mutate(contents, false);
  37. scheduleToPut({ currentSidebarContents: contents });
  38. }, [contents, mutate, onItemSelected, scheduleToPut]);
  39. const labelForTestId = label.toLowerCase().replace(' ', '-');
  40. return (
  41. <button
  42. type="button"
  43. data-testid={`grw-sidebar-nav-primary-${labelForTestId}`}
  44. className={`d-block btn btn-primary ${isSelected ? 'active' : ''}`}
  45. onClick={itemSelectedHandler}
  46. >
  47. <i className="material-icons">{iconName}</i>
  48. </button>
  49. );
  50. };
  51. type SecondaryItemProps = {
  52. label: string,
  53. href: string,
  54. iconName: string,
  55. isBlank?: boolean,
  56. }
  57. const SecondaryItem: FC<SecondaryItemProps> = memo((props: SecondaryItemProps) => {
  58. const { iconName, href, isBlank } = props;
  59. return (
  60. <Link
  61. href={href}
  62. className="d-block btn btn-primary"
  63. target={`${isBlank ? '_blank' : ''}`}
  64. prefetch={false}
  65. >
  66. <i className="material-icons">{iconName}</i>
  67. </Link>
  68. );
  69. });
  70. SecondaryItem.displayName = 'SecondaryItem';
  71. type Props = {
  72. onItemSelected: (contents: SidebarContentsType) => void,
  73. }
  74. export const SidebarNav: FC<Props> = (props: Props) => {
  75. const { data: isAdmin } = useIsAdmin();
  76. const { data: isGuestUser } = useIsGuestUser();
  77. const { data: growiCloudUri } = useGrowiCloudUri();
  78. const { data: isDefaultLogo } = useIsDefaultLogo();
  79. const { onItemSelected } = props;
  80. const isAuthenticated = isGuestUser === false;
  81. return (
  82. <div className={`grw-sidebar-nav ${styles['grw-sidebar-nav']}`}>
  83. {/* Brand Logo */}
  84. <div className="navbar-brand">
  85. <Link href="/" className="grw-logo d-block">
  86. <SidebarBrandLogo isDefaultLogo={isDefaultLogo} />
  87. </Link>
  88. <DrawerToggler />
  89. </div>
  90. <div className="grw-sidebar-nav-primary-container" data-vrt-blackout-sidebar-nav>
  91. {/* eslint-disable max-len */}
  92. <PrimaryItem contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onItemSelected={onItemSelected} />
  93. <PrimaryItem contents={SidebarContentsType.CUSTOM} label="Custom Sidebar" iconName="code" onItemSelected={onItemSelected} />
  94. <PrimaryItem contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" onItemSelected={onItemSelected} />
  95. <PrimaryItem contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmark" onItemSelected={onItemSelected} />
  96. <PrimaryItem contents={SidebarContentsType.TAG} label="Tags" iconName="local_offer" onItemSelected={onItemSelected} />
  97. <PrimaryItem contents={SidebarContentsType.TRASH} label="Trash" iconName="delete" />
  98. </div>
  99. <div className="grw-sidebar-nav-secondary-container">
  100. <AppearanceModeDropdown isAuthenticated={isAuthenticated} />
  101. <PersonalDropdown />
  102. <InAppNotificationDropdown />
  103. {/* <SecondaryItem label="Draft" iconName="file_copy" href="/me/drafts" /> */}
  104. <SecondaryItem label="Help" iconName="help" href={growiCloudUri != null ? 'https://growi.cloud/help/' : 'https://docs.growi.org'} isBlank />
  105. {isAdmin && <SecondaryItem label="Admin" iconName="settings" href="/admin" />}
  106. </div>
  107. </div>
  108. );
  109. };