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 { PageCreateButton } from './PageCreateButton';
  14. import { SidebarBrandLogo } from './SidebarBrandLogo';
  15. import styles from './SidebarNav.module.scss';
  16. const PersonalDropdown = dynamic(() => import('./PersonalDropdown').then(mod => mod.PersonalDropdown), { ssr: false });
  17. const InAppNotificationDropdown = dynamic(() => import('../InAppNotification/InAppNotificationDropdown')
  18. .then(mod => mod.InAppNotificationDropdown), { ssr: false });
  19. const AppearanceModeDropdown = dynamic(() => import('./AppearanceModeDropdown').then(mod => mod.AppearanceModeDropdown), { ssr: false });
  20. type PrimaryItemProps = {
  21. contents: SidebarContentsType,
  22. label: string,
  23. iconName: string,
  24. onItemSelected: (contents: SidebarContentsType) => void,
  25. }
  26. const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
  27. const {
  28. contents, label, iconName, onItemSelected,
  29. } = props;
  30. const { data: currentContents, mutate } = useCurrentSidebarContents();
  31. const { scheduleToPut } = useUserUISettings();
  32. const isSelected = contents === currentContents;
  33. const itemSelectedHandler = useCallback(() => {
  34. if (onItemSelected != null) {
  35. onItemSelected(contents);
  36. }
  37. mutate(contents, false);
  38. scheduleToPut({ currentSidebarContents: contents });
  39. }, [contents, mutate, onItemSelected, scheduleToPut]);
  40. const labelForTestId = label.toLowerCase().replace(' ', '-');
  41. return (
  42. <button
  43. type="button"
  44. data-testid={`grw-sidebar-nav-primary-${labelForTestId}`}
  45. className={`d-block btn btn-primary ${isSelected ? 'active' : ''}`}
  46. onClick={itemSelectedHandler}
  47. >
  48. <i className="material-icons">{iconName}</i>
  49. </button>
  50. );
  51. };
  52. type SecondaryItemProps = {
  53. label: string,
  54. href: string,
  55. iconName: string,
  56. isBlank?: boolean,
  57. }
  58. const SecondaryItem: FC<SecondaryItemProps> = memo((props: SecondaryItemProps) => {
  59. const { iconName, href, isBlank } = props;
  60. return (
  61. <Link
  62. href={href}
  63. className="d-block btn btn-primary"
  64. target={`${isBlank ? '_blank' : ''}`}
  65. prefetch={false}
  66. >
  67. <i className="material-icons">{iconName}</i>
  68. </Link>
  69. );
  70. });
  71. SecondaryItem.displayName = 'SecondaryItem';
  72. type Props = {
  73. onItemSelected: (contents: SidebarContentsType) => void,
  74. }
  75. export const SidebarNav: FC<Props> = (props: Props) => {
  76. const { data: isAdmin } = useIsAdmin();
  77. const { data: isGuestUser } = useIsGuestUser();
  78. const { data: growiCloudUri } = useGrowiCloudUri();
  79. const { data: isDefaultLogo } = useIsDefaultLogo();
  80. const { onItemSelected } = props;
  81. const isAuthenticated = isGuestUser === false;
  82. return (
  83. <div className={`grw-sidebar-nav ${styles['grw-sidebar-nav']}`}>
  84. {/* Brand Logo */}
  85. <div className="navbar-brand">
  86. <Link href="/" className="grw-logo d-block">
  87. <SidebarBrandLogo isDefaultLogo={isDefaultLogo} />
  88. </Link>
  89. </div>
  90. <PageCreateButton />
  91. <div className="grw-sidebar-nav-primary-container" data-vrt-blackout-sidebar-nav>
  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. <InAppNotificationDropdown />
  98. </div>
  99. <div className="grw-sidebar-nav-secondary-container">
  100. {/* TODO: This setting will be consolidated in "Settings" on My Page, so delete it from here. */}
  101. {/* <AppearanceModeDropdown isAuthenticated={isAuthenticated} /> */}
  102. <PersonalDropdown />
  103. <SecondaryItem label="Help" iconName="help" href={growiCloudUri != null ? 'https://growi.cloud/help/' : 'https://docs.growi.org'} isBlank />
  104. {isAdmin && <SecondaryItem label="Admin" iconName="settings" href="/admin" />}
  105. <SecondaryItem label="Trash" href="/trash" iconName="delete" />
  106. </div>
  107. </div>
  108. );
  109. };