.ts
TypeScript
(application/typescript)
// 3rd-party
import React, { useMemo, VFC } from "react";
import styled, { css } from "styled-components";
// app
import type { CommonProps, WithThemeSchemeProp } from "../types";
import { AppRoute } from "../routes.defs";
import { Const } from "../const";
import { NamedColors } from "../utils/style";
import { buildRouteLink } from "../utils/shared";
import { PageWrapper } from "./PageWrapper";

interface PageHeaderProps extends CommonProps {
  forceShowLogo?: boolean;
  setDrawerPrimaryOpen?: (predicate: (prev: boolean) => boolean) => void;
}

export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
  commonProps,
  themeScheme,
  forceShowLogo = true,
  setDrawerPrimaryOpen = undefined,
}) => {
  const invertThemeScheme = themeScheme === "light" ? "dark" : "light";

  const toggleDrawerPrimary = () => {
    if (setDrawerPrimaryOpen) {
      setDrawerPrimaryOpen((prev) => !prev);
    }
  };

  const pageHeaderActions = useMemo(() => {
    if (commonProps.authenticated) {
      return (
        <>
          <a
            aria-label={"View your profile and repositories"}
            title={`View @${commonProps.currentUserUsername || "ghost"} profile and settings`}
            href={buildRouteLink(
              AppRoute.USER_DETAILS,
              {
                username: commonProps.currentUserUsername || "ghost",
              },
              { encodeURIComponent: false },
            )}
          >
            <PageHeaderAvatar
              aria-label={commonProps.currentUserUsername || "ghost"}
              themeScheme={themeScheme}
            />
          </a>
        </>
      );
    }

    return (
      <>
        <a
          aria-label={"Register a new account"}
          href={buildRouteLink(AppRoute.AUTH_REGISTER, null)}
        >
          Register
        </a>
        <a
          aria-label={"Login to your account"}
          href={buildRouteLink(AppRoute.AUTH_LOGIN, null)}
        >
          Login
        </a>
      </>
    );
  }, [commonProps.authenticated]);

  return (
    <StyledPageHeader themeScheme={themeScheme}>
      <PageWrapper>
        <StyledBurgerMenu
          themeScheme={themeScheme}
          onClick={toggleDrawerPrimary}
        />
        <StyledLogoArea themeScheme={themeScheme} forceShowLogo={forceShowLogo}>
          <a href={"/"}>
            <h1>{Const.APP_NAME}</h1>
          </a>
        </StyledLogoArea>
        <StyledPageHeaderNav themeScheme={themeScheme}>
          <a
            aria-label={"Explore Repositories"}
            href={buildRouteLink(AppRoute.REPOSITORY_EXPLORE, null)}
            className={
              commonProps.path ===
              buildRouteLink(AppRoute.REPOSITORY_EXPLORE, null)
                ? "active"
                : undefined
            }
          >
            Explore
          </a>
          <a
            aria-label={"Contribute to GitFOSS development"}
            href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
              orgSlug: "ethicdevs",
              repoSlug: "gitfoss",
            })}
            className={
              commonProps.path!.startsWith(
                buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
                  orgSlug: "ethicdevs",
                  repoSlug: "gitfoss",
                }),
              )
                ? "active"
                : undefined
            }
          >
            Contribute
          </a>
        </StyledPageHeaderNav>
        <div style={{ flex: 1 }} />
        <StyledActionsArea>
          {commonProps.authenticated && (
            <a
              aria-label={"Create a new Repository"}
              href={buildRouteLink(AppRoute.REPOSITORY_CREATE, null)}
            >
              (+) Repo
            </a>
          )}
          <a
            aria-label={`Switch to ${invertThemeScheme} theme`}
            data-smooth-scroll={"disabled"}
            href={buildRouteLink(AppRoute.THEME_SET_SCHEME_ACTION, {
              themeScheme: invertThemeScheme,
            })}
            title={`Click to enable ${invertThemeScheme} mode`}
          >
            {`${themeScheme === "light" ? "Dark" : "Light"}`}
          </a>
          {pageHeaderActions}
        </StyledActionsArea>
      </PageWrapper>
    </StyledPageHeader>
  );
};

