PrimaryItem.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { useCallback } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import { UncontrolledTooltip } from 'reactstrap';
  4. import type { SidebarContentsType } from '~/interfaces/ui';
  5. import { SidebarMode } from '~/interfaces/ui';
  6. import { useCollapsedContentsOpened, useCurrentSidebarContents, useIsMobile } from '~/stores/ui';
  7. const useIndicator = (sidebarMode: SidebarMode, isSelected: boolean): string => {
  8. const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
  9. if (sidebarMode === SidebarMode.COLLAPSED && !isCollapsedContentsOpened) {
  10. return '';
  11. }
  12. return isSelected ? 'active' : '';
  13. };
  14. export type PrimaryItemProps = {
  15. contents: SidebarContentsType,
  16. label: string,
  17. iconName: string,
  18. sidebarMode: SidebarMode,
  19. badgeContents?: number,
  20. onHover?: (contents: SidebarContentsType) => void,
  21. onClick?: () => void,
  22. }
  23. export const PrimaryItem = (props: PrimaryItemProps): JSX.Element => {
  24. const {
  25. contents, label, iconName, sidebarMode, badgeContents,
  26. onClick, onHover,
  27. } = props;
  28. const { data: currentContents, mutateAndSave: mutateContents } = useCurrentSidebarContents();
  29. const indicatorClass = useIndicator(sidebarMode, contents === currentContents);
  30. const { data: isMobile } = useIsMobile();
  31. const { t } = useTranslation();
  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. onClick?.();
  42. }, [onClick, selectThisItem, sidebarMode]);
  43. const mouseEnteredHandler = useCallback(() => {
  44. // ignore other than collapsed mode
  45. if (sidebarMode !== SidebarMode.COLLAPSED) {
  46. return;
  47. }
  48. selectThisItem();
  49. onHover?.(contents);
  50. }, [contents, onHover, selectThisItem, sidebarMode]);
  51. const labelForTestId = label.toLowerCase().replace(' ', '-');
  52. return (
  53. <>
  54. <button
  55. type="button"
  56. data-testid={`grw-sidebar-nav-primary-${labelForTestId}`}
  57. className={`btn btn-primary ${indicatorClass}`}
  58. onClick={itemClickedHandler}
  59. onMouseEnter={mouseEnteredHandler}
  60. id={labelForTestId}
  61. >
  62. <div className="position-relative">
  63. { badgeContents != null && (
  64. <span className="position-absolute badge rounded-pill bg-primary">{badgeContents}</span>
  65. )}
  66. <span className="material-symbols-outlined">{iconName}</span>
  67. </div>
  68. </button>
  69. {
  70. isMobile === false ? (
  71. <UncontrolledTooltip
  72. autohide
  73. placement="right"
  74. target={labelForTestId}
  75. fade={false}
  76. >
  77. {t(label)}
  78. </UncontrolledTooltip>
  79. ) : <></>
  80. }
  81. </>
  82. );
  83. };
  84. PrimaryItem.displayName = 'PrimaryItem';