PrimaryItem.tsx 2.6 KB

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