const StyledBurgerMenu = styled.button<WithThemeSchemeProp>`
  ${({ themeScheme }) => css`
    /* above mobile size */
    @media only screen and (min-width: 768px) {
      & {
        display: none;
      }
    }

    width: 40px;
    height: 40px;
    border-image: none;
    border: none;
    border-radius: 20px;
    background: ${NamedColors.CARD_OVERLAY[themeScheme]};
    /*color: red;*/
  `}
`;

const StyledPageHeader = styled.header<WithThemeSchemeProp>`
  display: flex;
  flex-flow: row nowrap;
  justify-content: flex-start;
  align-items: center;

  height: 100%;
  width: 100%;

  /* above mobile size */
  @media only screen and (min-width: 768px) {
    & > ${PageWrapper} {
      padding: 0 16px;
    }
  }

  & > ${PageWrapper} {
    height: 100%;

    flex-flow: row nowrap;
    justify-content: flex-start;
    align-items: center;

    padding: 0;
    gap: 16px;
  }

  & a {
    transition: color 140ms ease-in-out 0s;
    text-decoration: none;

    ${({ themeScheme }) => css`
      color: ${NamedColors.TEXT_MUTED[themeScheme]};
    `};

    &:hover {
      ${({ themeScheme }) => css`
        color: ${NamedColors.TEXT_DEFAULT[themeScheme]};
      `};
      text-decoration: underline;
    }
  }
`;

const StyledLogoArea = styled.div<
  WithThemeSchemeProp & { forceShowLogo: boolean }
>`
  ${({ forceShowLogo }) =>
    forceShowLogo !== true &&
    css`
      @media only screen and (min-width: 768px) {
        display: none;
      }
    `};

  @media only screen and (max-width: 768px) {
    & > a > h1 {
      font-size: 22px;
    }
  }

  & > a {
    display: flex;
    flex-flow: row nowrap;
    justify-content: flex-start;
    align-items: center;

    ${({ themeScheme }) => css`
      color: ${NamedColors.TEXT_DEFAULT[themeScheme]};
    `};

    h1 {
      margin: 0;
    }
  }
`;

const StyledPageHeaderNav = styled.nav<WithThemeSchemeProp>`
  display: flex;
  flex-flow: row nowrap;
  justify-content: flex-start;
  align-items: center;

  /* flex: 1; */
  height: 40px;
  /* width: 100%; */

  gap: 2px;
  margin: 0;

  ${({ themeScheme }) => css`
    border-radius: 20px;
    background-color: ${NamedColors.CARD_OVERLAY[themeScheme]};
  `};

  @media only screen and (max-width: 768px) {
    display: none;
  }

  & > a {
    display: flex;
    justify-content: center;
    align-items: center;

    white-space: nowrap;

    height: 40px;
    min-width: 120px;
    padding: 0 16px;

    border-radius: 20px;
    font-weight: normal;
    text-decoration: none;

    ${({ themeScheme }) => css`
      color: ${NamedColors.TEXT_MUTED[themeScheme]};
      /* background-color: ${NamedColors.CARD[themeScheme]}; */
    `};

    &.active,
    &:hover {
      ${({ themeScheme }) => css`
        color: ${NamedColors.TEXT_DEFAULT[themeScheme]};
        background-color: ${NamedColors.CARD[themeScheme]};
        font-weight: bold;
        font-family: monospace;
        text-decoration: none;
      `};
    }
  }
`;

const StyledActionsArea = styled.div`
  display: flex;
  flex-flow: row nowrap;
  justify-content: flex-end;
  align-items: center;

  & > a {
    margin-left: 12px;
    white-space: nowrap;
  }
`;

const PageHeaderAvatar = styled.img<WithThemeSchemeProp>`
  width: 40px;
  height: 40px;

  border-image: none;
  border-radius: 40px;

  ${({ themeScheme }) => css`
    border: 1px solid ${NamedColors.BORDER_CARD[themeScheme]};
    background-color: ${NamedColors.CARD_OVERLAY[themeScheme]};
  `};
`;