diff --git a/frontend/components/NewLayout/Common/Button.tsx b/frontend/components/NewLayout/Common/Button.tsx new file mode 100644 index 000000000..2a99922a5 --- /dev/null +++ b/frontend/components/NewLayout/Common/Button.tsx @@ -0,0 +1,89 @@ +import { + EnhancedButton, + EnhancedButtonProps, + Button as MUIButton, +} from "@mui/material" +import { css, styled } from "@mui/material/styles" + +import ArrowRight from "../Icons/ArrowRight" + +const ButtonText = styled("span")` + margin: 12px 24px; +` + +const EnhancedMUIButton = styled(MUIButton)( + ({ theme, startIcon, href }) => ` + ${ + href + ? css` + &.MuiButton-containedPrimary { + ${ButtonText} { + margin: 12px 0 12px 16px; + padding-right: 16px; + border-right: solid 1px ${theme.palette.common.additional.skyblue}; + } + &.Mui-disabled { + ${ButtonText} { + border-right: solid 1px ${theme.palette.common.grayscale.dark}; + } + } + } + &.MuiButton-containedSecondary { + ${ButtonText} { + margin: 12px 16px; + } + &.MuiButton-endIcon { + padding-right: 1rem; + padding-left: 0; + } + } + `.styles + : "" + } + ${ + startIcon + ? css` + &.MuiButton-containedPrimary { + ${ButtonText} { + margin: 12px 16px 12px 0; + padding-left: 16px; + border-left: solid 1px ${theme.palette.common.additional.skyblue}; + } + &.Mui-disabled { + ${ButtonText} { + border-left: solid 1px ${theme.palette.common.grayscale.dark}; + } + } + } + &.MuiButton-containedSecondary { + ${ButtonText} { + margin: 12px 16px; + } + &.MuiButton-startIcon { + padding-left: 1rem; + padding-right: 0; + } + } + `.styles + : "" + } +`, +) as EnhancedButton + +const Button = ({ + children, + ...props +}: React.PropsWithChildren) => { + const { href } = props + + const endIcon: EnhancedButtonProps["endIcon"] = + props.endIcon ?? (href ? : undefined) + + return ( + + {children} + + ) +} + +export default Button diff --git a/frontend/components/NewLayout/Common/CTAButton.tsx b/frontend/components/NewLayout/Common/CTAButton.tsx new file mode 100644 index 000000000..23113ccca --- /dev/null +++ b/frontend/components/NewLayout/Common/CTAButton.tsx @@ -0,0 +1,324 @@ +import { + ButtonBase, + EnhancedButtonBase, + EnhancedButtonBaseProps, +} from "@mui/material" +import { styled } from "@mui/material/styles" + +import ArrowRight from "../Icons/ArrowRight" + +const ctaButtonVariants = [ + "transparent-background", + "black-background", + "white-background", + "blue-background", + "hero-white", + "hero-blue", + "hero-black", + "dark-blue-background", +] as const + +export type CTAButtonVariant = (typeof ctaButtonVariants)[number] + +const EnhancedMUIButton = styled(ButtonBase)( + ({ theme }) => ` + cursor: pointer; + font-weight: 700; + position: relative; + text-decoration: none; + + &.transparent-background, + &.black-background, + &.blue-background, + &.white-background, + &.hero-white, + &.hero-blue, + &.hero-black, + &.dark-blue-background { + align-items: center; + box-sizing: border-box; + display: flex; + height: 100%; + justify-content: center; + max-width: 328px; + padding: 13px 16px; + + .text { + font-size: 16px; + line-height: 18px; + letter-spacing: -0.3px; + } + + .link-icon { + display: flex; + margin-left: 8px; + + svg { + height: 16px; + width: 16px; + } + } + } + + &.dark-blue-background { + background-color: ${theme.palette.common.brand.dark}; + border: 4px solid ${theme.palette.common.grayscale.white}; + color: ${theme.palette.common.grayscale.white}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.dark}; + fill: ${theme.palette.common.grayscale.white}; + } + + .text { + color: ${theme.palette.common.grayscale.white}; + } + } + + &.transparent-background { + background-color: ${theme.palette.common.grayscale.white}; + border: 4px solid ${theme.palette.common.grayscale.black}; + color: ${theme.palette.common.grayscale.black}; + + .link-icon svg { + background-color: ${theme.palette.common.grayscale.white}; + fill: ${theme.palette.common.grayscale.black}; + } + + .text { + color: ${theme.palette.common.grayscale.black}; + } + } + + &.black-background { + background-color: ${theme.palette.common.grayscale.black}; + border: 4px solid ${theme.palette.common.grayscale.white}; + color: ${theme.palette.common.grayscale.white}; + + .link-icon svg { + background-color: ${theme.palette.common.grayscale.black}; + fill: ${theme.palette.common.grayscale.white}; + } + + .text { + color: ${theme.palette.common.grayscale.white}; + } + + &:hover { + background-color: ${theme.palette.common.brand.active}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.active}; + } + } + + &:focus { + background-color: ${theme.palette.common.brand.main}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.main}; + } + } + } + + &.blue-background { + background-color: ${theme.palette.common.brand.light}; + border: 4px solid ${theme.palette.common.grayscale.white}; + color: ${theme.palette.common.grayscale.white}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.light}; + fill: ${theme.palette.common.grayscale.white}; + } + + .text { + color: ${theme.palette.common.grayscale.white}; + } + + &:hover { + background-color: ${theme.palette.common.brand.main}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.main}; + } + } + + &:focus { + background-color: ${theme.palette.common.brand.active}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.active}; + } + } + } + + &.white-background { + background-color: ${theme.palette.common.grayscale.white}; + border: 4px solid ${theme.palette.common.brand.light}; + color: ${theme.palette.common.brand.light}; + + .link-icon svg { + background-color: ${theme.palette.common.grayscale.white}; + fill: ${theme.palette.common.brand.light}; + } + + .text { + color: ${theme.palette.common.brand.light}; + } + + &:hover { + border-color: ${theme.palette.common.brand.active}; + + .text { + color: ${theme.palette.common.brand.active}; + } + + .link-icon svg { + fill: ${theme.palette.common.brand.active}; + } + } + + &:focus { + border-color: ${theme.palette.common.brand.active}; + + .text { + color: ${theme.palette.common.brand.active}; + } + + .link-icon svg { + fill: ${theme.palette.common.brand.active}; + } + } + } + + &.hero-white { + background-color: ${theme.palette.common.brand.light}; + color: ${theme.palette.common.grayscale.white}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.light}; + fill: ${theme.palette.common.grayscale.white}; + } + + .text { + color: ${theme.palette.common.grayscale.white}; + } + + &:hover { + background-color: ${theme.palette.common.brand.active}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.active}; + } + } + + &:focus { + background-color: ${theme.palette.common.brand.active}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.active}; + } + } + } + + &.hero-black { + background-color: ${theme.palette.common.grayscale.black}; + border: 4px solid ${theme.palette.common.grayscale.white}; + color: ${theme.palette.common.grayscale.white}; + + .link-icon svg { + background-color: ${theme.palette.common.grayscale.black}; + fill: ${theme.palette.common.grayscale.white}; + } + + .text { + color: ${theme.palette.common.grayscale.white}; + } + + &:hover { + border-color: ${theme.palette.common.brand.soft}; + + .text { + color: ${theme.palette.common.brand.soft}; + } + + .link-icon svg { + fill: ${theme.palette.common.brand.soft}; + } + } + + &:focus { + border-color: ${theme.palette.common.brand.soft}; + + .text { + color: ${theme.palette.common.brand.soft}; + } + + .link-icon svg { + fill: ${theme.palette.common.brand.soft}; + } + } + } + + &.hero-blue { + background-color: ${theme.palette.common.brand.main}; + border: 4px solid ${theme.palette.common.grayscale.white}; + color: ${theme.palette.common.grayscale.white}; + + .link-icon svg { + background-color: ${theme.palette.common.brand.main}; + fill: ${theme.palette.common.grayscale.white}; + } + + .text { + color: ${theme.palette.common.grayscale.white}; + } + + &:hover { + border-color: ${theme.palette.common.brand.soft}; + + .text { + color: ${theme.palette.common.brand.soft}; + } + + .link-icon svg { + fill: ${theme.palette.common.brand.soft}; + } + } + + &:focus { + border-color: ${theme.palette.common.brand.soft}; + + .text { + color: ${theme.palette.common.brand.soft}; + } + + .link-icon svg { + fill: ${theme.palette.common.brand.soft}; + } + } + } + `, +) as EnhancedButtonBase + +export interface CTAButtonProps extends EnhancedButtonBaseProps { + variant?: CTAButtonVariant +} + +const CTAButton = ({ variant, children, ...props }: CTAButtonProps) => { + const buttonVariant = variant ?? "blue-background" + + return ( + + {children} + + + + + ) +} + +export default CTAButton diff --git a/frontend/components/NewLayout/Common/CTALink.tsx b/frontend/components/NewLayout/Common/CTALink.tsx new file mode 100644 index 000000000..38ac536b3 --- /dev/null +++ b/frontend/components/NewLayout/Common/CTALink.tsx @@ -0,0 +1,73 @@ +import { EnhancedLink, EnhancedLinkProps, Link as MUILink } from "@mui/material" +import { styled } from "@mui/material/styles" + +import ArrowLeft from "../Icons/ArrowLeft" +import ArrowRight from "../Icons/ArrowRight" + +const ctaLinkVariants = ["link-list", "default"] as const + +export type CTALinkVariant = (typeof ctaLinkVariants)[number] + +const Link = styled(MUILink)( + ({ theme }) => ` + font-size: 16px; + line-height: 24px; + font-weight: 600; + align-items: center; + color: ${theme.palette.common.brand.main}; + display: inline-grid; + gap: 16px; + grid-auto-flow: column; + letter-spacing: -0.5px; + overflow: hidden; + text-decoration: none; + + &:hover, &:focus { + text-decoration: underline; + + ${LinkIcon} { + background-color: ${theme.palette.common.brand.main}; + } + } +`, +) as EnhancedLink + +const LinkIcon = styled("span")( + ({ theme }) => ` + align-items: center; + background-color: ${theme.palette.common.brand.light}; + display: inline-flex; + height: 40px; + justify-content: center; + width: 40px; + + svg { + fill: ${theme.palette.common.grayscale.white}; + } +`, +) + +export interface CTALinkProps extends EnhancedLinkProps { + linkVariant?: CTALinkVariant +} + +const CTALink = (props: CTALinkProps) => { + const { children, target, linkVariant, ...rest } = props + + const isExternal = target === "_blank" + + return ( + + {children} + + {isExternal ? ( + + ) : ( + + )} + + + ) +} + +export default CTALink diff --git a/frontend/components/NewLayout/Courses/CourseCard.tsx b/frontend/components/NewLayout/Courses/CourseCard.tsx index 11aa38c45..5de8df418 100644 --- a/frontend/components/NewLayout/Courses/CourseCard.tsx +++ b/frontend/components/NewLayout/Courses/CourseCard.tsx @@ -8,6 +8,7 @@ import { Skeleton, Typography } from "@mui/material" import { css, styled } from "@mui/material/styles" import { CardTitle } from "../Common/Card" +import CTALink from "../Common/CTALink" import { courseColorSchemes } from "./common" import Sponsors from "./Sponsors" import { @@ -18,7 +19,6 @@ import { ModuleTags, ModuleTagsContainer, } from "./Tags" -import OutboundLink from "/components/OutboundLink" import { CardSubtitle } from "/components/Text/headers" import Tooltip from "/components/Tooltip" import { useTranslator } from "/hooks/useTranslator" @@ -254,11 +254,6 @@ const StyledHelpIcon = styled(HelpIcon)` } ` -const Link = styled(OutboundLink)` - justify-self: right; - margin-bottom: 0; -` as typeof OutboundLink - const LinkArea = styled("div")` display: flex; justify-content: flex-end; @@ -443,7 +438,11 @@ const CourseCard = React.forwardRef< organizer="Helsingin yliopisto" languageTags={} difficultyTags={} - link={{t("showCourse")}} + link={ + + {t("showCourse")} + + } sponsors={} /* diff --git a/frontend/components/NewLayout/Frontpage/Hero.tsx b/frontend/components/NewLayout/Frontpage/Hero.tsx index 13fa25665..73ed4762c 100644 --- a/frontend/components/NewLayout/Frontpage/Hero.tsx +++ b/frontend/components/NewLayout/Frontpage/Hero.tsx @@ -1,6 +1,7 @@ -import { Button, Typography } from "@mui/material" +import { Typography } from "@mui/material" import { styled } from "@mui/material/styles" +import CTAButton from "../Common/CTAButton" import { BackgroundImage } from "/components/Images/CardBackgroundFullCover" import { useTranslator } from "/hooks/useTranslator" import HomeTranslations from "/translations/home" @@ -33,15 +34,8 @@ const Paragraph = styled(Typography)` text-align: center; ` -const CourseButton = styled(Button)` - border-radius: 20px; - background-color: #fff; +const CourseButton = styled(CTAButton)` margin-top: 2rem; - - &:hover { - background-color: #378170; - color: #fff; - } ` function HeroContent() { @@ -51,7 +45,7 @@ function HeroContent() { {t("tagLine")} {t("intro")} - + {t("courseButton")} diff --git a/frontend/components/NewLayout/Frontpage/Modules/ModuleCard.tsx b/frontend/components/NewLayout/Frontpage/Modules/ModuleCard.tsx index f2d5455ee..2c2a676b5 100644 --- a/frontend/components/NewLayout/Frontpage/Modules/ModuleCard.tsx +++ b/frontend/components/NewLayout/Frontpage/Modules/ModuleCard.tsx @@ -1,6 +1,7 @@ import { Button, Skeleton } from "@mui/material" import { styled } from "@mui/material/styles" +import CTALink from "../../Common/CTALink" import { CardBody, CardDescription, @@ -57,9 +58,9 @@ export const ModuleCard = ({ {description} - + {t("moduleInformation")} - + diff --git a/frontend/components/NewLayout/Frontpage/Modules/ModuleNavigation.tsx b/frontend/components/NewLayout/Frontpage/Modules/ModuleNavigation.tsx index 452004189..4aa062634 100644 --- a/frontend/components/NewLayout/Frontpage/Modules/ModuleNavigation.tsx +++ b/frontend/components/NewLayout/Frontpage/Modules/ModuleNavigation.tsx @@ -1,9 +1,9 @@ import { useRouter } from "next/router" import { useQuery } from "@apollo/client" -import { Button } from "@mui/material" import { styled } from "@mui/material/styles" +import CTAButton from "../../Common/CTAButton" import ModuleNaviList from "./ModuleNaviList" import { SectionContainer, SectionTitle } from "/components/NewLayout/Common" import { useTranslator } from "/hooks/useTranslator" @@ -105,7 +105,7 @@ export function ModuleNavigation() { {t("studyModulesTitle")} - + {t("showAllModules")} ) } diff --git a/frontend/components/NewLayout/Frontpage/SelectedCourses.tsx b/frontend/components/NewLayout/Frontpage/SelectedCourses.tsx index 27ed35a3c..fd4af2c32 100644 --- a/frontend/components/NewLayout/Frontpage/SelectedCourses.tsx +++ b/frontend/components/NewLayout/Frontpage/SelectedCourses.tsx @@ -4,6 +4,7 @@ import { useQuery } from "@apollo/client" import { Button, Typography, TypographyProps } from "@mui/material" import { styled } from "@mui/material/styles" +import CTAButton from "../Common/CTAButton" import { SectionContainer, SectionTitle } from "/components/NewLayout/Common" import { CardBody, @@ -114,7 +115,7 @@ function SelectedCourses() { ))} - + {t("showAllCourses")} ) } diff --git a/frontend/components/NewLayout/Header/LanguageSwitch.tsx b/frontend/components/NewLayout/Header/LanguageSwitch.tsx index eba3e79b3..fcd755544 100644 --- a/frontend/components/NewLayout/Header/LanguageSwitch.tsx +++ b/frontend/components/NewLayout/Header/LanguageSwitch.tsx @@ -1,12 +1,13 @@ import React, { useState } from "react" -import Link from "next/link" import { useRouter } from "next/router" import { Button, EnhancedButton, + EnhancedLink, EnhancedMenuItem, + Link, Menu, MenuItem, } from "@mui/material" @@ -95,7 +96,7 @@ const LanguageOptionLink = styled(Link)` display: block; color: inherit; text-decoration: none; -` +` as EnhancedLink const LanguageSwitchContainer = styled("div")` align-items: center; diff --git a/frontend/components/NewLayout/Icons/ArrowLeft.tsx b/frontend/components/NewLayout/Icons/ArrowLeft.tsx new file mode 100644 index 000000000..42fbbcd83 --- /dev/null +++ b/frontend/components/NewLayout/Icons/ArrowLeft.tsx @@ -0,0 +1,18 @@ +import { createSvgIcon } from "@mui/material/utils" + +const ArrowLeftIcon = createSvgIcon( + + + , + "ArrowLeft", +) + +export default ArrowLeftIcon diff --git a/frontend/components/NewLayout/Modules/StudyModuleListItem.tsx b/frontend/components/NewLayout/Modules/StudyModuleListItem.tsx index 4a768aa1e..a4f1e707c 100644 --- a/frontend/components/NewLayout/Modules/StudyModuleListItem.tsx +++ b/frontend/components/NewLayout/Modules/StudyModuleListItem.tsx @@ -1,10 +1,11 @@ import { useCallback, useEffect, useMemo, useRef } from "react" -import { Button, Skeleton, Typography } from "@mui/material" +import { Skeleton, Typography } from "@mui/material" import { css, styled } from "@mui/material/styles" import { CorrectedAnchor } from "../Common" import { CardWrapper } from "../Common/Card" +import CTALink from "../Common/CTALink" import CourseCard, { CourseCardSkeleton } from "../Courses/CourseCard" import useIsomorphicLayoutEffect from "/hooks/useIsomorphicLayoutEffect" import { useTranslator } from "/hooks/useTranslator" @@ -133,14 +134,6 @@ const EndedCoursesLinkContainer = styled("div")` justify-content: flex-end; ` -const EndedCoursesButton = styled(Button)` - background-color: #fff; - - :hover { - background-color: #eee; - } -` - const getCoursesByStatus = ( courses: NewStudyModuleFieldsWithCoursesFragment["courses"], ) => { @@ -243,11 +236,11 @@ export function ListItem({ {ended.length > 0 && ( - {t("showEndedCourses")} - + )} diff --git a/frontend/components/NewLayout/Navigation/Breadcrumbs.tsx b/frontend/components/NewLayout/Navigation/Breadcrumbs.tsx new file mode 100644 index 000000000..ffc26e5c5 --- /dev/null +++ b/frontend/components/NewLayout/Navigation/Breadcrumbs.tsx @@ -0,0 +1,130 @@ +import { useRouter } from "next/router" + +import { Link, Skeleton } from "@mui/material" +import { styled } from "@mui/material/styles" + +import CaretRightIcon from "../Icons/CaretRight" +import { Breadcrumb, useBreadcrumbContext } from "/contexts/BreadcrumbContext" +import { useTranslator } from "/hooks/useTranslator" +import { isTranslationKey } from "/translations" +import BreadcrumbsTranslations, { + Breadcrumbs as BreadcrumbsTranslationType, +} from "/translations/breadcrumbs" + +const BreadcrumbContainer = styled("nav")` + display: inline-block; + position: relative; + width: auto; +` + +const BreadcrumbList = styled("ol")( + ({ theme }) => ` + margin: 0; + color: ${theme.palette.common.grayscale.dark}; + display: flex; + flex-wrap: wrap; + list-style-type: none; + padding: 20px 0 16px; + + ${theme.breakpoints.down("sm")} { + padding: 24px 0 16px; + } + + ${theme.breakpoints.up("xl")} { + padding: 32px 0 16px; + } +`, +) + +interface BreadcrumbItemComponentProps { + isCurrent?: boolean +} +const BreadcrumbItemComponent = styled("li")( + ({ theme, isCurrent }) => ` + font-size: 15px; + line-height: 20px; + font-weight: 600; + display: "flex"; + align-items: center; + margin-bottom: 0.75rem; + letter-spacing: -0.5px; + + ${theme.breakpoints.down("sm")} { + margin-bottom: 1rem; + } + + a { + color: ${ + isCurrent + ? theme.palette.common.grayscale.dark + : theme.palette.common.brand.light + }; + ${isCurrent ? "display: block;" : ""} + text-decoration: none; + + &:hover { + color: ${theme.palette.common.brand.active}; + } + } + + svg { + margin: 0 21px; + } +`, +) + +interface BreadcrumbItemProps + extends BreadcrumbItemComponentProps, + Breadcrumb {} + +const BreadcrumbItem = ({ isCurrent, ...item }: BreadcrumbItemProps) => { + const t = useTranslator(BreadcrumbsTranslations) + const { translation, label, href } = item + const _translation = isTranslationKey(translation) + ? t(translation) + : translation + + const text = label ?? _translation + + return ( + + {text ?? } + {!isCurrent && } + + ) +} + +const Breadcrumbs = () => { + const { asPath } = useRouter() + const { breadcrumbs } = useBreadcrumbContext() + + const isHomePage = !!RegExp(/^(?:\/_new)?\/?$/).exec( + asPath?.replace(/#(.*)/, ""), + ) + + if (isHomePage) { + return null + } + + console.log("breadcrumbs", breadcrumbs) + return ( + + + + {breadcrumbs.map((breadcrumb, index) => ( + + ))} + + + ) +} + +export default Breadcrumbs diff --git a/frontend/components/NewLayout/Navigation/DesktopNavigationMenu.tsx b/frontend/components/NewLayout/Navigation/DesktopNavigationMenu.tsx index af7ac2e1a..fcc960c2a 100644 --- a/frontend/components/NewLayout/Navigation/DesktopNavigationMenu.tsx +++ b/frontend/components/NewLayout/Navigation/DesktopNavigationMenu.tsx @@ -1,18 +1,7 @@ -import React, { useCallback, useMemo } from "react" - -import { useRouter } from "next/router" +import React, { useCallback } from "react" import { useApolloClient } from "@apollo/client" -import SignOut from "@fortawesome/fontawesome-free/svgs/solid/right-from-bracket.svg?icon" -import User from "@fortawesome/fontawesome-free/svgs/solid/user.svg?icon" -import { - Button, - EnhancedButton, - EnhancedButtonProps, - SvgIconProps, - useMediaQuery, -} from "@mui/material" -import { styled, Theme } from "@mui/material/styles" +import { styled } from "@mui/material/styles" import { NavigationMenuItem } from "." import { NavigationLinks } from "./NavigationLinks" @@ -28,49 +17,12 @@ const NavigationRightContainer = styled("div")` justify-content: flex-end; ` -interface MenuButtonProps { - Icon?: React.FunctionComponent - narrow?: boolean -} - -const MenuButtonBase = styled(Button)` - display: flex; - max-height: 8vh; - white-space: nowrap; - font-size: 1rem; - gap: 0.5rem; - max-width: 240px; - overflow: hidden; - word-wrap: break-word; - overflow-wrap: normal; -` as EnhancedButton - -const MenuButton = React.memo( - ({ - Icon, - narrow, - children, - ...props - }: React.PropsWithChildren) => { - return ( - - {Icon && } - {children} - - ) - }, -) - const UserOptionsMenu = () => { const apollo = useApolloClient() - const { pathname } = useRouter() - const { loggedIn, logInOrOut, currentUser } = useLoginStateContext() + const { loggedIn, logInOrOut } = useLoginStateContext() const t = useTranslator(CommonTranslations) - const isNarrow = useMediaQuery((theme: Theme) => - theme.breakpoints.down("desktop"), - ) - const userDisplayName = useMemo(() => { + /*const userDisplayName = useMemo(() => { if (isNarrow) { return null } @@ -88,7 +40,7 @@ const UserOptionsMenu = () => { return name }, [currentUser, t, isNarrow]) - +*/ const onLogOut = useCallback( () => signOut(apollo, logInOrOut), [apollo, logInOrOut], @@ -96,32 +48,35 @@ const UserOptionsMenu = () => { if (loggedIn) { return ( - <> - - {userDisplayName} - - - + ) } return ( - <> - {t("loginShort")} - - {t("signUp")} - - + ) } diff --git a/frontend/components/NewLayout/Navigation/MobileNavigationMenu.tsx b/frontend/components/NewLayout/Navigation/MobileNavigationMenu.tsx index a57c20299..5fe7cb56a 100644 --- a/frontend/components/NewLayout/Navigation/MobileNavigationMenu.tsx +++ b/frontend/components/NewLayout/Navigation/MobileNavigationMenu.tsx @@ -1,9 +1,11 @@ -import React, { createContext, useMemo, useState } from "react" +import React, { createContext, useCallback, useMemo, useState } from "react" import { useRouter } from "next/router" +import { useApolloClient } from "@apollo/client" import { Button, + ButtonBase, Drawer, EnhancedListItemButton, ExtendList, @@ -21,7 +23,9 @@ import CaretLeftIcon from "../Icons/CaretLeft" import CaretRightIcon from "../Icons/CaretRight" import HamburgerIcon from "../Icons/Hamburger" import RemoveIcon from "../Icons/Remove" +import { useLoginStateContext } from "/contexts/LoginStateContext" import { useTranslator } from "/hooks/useTranslator" +import { signOut } from "/lib/authentication" import CommonTranslations from "/translations/common" const MobileMenuContainer = styled("div")( @@ -47,7 +51,7 @@ const MobileMenu = styled(Drawer)( `, ) as typeof Drawer -const MobileMenuButton = styled(Button)( +const MobileMenuButton = styled(ButtonBase)( ({ theme }) => ` align-items: center; display: inline-flex; @@ -90,7 +94,7 @@ const MobileMenuButton = styled(Button)( } } `, -) as typeof Button +) as typeof ButtonBase const MobileMenuHeader = styled("section")` display: grid; @@ -361,10 +365,47 @@ interface MobileNavigationMenuProps { items: Array } -const MobileNavigationMenu = ({ items }: MobileNavigationMenuProps) => { +const MobileNavigationMenu = ({ + items: originalItems, +}: MobileNavigationMenuProps) => { + const apollo = useApolloClient() const [open, setOpen] = useState(false) - + const { loggedIn, logInOrOut } = useLoginStateContext() + const onLogOut = useCallback( + () => signOut(apollo, logInOrOut), + [apollo, logInOrOut], + ) const t = useTranslator(CommonTranslations) + // TODO: move somewhere else, just POC + const items = useMemo( + () => [ + ...originalItems, + ...(loggedIn + ? [ + { + href: "/_new/profile", + label: t("myProfile"), + }, + { + href: "#", + label: t("logout"), + onClick: onLogOut, + }, + ] + : [ + { + href: "/_new/sign-in", + label: t("loginShort"), + }, + { + href: "/_new/sign-up", + label: t("signUp"), + }, + ]), + ], + [loggedIn, t], + ) + const [currentLevel, setCurrentLevel] = useState(0) const [breadcrumbs, setBreadcrumbs] = useState< Array diff --git a/frontend/components/NewLayout/Navigation/NavigationDropdown.tsx b/frontend/components/NewLayout/Navigation/NavigationDropdown.tsx index 0ff2cc717..56338c67d 100644 --- a/frontend/components/NewLayout/Navigation/NavigationDropdown.tsx +++ b/frontend/components/NewLayout/Navigation/NavigationDropdown.tsx @@ -1,8 +1,8 @@ import { useState } from "react" import { - Button, - EnhancedButton, + ButtonBase, + EnhancedButtonBase, EnhancedLink, EnhancedLinkProps, Link, @@ -23,7 +23,7 @@ interface NavigationDropdownButtonProps { expanded?: boolean } -const NavigationDropdownButton = styled(Button, { +const NavigationDropdownButton = styled(ButtonBase, { shouldForwardProp: (prop) => prop !== "expanded", })( ({ theme, expanded }) => ` @@ -48,6 +48,7 @@ const NavigationDropdownButton = styled(Button, { svg { fill: ${theme.palette.common.brand.main}; } + background-color: transparent; } ${ expanded @@ -76,7 +77,7 @@ const NavigationDropdownButton = styled(Button, { : "" } `, -) as EnhancedButton<"button", NavigationDropdownButtonProps> +) as EnhancedButtonBase<"button", NavigationDropdownButtonProps> const NavigationDropdownMenu = styled(Popover)( ({ theme }) => ` @@ -239,7 +240,6 @@ export const NavigationDropdownLink = ({ aria-controls={open ? menuName : undefined} aria-haspopup="true" aria-expanded={open ? "true" : undefined} - variant="text" onClick={onClick} expanded={open} > diff --git a/frontend/components/NewLayout/Navigation/NavigationLinks.tsx b/frontend/components/NewLayout/Navigation/NavigationLinks.tsx index 1bd1f7882..06c08e28a 100644 --- a/frontend/components/NewLayout/Navigation/NavigationLinks.tsx +++ b/frontend/components/NewLayout/Navigation/NavigationLinks.tsx @@ -88,7 +88,7 @@ interface NavigationItemProps { } const NavigationItem = ({ item }: NavigationItemProps) => { - const { name, label, href } = item + const { name, label, href, onClick } = item return ( {isSubmenuItem(item) ? ( @@ -100,7 +100,9 @@ const NavigationItem = ({ item }: NavigationItemProps) => { ))} ) : ( - {label} + + {label} + )} ) diff --git a/frontend/components/NewLayout/Navigation/index.tsx b/frontend/components/NewLayout/Navigation/index.tsx index 5757f5e0d..d34dab957 100644 --- a/frontend/components/NewLayout/Navigation/index.tsx +++ b/frontend/components/NewLayout/Navigation/index.tsx @@ -44,7 +44,7 @@ export type NavigationMenuLinkItem = { name?: string description?: string level?: number - parent?: NavigationMenuItem + onClick?: (...params: any[]) => any } export type NavigationMenuSubmenuItem = NavigationMenuLinkItem & { diff --git a/frontend/pages/_new/_layout.tsx b/frontend/pages/_new/_layout.tsx index 58acea060..a72180ce1 100644 --- a/frontend/pages/_new/_layout.tsx +++ b/frontend/pages/_new/_layout.tsx @@ -4,10 +4,10 @@ import { useRouter } from "next/router" import { styled } from "@mui/material/styles" -import { Breadcrumbs } from "/components/Breadcrumbs" import Footer from "/components/Footer" import Alerts from "/components/HeaderBar/Alerts" import Header from "/components/NewLayout/Header" +import Breadcrumbs from "/components/NewLayout/Navigation/Breadcrumbs" import PageLoadingIndicators from "/components/PageLoadingIndicators" import SkipLink from "/components/SkipLink" @@ -18,14 +18,20 @@ const FooterDownPusherWrapper = styled("div")` justify-content: space-between; ` -const MainContent = styled("main")` +const MainContent = styled("main")( + ({ theme }) => ` position: relative; display: flex; flex-direction: column; width: 100%; max-width: 1920px; margin: 0 auto; -` + padding: 0 1rem; + ${theme.breakpoints.up("sm")} { + padding: 0 2rem; + } +`, +) const Layout: React.FunctionComponent = ({ children, diff --git a/frontend/pages/_new/study-modules/index.tsx b/frontend/pages/_new/study-modules/index.tsx index 087365b2c..6b9ef5fb9 100644 --- a/frontend/pages/_new/study-modules/index.tsx +++ b/frontend/pages/_new/study-modules/index.tsx @@ -3,6 +3,7 @@ import Background from "components/NewLayout/Background" import { styled } from "@mui/material/styles" import { StudyModuleHero, StudyModuleList } from "/components/NewLayout/Modules" +import { useBreadcrumbs } from "/hooks/useBreadcrumbs" const Container = styled("div")` display: flex; @@ -11,6 +12,13 @@ const Container = styled("div")` ` function StudyModules() { + useBreadcrumbs([ + { + translation: "studyModules", + href: "/study-modules", + }, + ]) + return ( diff --git a/frontend/pages/study-modules.tsx b/frontend/pages/study-modules.tsx index 144542719..f947d3363 100644 --- a/frontend/pages/study-modules.tsx +++ b/frontend/pages/study-modules.tsx @@ -17,7 +17,7 @@ function StudyModules() { useBreadcrumbs([ { translation: "studyModules", - href: "/study-modules", + href: "/_new/study-modules", }, ]) diff --git a/frontend/src/newTheme/components.tsx b/frontend/src/newTheme/components.tsx index cf7535439..b0a446811 100644 --- a/frontend/src/newTheme/components.tsx +++ b/frontend/src/newTheme/components.tsx @@ -40,7 +40,13 @@ export const withComponents = (theme: Theme) => MuiButtonBase: { defaultProps: { LinkComponent: LinkBehavior, + disableRipple: true, } as ButtonBaseProps, + styleOverrides: { + root: { + ...bodyFontDeclaration, + }, + }, }, MuiMenuItem: { defaultProps: { @@ -56,7 +62,7 @@ export const withComponents = (theme: Theme) => }, MuiButton: { defaultProps: { - variant: "outlined", + variant: "text", color: "primary", disableRipple: true, LinkComponent: LinkBehavior, @@ -64,13 +70,16 @@ export const withComponents = (theme: Theme) => styleOverrides: { root: { ...bodyFontDeclaration, + }, + /* borderRadius: 0, + textTransform: "none", display: "flex", alignItems: "center", border: "none", cursor: "pointer", fontSize: "0.9375rem", - justifyContent: "center", lineHeight: "0.9375rem", + justifyContent: "center", margin: 0, padding: 0, transitionDuration: "0.1s", @@ -78,8 +87,89 @@ export const withComponents = (theme: Theme) => minHeight: "44px", position: "relative", textDecoration: "none", - }, + ".MuiButton-startIcon": { + alignContent: "center", + display: "flex", + margin: "0", + svg: { + fontSize: "13px" + } + }, + ".MuiButton-endIcon": { + alignContent: "center", + display: "flex", + margin: "0", + paddingRight: "12px", + svg: { + fontSize: "13px" + } + }, + },*/ contained: { + ...bodyFontDeclaration, + cursor: "pointer", + fontWeight: "700", + position: "relative", + textDecoration: "none", + alignItems: "center", + boxSizing: "border-box", + display: "flex", + height: "100%", + justifyContent: "center", + maxWidth: "328px", + padding: "13px 16px", + fontSize: "16px", + lineHeight: "18px", + letterSpacing: "-0.3px", + svg: { + marginLeft: "8px", + height: "16px", + width: "16px", + }, + }, + containedSecondary: { + backgroundColor: theme.palette.common.grayscale.white, + border: `4px solid ${theme.palette.common.grayscale.black}`, + color: theme.palette.common.grayscale.black, + svg: { + backgroundColor: theme.palette.common.grayscale.white, + fill: theme.palette.common.grayscale.black, + }, + }, + text: { + borderRadius: 0, + textTransform: "none", + display: "flex", + alignItems: "center", + border: "none", + cursor: "pointer", + fontSize: "0.9375rem", + lineHeight: "0.9375rem", + justifyContent: "center", + margin: 0, + padding: 0, + transitionDuration: "0.1s", + transitionProperty: "all", + minHeight: "44px", + position: "relative", + textDecoration: "none", + ".MuiButton-startIcon": { + alignContent: "center", + display: "flex", + margin: "0", + svg: { + fontSize: "13px", + }, + }, + ".MuiButton-endIcon": { + alignContent: "center", + display: "flex", + margin: "0", + paddingRight: "12px", + svg: { + fontSize: "13px", + }, + }, backgroundColor: theme.palette.common.brand.main, color: theme.palette.common.grayscale.white, "&::after": { @@ -100,9 +190,17 @@ export const withComponents = (theme: Theme) => }, }, }, - containedPrimary: { + textPrimary: { backgroundColor: theme.palette.common.brand.main, color: theme.palette.common.grayscale.white, + ".MuiButton-startIcon": { + padding: "8px 13px", + fill: theme.palette.common.grayscale.white, + }, + ".MuiButton-endIcon": { + padding: "8px 12px 8px 13px", + fill: theme.palette.common.grayscale.white, + }, svg: { fill: theme.palette.common.grayscale.white, }, @@ -130,10 +228,17 @@ export const withComponents = (theme: Theme) => bottom: "-2px", }, }, - containedSecondary: { + textSecondary: { backgroundColor: "transparent", border: `solid 2px ${theme.palette.common.brand.main}`, color: theme.palette.common.brand.main, + boxShadow: "none", + ".MuiButton-startIcon": { + fill: theme.palette.common.brand.main, + }, + ".MuiButton-endIcon": { + fill: theme.palette.common.brand.main, + }, ".svg": { fill: theme.palette.common.brand.main, }, @@ -143,6 +248,9 @@ export const withComponents = (theme: Theme) => svg: { fill: theme.palette.common.brand.active, }, + textDecoration: "underline", + backgroundColor: "inherit", + boxShadow: "none", }, "&:active": { border: `solid 2px ${theme.palette.common.brand.dark}`, @@ -159,6 +267,9 @@ export const withComponents = (theme: Theme) => }, }, }, + containedSizeLarge: { + minHeight: "48px", + }, }, }, MuiAppBar: { diff --git a/frontend/types/mui.d.ts b/frontend/types/mui.d.ts index 3940ebad3..297222457 100644 --- a/frontend/types/mui.d.ts +++ b/frontend/types/mui.d.ts @@ -4,6 +4,7 @@ import { LinkProps as NextLinkProps } from "next/link" import { ListItemButtonTypeMap } from "@mui/material" import { ButtonTypeMap } from "@mui/material/Button" import { ButtonBaseTypeMap, ExtendButtonBase } from "@mui/material/ButtonBase" +import { IconButtonTypeMap } from "@mui/material/IconButton" import { LinkTypeMap } from "@mui/material/Link" import { MenuItemTypeMap } from "@mui/material/MenuItem" import { @@ -48,12 +49,7 @@ declare module "@mui/material/ButtonBase" { export type EnhancedButtonBase< D extends React.ElementType = ButtonBaseTypeMap["defaultComponent"], P = {}, - > = ExtendButtonBase< - ButtonBaseTypeMap< - P & Partial, - D & typeof LinkBehavior - > - > + > = ExtendButtonBase, D>> const ButtonBase: ExtendButtonBase< ButtonBaseTypeMap> > @@ -106,6 +102,20 @@ declare module "@mui/material/ListItemButton" { > } +declare module "@mui/material/IconButton" { + export type EnhancedIconButtonProps< + D extends React.ElementType = IconButtonTypeMap["defaultComponent"], + P = {}, + > = OverrideProps, D>, D> + export type EnhancedIconButton< + D extends React.ElementType = ButtonTypeMap["defaultComponent"], + P = {}, + > = ExtendButtonBase, D>> + const IconButton: ExtendButtonBase< + IconButonTypeMap> + > +} + declare module "@mui/material/Typography" { // add typography variants - also needs to be declared in "styles" interface TypographyPropsVariantOverrides {