Coursemology/coursemology2

View on GitHub
client/app/bundles/course/container/Sidebar/SidebarItem.tsx

Summary

Maintainability
A
2 hrs
Test Coverage
import { useLayoutEffect, useRef } from 'react';
import { defineMessages } from 'react-intl';
import { Link, useLocation } from 'react-router-dom';
import { Badge, Typography } from '@mui/material';
import { SidebarItemData } from 'types/course/courses';

import { defensivelyGetIcon } from 'lib/constants/icons';
import { useUnreadCountForItem } from 'lib/hooks/unread';
import useTranslation from 'lib/hooks/useTranslation';

interface SidebarItemProps {
  of: SidebarItemData;
  square?: boolean;
  exact?: boolean;
  activePath?: string;
}

const SidebarItem = (props: SidebarItemProps): JSX.Element => {
  const { of: item, square, exact, activePath } = props;

  const location = useLocation();
  const activeUrl = activePath ?? location.pathname + location.search;

  const isActive = exact
    ? activeUrl === item.path
    : activeUrl.startsWith(item.path);

  const Icon = defensivelyGetIcon(item.icon, isActive ? 'filled' : 'outlined');

  const ref = useRef<HTMLAnchorElement>(null);

  useLayoutEffect(() => {
    if (!isActive) return;

    ref.current?.scrollIntoView({ behavior: 'auto', block: 'nearest' });
  }, [isActive]);

  const unreadCount = useUnreadCountForItem(item.key);

  return (
    <Link
      ref={ref}
      className={`no-underline ${isActive ? 'text-primary' : 'text-inherit'}`}
      to={item.path}
    >
      <div
        className={`flex select-none items-center space-x-5 p-4 transition-transform active:scale-95 active:rounded-xl ${
          !square ? 'rounded-xl' : ''
        } ${
          isActive
            ? 'bg-primary/10 hover:bg-primary/20 active:bg-primary/30'
            : 'hover:bg-neutral-200 active:bg-neutral-300'
        }`}
        role="button"
      >
        <Badge badgeContent={unreadCount} color="primary" max={999}>
          <Icon />
        </Badge>

        <Typography
          className="overflow-hidden text-ellipsis whitespace-nowrap font-medium"
          variant="body2"
        >
          {item.label}
        </Typography>
      </div>
    </Link>
  );
};

const translations = defineMessages({
  home: {
    id: 'course.courses.SidebarItem.home',
    defaultMessage: 'Home',
  },
});

const HomeSidebarItem = (props: { to: string }): JSX.Element => {
  const { t } = useTranslation();

  return (
    <SidebarItem
      exact
      of={{
        key: 'home',
        icon: 'home',
        label: t(translations.home),
        path: props.to,
      }}
    />
  );
};

export default Object.assign(SidebarItem, { Home: HomeSidebarItem });