PrimaryItems.tsx 3.4 KB

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